Thread overview
Initialization vs Assignment
Nov 03, 2013
Luís Marques
Nov 03, 2013
jerro
Nov 03, 2013
Luís Marques
Nov 03, 2013
Peter Alexander
Nov 03, 2013
Kenji Hara
Nov 03, 2013
Luís Marques
Nov 03, 2013
Kozzi
November 03, 2013
Hi,

Most of the time my D code has been high-level, so I had never considered the following issue. Imagine you have a struct A as a member of a class/struct X (here a struct, to ensure the dtor is called):

    struct A
    {
        int v;

        this(int v)
        {
            this.v = v*2;
        }

        ~this()
        {
            writefln("~A(%d)", v);
        }
    }

    struct X
    {
        A a;

        this(int v)
        {
            a = A(v);
            writeln("-");
        }
    }

    void main()
    {
        X x = X(42);
    }

Output:

    ~A(0)
    -
    ~A(84)

That is, because we don't have C++'s colon initialization syntax, we are paying the cost of initializing (and then destroying) X.a before we assign to it with "a = A(v)" in X's ctor. This seems to be the case even with @disable A.this(), which here does not seem to do anything (does not prevent the default/implicit initialization of X.a, before it is assigned A(v) ).

If C++ distinguishes between initialization and assignment to avoid this issue, is there a reason why D can avoid making the distinction? That is a performance issue. How about correctness? For instance:

    struct A
    {
        void* mem;

        @disable this();

        this(int v)
        {
            mem = malloc(v);
        }

        ~this()
        {
            free(mem);
        }
    }

Now we can't have an A as a member of X? (it would free a null pointer)

How have you solved these cases? Do you change it to a PIMPL? What if that's not desirable? What if you don't want to break encapsulation / cleanliness too much? Etc. Is there a good general solution for this issue?
November 03, 2013
> Now we can't have an A as a member of X? (it would free a null pointer)

Actually, there is nothing wrong with calling free on a null pointer.
From the C 89 standard:

> The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs.
November 03, 2013
It's fixed in 2.064 :-)

http://dpaste.dzfl.pl/c292229f

Application output:
-
~A(84)
November 03, 2013
2013/11/4 <luis@luismarques.eu>"@puremagic.com <"\"Luís".Marques">

> Hi,
>
> Most of the time my D code has been high-level, so I had never considered the following issue. Imagine you have a struct A as a member of a class/struct X (here a struct, to ensure the dtor is called):
>
>     struct A
>     {
>         int v;
>
>         this(int v)
>         {
>             this.v = v*2;
>         }
>
>         ~this()
>         {
>             writefln("~A(%d)", v);
>         }
>     }
>
>     struct X
>     {
>         A a;
>
>         this(int v)
>         {
>             a = A(v);
>             writeln("-");
>         }
>     }
>
>     void main()
>     {
>         X x = X(42);
>     }
>
> Output:
>
>     ~A(0)
>     -
>     ~A(84)
>
> That is, because we don't have C++'s colon initialization syntax, we are
> paying the cost of initializing (and then destroying) X.a before we assign
> to it with "a = A(v)" in X's ctor. This seems to be the case even with
> @disable A.this(), which here does not seem to do anything (does not
> prevent the default/implicit initialization of X.a, before it is assigned
> A(v) ).
>
> If C++ distinguishes between initialization and assignment to avoid this issue, is there a reason why D can avoid making the distinction? That is a performance issue. How about correctness? For instance:
>
>     struct A
>     {
>         void* mem;
>
>         @disable this();
>
>         this(int v)
>         {
>             mem = malloc(v);
>         }
>
>         ~this()
>         {
>             free(mem);
>         }
>     }
>
> Now we can't have an A as a member of X? (it would free a null pointer)
>
> How have you solved these cases? Do you change it to a PIMPL? What if that's not desirable? What if you don't want to break encapsulation / cleanliness too much? Etc. Is there a good general solution for this issue?
>

The issue is timely fixed in 2.064. http://d.puremagic.com/issues/show_bug.cgi?id=9665 https://github.com/D-Programming-Language/dlang.org/pull/404

Therefore with 2.064, the first test case will output following:

    -
    ~A(84)


Kenji Hara


November 03, 2013
On Sunday, 3 November 2013 at 16:01:44 UTC, jerro wrote:
>> Now we can't have an A as a member of X? (it would free a null pointer)
>
> Actually, there is nothing wrong with calling free on a null pointer.
> From the C 89 standard:
>
>> The free function causes the space pointed to by ptr to be deallocated, that is, made available for further allocation. If ptr is a null pointer, no action occurs.

Oops, right, not the best example. But you get the motivating idea :-)
November 03, 2013
On Sunday, 3 November 2013 at 16:08:26 UTC, Kenji Hara wrote:
> The issue is timely fixed in 2.064.
> http://d.puremagic.com/issues/show_bug.cgi?id=9665
> https://github.com/D-Programming-Language/dlang.org/pull/404
>
> Therefore with 2.064, the first test case will output following:
>
>     -
>     ~A(84)
>
>
> Kenji Hara

This is really weird. Not the first time that it happens to me. I swear that I'm not reading the pull requests and coming up with made up questions, hah :-) I guess it's a good sign of bug fixing speed!
November 03, 2013
On Sunday, 3 November 2013 at 15:38:30 UTC, Luís Marques wrote:
> Hi,
>
> Most of the time my D code has been high-level, so I had never considered the following issue. Imagine you have a struct A as a member of a class/struct X (here a struct, to ensure the dtor is called):
>
>     struct A
>     {
>         int v;
>
>         this(int v)
>         {
>             this.v = v*2;
>         }
>
>         ~this()
>         {
>             writefln("~A(%d)", v);
>         }
>     }
>
>     struct X
>     {
>         A a;
>
>         this(int v)
>         {
>             a = A(v);
>             writeln("-");
>         }
>     }
>
>     void main()
>     {
>         X x = X(42);
>     }
>
> Output:
>
>     ~A(0)
>     -
>     ~A(84)
>
> That is, because we don't have C++'s colon initialization syntax, we are paying the cost of initializing (and then destroying) X.a before we assign to it with "a = A(v)" in X's ctor. This seems to be the case even with @disable A.this(), which here does not seem to do anything (does not prevent the default/implicit initialization of X.a, before it is assigned A(v) ).
>
> If C++ distinguishes between initialization and assignment to avoid this issue, is there a reason why D can avoid making the distinction? That is a performance issue. How about correctness? For instance:
>
>     struct A
>     {
>         void* mem;
>
>         @disable this();
>
>         this(int v)
>         {
>             mem = malloc(v);
>         }
>
>         ~this()
>         {
>             free(mem);
>         }
>     }
>
> Now we can't have an A as a member of X? (it would free a null pointer)
>
> How have you solved these cases? Do you change it to a PIMPL? What if that's not desirable? What if you don't want to break encapsulation / cleanliness too much? Etc. Is there a good general solution for this issue?

for actual version of dmd, you can use this trick :)

struct A
{
	int v;
	
	void opAssign(int v) {
		this.v = v;
	}
	
	static int opCall(int v) {
		return v * 2;
	}
	
	~this()
	{
		writefln("~A(%d)", v);
	}
}