www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - confusion about structs

reply =?ISO-8859-1?Q?Christian_K=F6stlin?= <christian.koestlin gmail.com> writes:
I have this small program, which does some data handling with structs:

struct App {
   private int[] fData;
   this(size_t initialSize) {
     fData = new int[initialSize];
   }
   void put(int i) {
     fData[0] = i;
   }
   int[] get() {
     return fData;
   }
}

struct Builder {
   int i;
   App app = App(2);
   this(int i_) {
     i = i_;
//    app = App(2);
   }
   void put(int v) {
     app.put(v);
   }
   int[] get() {
     return app.get();
   }
}

int[] get(int i) {
   auto b = Builder(i);
   b.put(i);
   return b.get();
}


unittest {

   auto h1 = get(1);
   auto h2 = get(2);
   assert(h1[0] == 1);
   assert(h2[0] == 2);
}

int main(string[] args) {
   return 0;
}

compiling this with dmd -unittest shows, that the tests are failing.
if you comment in the line //    app = App(2); then the tests work as 
expected.
Could you please explain what struct Builder { App app = App(2); } does 
exactly compared to doing thins in the structs constructor?

thanks in advance

christian
Sep 25 2011
next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
This should make things clearer for you:

unittest
{
    auto h1 = get(1);
    auto h2 = get(2);

    assert(h1 is h2);  // both reference the same array
}

Field initialization is only done once (once per thread, or if a field
is shared once on app start) and not each time you call the
constructor.
Sep 25 2011
parent =?ISO-8859-1?Q?Christian_K=F6stlin?= <christian.koestlin gmail.com> writes:
On 09/26/2011 12:04 AM, Andrej Mitrovic wrote:
 This should make things clearer for you:

 fittest
 {
      auto h1 = get(1);
      auto h2 = get(2);

      assert(h1 is h2);  // both reference the same array
 }

 Field initialization is only done once (once per thread, or if a field
 is shared once on app start) and not each time you call the
 constructor.
Thanks for the explanation! Coming from a java background I confused the possibility to initialize at compile-time with the java-style initialization.
Sep 26 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, September 25, 2011 23:40:36 Christian K=C3=B6stlin wrote:
 I have this small program, which does some data handling with structs=
:
=20
 struct App {
    private int[] fData;
    this(size_t initialSize) {
      fData =3D new int[initialSize];
    }
    void put(int i) {
      fData[0] =3D i;
    }
    int[] get() {
      return fData;
    }
 }
=20
 struct Builder {
    int i;
    App app =3D App(2);
    this(int i_) {
      i =3D i_;
 //    app =3D App(2);
    }
    void put(int v) {
      app.put(v);
    }
    int[] get() {
      return app.get();
    }
 }
=20
 int[] get(int i) {
    auto b =3D Builder(i);
    b.put(i);
    return b.get();
 }
=20
=20
 unittest {
=20
    auto h1 =3D get(1);
    auto h2 =3D get(2);
    assert(h1[0] =3D=3D 1);
    assert(h2[0] =3D=3D 2);
 }
=20
 int main(string[] args) {
    return 0;
 }
=20
 compiling this with dmd -unittest shows, that the tests are failing.
 if you comment in the line //    app =3D App(2); then the tests work =
as
 expected.
 Could you please explain what struct Builder { App app =3D App(2); } =
does
 exactly compared to doing thins in the structs constructor?
It's a bug, but I don't know if it's that what you're trying to do does= n't=20 work or that it isn't illegal. When you directly initialize a member va= riable,=20 that initialization happens at compile time. The value of app in Builde= r.init=20 is what app is directly initialized to. So, if you set the value of app= in the=20 constructor to the same value that it was set to at compile time, it sh= ould=20 result in the same value for app. The problem is that App has a dynamic array in it, and that's allocated= on the=20 heap. Arrays can be used in CTFE, but I don't know if the same array al= located=20 in CTFE can then be used at runtime. If it can, then there's a bug here= ,=20 because app =3D App(2) in the constructor shouldn't be necessary. If it= can't,=20 then directly initializing an App at compile time shouldn't compile in = the=20 first place. - Jonathan M Davis
Sep 25 2011
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, September 26, 2011 00:04:48 Andrej Mitrovic wrote:
 This should make things clearer for you:
 
 unittest
 {
     auto h1 = get(1);
     auto h2 = get(2);
 
     assert(h1 is h2);  // both reference the same array
 }
 
 Field initialization is only done once (once per thread, or if a field
 is shared once on app start) and not each time you call the
 constructor.
Ah. Yes. Okay. There isn't a bug here then (at least not in the compiler). The problem is that if you don't assign app in the constructor, it uses the exact same value as is in App.init - there is no copying going on - so every instance (or at least every instance on each thread) then points to exactly the same array. That's definitely something that could be easy to miss if you're not careful and is probably a sign that mutable arrays shouldn't generally be in the init property of structs (unless they're empty). - Jonathan M Davis
Sep 25 2011