www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Initialization vs Assignment

reply =?UTF-8?B?Ikx1w61z?= Marques" <luis luismarques.eu> writes:
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?
Nov 03 2013
next sibling parent reply "jerro" <a a.com> writes:
 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.
Nov 03 2013
parent =?UTF-8?B?Ikx1w61z?= Marques" <luis luismarques.eu> writes:
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 :-)
Nov 03 2013
prev sibling next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
It's fixed in 2.064 :-)

http://dpaste.dzfl.pl/c292229f

Application output:
-
~A(84)
Nov 03 2013
prev sibling next sibling parent reply Kenji Hara <k.hara.pg gmail.com> writes:
2013/11/4 <luis luismarques.eu>" puremagic.com <"\"Lu=C3=ADs".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 =3D v*2;
         }

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

     struct X
     {
         A a;

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

     void main()
     {
         X x =3D 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 assig=
n
 to it with "a =3D 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 =3D 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 issu=
e?

The issue is timely fixed in 2.064.
http://d.puremagic.com/issues/show_bug.cgi?id=3D9665
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
Nov 03 2013
parent =?UTF-8?B?Ikx1w61z?= Marques" <luis luismarques.eu> writes:
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!
Nov 03 2013
prev sibling parent "Kozzi" <kozzi11 gmail.com> writes:
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); } }
Nov 03 2013