www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - struct construction (how?)

reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
Coming from C++ and reading the D2 spec, I realized that I don't 
understand struct construction yet. :) I am playing with dmd 2.037.

Considering

struct S
{
     int x;
     int y;
}

which may have a

     this(int x, int y)

defined:

1)

S s = { 1 };

Not good; because we may forget to provide initializers and the 
remaining members are not even default initialized. (I wrote about this 
recently; I hope it's just a bug and will get fixed.)

2)

S s;
s.x = 1;
s.y = 2;

Not good; because that is default initialization plus assignment. Also, 
we shouldn't be doing that everywhere in user code; it should be the job 
of the constructor.

3)

auto s = S(1, 2);

Not good, because the right hand side is a struct literal and there is a 
copy involved.

Also, that syntax fails if S also has an opCall defined.

4)

S s(1,2);

Does not work.

5) S(1,2) s;

My favorite, but does not work.

How do you construct a struct object? Am I giving structs too much 
attention? Should I just know that they exist but use classes instead?

Thank you,
Ali
Dec 27 2009
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Ali Çehreli:

 S s = { 1 };
 
 Not good; because we may forget to provide initializers and the 
 remaining members are not even default initialized.
All fields get initialized, unless explicitly told to not be initialized (and sometimes they get initialized even if you explicitly tell them to be not initialized). Another simple way to initialize the fields: struct S { int x = 1; int y = 2; }
(I wrote about this recently; I hope it's just a bug and will get fixed.)<
A recent change done by Walter: bugzilla 3476 C-style initializer for structs must be disallowed for structs with a constructor http://dsource.org/projects/dmd/changeset/291
 S s;
 s.x = 1;
 s.y = 2;
 
 Not good; because that is default initialization plus assignment.
You can change that to: S s = void; s.x = 1; s.y = 2; But in practice even without the =void LDC is often able to spot the redundancy and remove it.
 auto s = S(1, 2);
 
 Not good, because the right hand side is a struct literal and there is a 
 copy involved.
The compiler isn't dumb, here I think it performs only one initialization. You can take a look at the resulting asm when/if you aren't sure. Months ago I have filed a bug to llvm asking for better capabilities in detecting and removing similar double initializations of dynamic arrays.
 Am I giving structs too much 
 attention? Should I just know that they exist but use classes instead?
structs have their purpose, they can be useful to lay memory as you need, or for performance purposes, to avoid heap allocations (this is especially true for small structs, like a Vector3 or Complex, that coupled with the slow D GC and low-tech D compilers produce very slow code if you manage them as objects instead of structs). Structs have some downsides too, so you have to be careful. In some situations I start coding with classes and later when the code works correctly and I have a unittest suite that can be used as a safety net, I convert some of them to structs for performance purposes. Bye, bearophile
Dec 28 2009
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Ali Çehreli:
 auto s = S(1, 2);
And by the way, that's the idiomatic way to initialize a struct in D. If you want to statically initialize it, there are other ways I've shown you. Bye, bearophile
Dec 28 2009
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
bearophile wrote:
 Ali Çehreli:
 auto s = S(1, 2);
Doesn't work for structs that have opCall (or maybe an opCall with matching parameters to that use).
 And by the way, that's the idiomatic way to initialize a struct in D.
Excellent! That's the way I have chosen and have been using in my D tutorial. :) I had included a warning against the C-style initializers; good to see that they are gone at least for structs with constructors. Since there is also '=void', I think the {} should still default initialize the remaining members (like C and C++). One issue remains, which prompted me to open this thread in the first place: I wanted to experiment with defining opCall for that struct: struct S { int x; int y; const int opCall(int p0, int p1) { return p0 + p1; } } This does not compile anymore: auto s = S(1, 2); s(3, 4); // hoping to call opCall But compiler error instead: Error: function expected before (), not s of type int See, the type of 's' is 'int', meaning that S(1,2) is not a constructor but a call to opCall. (This behavior documented on the struct spec page.) Here is a consistent deduction of that behavior: - S(1,2) is always the opCall - if the programmer doesn't define an opCall, the automatic one is called and the automatic one initializes the members That is of course my deduction of the current behavior. I don't know what part of that is by design. (?) Also, I have no clue why we would ever want to use the type name as function call syntax as in S(1,2). Coming from C++, I can understand s(3,4)... :) Thank you, Ali
Dec 28 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Ali Çehreli:

  >> auto s = S(1, 2);
 
 Doesn't work for structs that have opCall (or maybe an opCall with 
 matching parameters to that use).
Try: static S opCall(int x, int y) { this.x = x; this.y = y; }
 I think the {} should still default initialize the 
 remaining members (like C and C++).
That's what it does.
 One issue remains, which prompted me to open this thread in the first place:
Other people that understand you better than me will have to answer you. Bye, bearophile
Dec 28 2009
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
bearophile wrote:
 Ali Çehreli:

  >> auto s = S(1, 2);

 Doesn't work for structs that have opCall (or maybe an opCall with
 matching parameters to that use).
Try: static S opCall(int x, int y) { this.x = x; this.y = y; }
As 'this' is not available in static member functions; you mean static S opCall(int x, int y) { S s; s.x = x; s.y = y; return s; } But that is basically the constructor. I don't understand why opCall gets in the way in auto s = S(1, 2); The right hand side should call the constructor. Only after an object is constructed, should opCall be used: s(3, 4); That's what I expected, but there must be a rationale... :)
 I think the {} should still default initialize the
 remaining members (like C and C++).
That's what it does.
Not with dmd 2.037. :( I've responded to your message on the other thread titled "struct members not default initialized?". It turned out to be a bug that was reported about a year ago: http://d.puremagic.com/issues/show_bug.cgi?id=2485 Ali
Dec 28 2009
parent bearophile <bearophileHUGS lycos.com> writes:
Ali Çehreli:
 As 'this' is not available in static member functions; you mean
Yes, of course, I am sorry. You know about D as much as me or more, so it's time for me to shut up :-) Bye, bearophile
Dec 28 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 28 Dec 2009 10:40:58 -0500, Ali Çehreli <acehreli yahoo.com> wrote:

 One issue remains, which prompted me to open this thread in the first  
 place:

 I wanted to experiment with defining opCall for that struct:

 struct S
 {
      int x;
      int y;

      const int opCall(int p0, int p1)
      {
          return p0 + p1;
      }
 }

 This does not compile anymore:

      auto s = S(1, 2);
      s(3, 4);          // hoping to call opCall

 But compiler error instead:

 Error: function expected before (), not s of type int

 See, the type of 's' is 'int', meaning that S(1,2) is not a constructor  
 but a call to opCall. (This behavior documented on the struct spec page.)

 Here is a consistent deduction of that behavior:

 - S(1,2) is always the opCall
 - if the programmer doesn't define an opCall, the automatic one is  
 called and the automatic one initializes the members
Removing s(3, 4), you get the following error message: testbug.d(14): Error: need 'this' to access member opCall So it appears that the compiler isn't outputting this error message when it should. -Steve
Dec 28 2009
prev sibling parent Don <nospam nospam.com> writes:
Ali Çehreli wrote:
 bearophile wrote:
  > Ali Çehreli:
  >> auto s = S(1, 2);
 
 Doesn't work for structs that have opCall (or maybe an opCall with 
 matching parameters to that use).
 
  > And by the way, that's the idiomatic way to initialize a struct in D.
 
 Excellent! That's the way I have chosen and have been using in my D 
 tutorial. :)
 
 I had included a warning against the C-style initializers; good to see 
 that they are gone at least for structs with constructors. Since there 
 is also '=void', I think the {} should still default initialize the 
 remaining members (like C and C++).
There's a good chance that C-style struct initializers will be completely removed from the language. It might help to know that struct literals were added at a late stage in language development. Before they were added, there were a few hacky workarounds in the language and compiler; you may encounter them occasionally. Also static opCall was a workaround for not having struct constructors.
 One issue remains, which prompted me to open this thread in the first 
 place:
 
 I wanted to experiment with defining opCall for that struct:
 
 struct S
 {
     int x;
     int y;
 
     const int opCall(int p0, int p1)
     {
         return p0 + p1;
     }
 }
 
 This does not compile anymore:
 
     auto s = S(1, 2);
     s(3, 4);          // hoping to call opCall
 
 But compiler error instead:
 
 Error: function expected before (), not s of type int
 
 See, the type of 's' is 'int', meaning that S(1,2) is not a constructor 
 but a call to opCall. (This behavior documented on the struct spec page.)
 
 Here is a consistent deduction of that behavior:
 
 - S(1,2) is always the opCall
 - if the programmer doesn't define an opCall, the automatic one is 
 called and the automatic one initializes the members
 
 That is of course my deduction of the current behavior. I don't know 
 what part of that is by design. (?)
 
 Also, I have no clue why we would ever want to use the type name as 
 function call syntax as in S(1,2). Coming from C++, I can understand 
 s(3,4)... :)
 
 Thank you,
 Ali
Dec 28 2009