www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Struct constructor, opCall mess.

reply "Remo" <remo4d gmail.com> writes:
Hi,

right now I am truing to figure out how the constructors behave 
in D2.

Question 1: why it is not possible to create custom ctor for 
struct?
I know this is not really necessary because you can initialize 
fields like this.
struct S{ int i = 1; }

But this is a big problem if one tries to port C++ code to D2 and 
default ctor call some important initialization code.

Question 2: is there a way to mimic compiler generated ctor (that 
work in CTFE)?

Question 3: why it is possible to call static opCall like this?
VectorC v5 = v4(1.0);

Right now opCall seems to be better as this(...) because it work 
in CTFE but because of unwanted calls this is not well at all.

Question 4: why this(...) does not work in CTFE mode?

Here is highly simplified test code.
http://melpon.org/wandbox/permlink/ofvNby99mKqOBQBf

I am asking this because I am learning D2 now by porting and 
warping/connecting some big C++ (C++11) project to D2.
Feb 24 2014
parent reply "Tobias Pankrath" <tobias pankrath.net> writes:
On Monday, 24 February 2014 at 13:56:01 UTC, Remo wrote:
 Hi,

 right now I am truing to figure out how the constructors behave 
 in D2.

 Question 1: why it is not possible to create custom ctor for 
 struct?
The design of D relies on the fact that every type has a T.init property that is known to the compiler and used when in C++ the default ctor would get called. In constructors you can rely on (this == T.init), for example. You need to pick one T.init or default constructors and D picked T.init.
Feb 24 2014
next sibling parent reply "Remo" <remo4d gmail.com> writes:
On Monday, 24 February 2014 at 14:14:43 UTC, Tobias Pankrath 
wrote:
 On Monday, 24 February 2014 at 13:56:01 UTC, Remo wrote:
 Hi,

 right now I am truing to figure out how the constructors 
 behave in D2.

 Question 1: why it is not possible to create custom ctor for 
 struct?
The design of D relies on the fact that every type has a T.init property that is known to the compiler and used when in C++ the default ctor would get called. In constructors you can rely on (this == T.init), for example. You need to pick one T.init or default constructors and D picked T.init.
Well fortunately it seems to be possible to override init property. But it still does not called at struct construction. http://melpon.org/wandbox/permlink/9EvcdzKUKoufqbJa So what is proper/best way to mimic default constructor for struct ?
Feb 24 2014
next sibling parent reply "Stanislav Blinov" <stanislav.blinov gmail.com> writes:
On Monday, 24 February 2014 at 17:15:10 UTC, Remo wrote:

 Well fortunately it seems to be possible to override init 
 property.
Fortunately? I think not. It's an abomination that, IMO, has to be annihilated. Recently Andrei suggested adding more explicit semantics to .init that may give some leeway in this matter, although this was concerning classes and non-null default values, so it may not concern structs at all. Regardless, my advice - don't try to override .init, i.e. don't invite trouble into your code :)
 But it still does not called at struct construction.
 http://melpon.org/wandbox/permlink/9EvcdzKUKoufqbJa
Yup.
 So what is proper/best way to mimic default constructor for 
 struct ?
Don't do it. Default construction for struct *is* initialization of its fields. If you want to do something other that initialize fields - create a function and call it explicitly. D is not C++, don't expect it to behave identically. To make it easier when porting code, you can always temporarily disable this() so the compiler will stop whenever you'd use your "special" default construction in C++. As you've mentioned, the code from your example doesn't need any special default constructors at all, this will work just fine: struct Vector(T) { T x = 0, y = 0, z = 0; this(T v) { x = y = z = v; } this(T x, T y, T z) { this.x = x; this.y = y; this.z = z; } } unittest { Vector!double v; auto v2 = Vector!double(1); auto v3 = Vector!double(1,2,3); assert(v.x == v.y && v.y == v.z && v.z == 0); assert(v2.x == v2.x && v2.y == v2.z && v2.z == 1); assert(v3.x == 1 && v3.y == 2 && v3.z == 3); } Also please take a look at those: https://d.puremagic.com/issues/show_bug.cgi?id=3438 https://d.puremagic.com/issues/show_bug.cgi?id=6080 https://d.puremagic.com/issues/show_bug.cgi?id=7066 https://d.puremagic.com/issues/show_bug.cgi?id=7597 https://d.puremagic.com/issues/show_bug.cgi?id=8816 https://d.puremagic.com/issues/show_bug.cgi?id=8817 https://d.puremagic.com/issues/show_bug.cgi?id=10413 https://d.puremagic.com/issues/show_bug.cgi?id=11307 There may be some others I've missed; the sheer amount and unresolved state is terrifying.
Feb 24 2014
parent reply "Remo" <remo4d gmail.com> writes:
 Fortunately?
Yes I think it is. Of course it could be made a more safe in some way. I think the big advantage of D is that it has 'bridge' to C and C++. This way it appears to be easy to port some C++ code to D. And it appears to be easy to interconnect C++ and D code. (via Dll for example)
  disable this()
Yes this is possible. But then why it is not possible to use something like this ? default this(); For Vector example this works pretty well this way. But my main problem is more complicated. extern(C) int init(ref CWrapper p); extern(C) void free(ref CWrapper p); struct CWrapper { //some data that must be the same at C side. Data m_data; this() { init(&this); } ~this() { free(&this); } } How to do something like this in D ? Using class appears for me to be wrong direction. On Monday, 24 February 2014 at 18:13:02 UTC, Stanislav Blinov wrote:
 On Monday, 24 February 2014 at 17:15:10 UTC, Remo wrote:

 Well fortunately it seems to be possible to override init 
 property.
Fortunately? I think not. It's an abomination that, IMO, has to be annihilated. Recently Andrei suggested adding more explicit semantics to .init that may give some leeway in this matter, although this was concerning classes and non-null default values, so it may not concern structs at all. Regardless, my advice - don't try to override .init, i.e. don't invite trouble into your code :)
 But it still does not called at struct construction.
 http://melpon.org/wandbox/permlink/9EvcdzKUKoufqbJa
Yup.
 So what is proper/best way to mimic default constructor for 
 struct ?
Don't do it. Default construction for struct *is* initialization of its fields. If you want to do something other that initialize fields - create a function and call it explicitly. D is not C++, don't expect it to behave identically. To make it easier when porting code, you can always temporarily disable this() so the compiler will stop whenever you'd use your "special" default construction in C++. As you've mentioned, the code from your example doesn't need any special default constructors at all, this will work just fine: struct Vector(T) { T x = 0, y = 0, z = 0; this(T v) { x = y = z = v; } this(T x, T y, T z) { this.x = x; this.y = y; this.z = z; } } unittest { Vector!double v; auto v2 = Vector!double(1); auto v3 = Vector!double(1,2,3); assert(v.x == v.y && v.y == v.z && v.z == 0); assert(v2.x == v2.x && v2.y == v2.z && v2.z == 1); assert(v3.x == 1 && v3.y == 2 && v3.z == 3); } Also please take a look at those: https://d.puremagic.com/issues/show_bug.cgi?id=3438 https://d.puremagic.com/issues/show_bug.cgi?id=6080 https://d.puremagic.com/issues/show_bug.cgi?id=7066 https://d.puremagic.com/issues/show_bug.cgi?id=7597 https://d.puremagic.com/issues/show_bug.cgi?id=8816 https://d.puremagic.com/issues/show_bug.cgi?id=8817 https://d.puremagic.com/issues/show_bug.cgi?id=10413 https://d.puremagic.com/issues/show_bug.cgi?id=11307 There may be some others I've missed; the sheer amount and unresolved state is terrifying.
Feb 24 2014
next sibling parent reply "Remo" <remo4d gmail.com> writes:
 Also please take a look at those:

 https://d.puremagic.com/issues/show_bug.cgi?id=7066

 There may be some others I've missed; the sheer amount and 
 unresolved state is terrifying.
IMHO Issue 7066 is not a bug but a feature. Of course it could be handled i a bit more safe way. This looks like D2 is still in Beta stadium and not really ready for production use !?
Feb 24 2014
parent reply "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Monday, 24 February 2014 at 20:35:54 UTC, Remo wrote:
 This looks like D2 is still in Beta stadium and not really 
 ready for production use !?
I believe it is ready for production, but you can't expect it to be ready in all cases. Mostly its lack of readiness isn't because of the language though. For example it isn't ready for production Android/iOS development, because it hasn't been done yet. It isn't ready for Epic to write Unreal 5 Engine in it. However if Valve believed in D I'd say it was ready for them to take on the challenge and write Source 2 in D.
Feb 24 2014
parent "Tobias Pankrath" <tobias pankrath.net> writes:
On Tuesday, 25 February 2014 at 06:06:22 UTC, Jesse Phillips 
wrote:
 On Monday, 24 February 2014 at 20:35:54 UTC, Remo wrote:
 This looks like D2 is still in Beta stadium and not really 
 ready for production use !?
If someone thinks that C++ was production ready in 1998, just go and try a C++ compiler from this time.
Feb 24 2014
prev sibling parent "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Monday, 24 February 2014 at 19:41:57 UTC, Remo wrote:
 For Vector example this works pretty well this way.
 But my main problem is more complicated.

 extern(C)  int init(ref CWrapper p);
 extern(C) void free(ref CWrapper p);

 struct CWrapper
 {
   //some data that must be the same at C side.
   Data m_data;

   this() {
      init(&this);
   }
   ~this() {
      free(&this);
   }
 }

 How to do something like this in D ?
 Using class appears for me to be wrong direction.
1) Please comment after previous speaker, not before. You can't avoid default struct constructor problem if functions which you call in constructors are not at least CTFEable because you would need to call them if you wish to define default struct constructor. Since this is not your case, you can do: 2) You can use static OpCall, Voldemort type or alias this. In case of alias this it looks like: extern(C) int printf(const char*, ...); extern(C) int init(CWrapper* p) { printf("init\n"); return 0; } extern(C) void d_free(CWrapper* p) { printf("free\n"); } struct Data { void do_something(){} } struct CWrapper { //some data that must be the same at C side. Data m_data; bool m_init; /*this(int) { init(&this); }*/ ~this() { d_free(&this); } Data get() { if (!m_init) init(&this); return m_data; } alias get this; } void main() { CWrapper cw; cw.do_something(); } In case of Voldermort struct some struct is defined inside function, so the only way (in theory) to create an instance is to call that function. It is inconviniet but guarantees that you will not have non initialzed structs (in theory). extern(C) int printf(const char*,...); auto make_struct() { struct Data{} struct CWrapper { extern(C) int m_init(CWrapper* p){ return 0; } extern(C) void m_free(CWrapper* p){ printf("free\n");} //some data that must be the same at C side. Data m_data; this(int) { m_init(&this); } ~this() { m_free(&this); } } return CWrapper(0); } void main() { auto x = make_struct(); } 4) Please do not define free() function.
Feb 24 2014
prev sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Monday, 24 February 2014 at 17:15:10 UTC, Remo wrote:
 So what is proper/best way to mimic default constructor for 
 struct ?
Honestly, you can't, and you shouldn't try either. There "used" to be the static opCall that allowed: ---- auto a = T(); ---- But: a) This is being phased out: If T has a constructor, it will seize to compile. b) It's not "default": "T a;" will still compile, but not construct. The feedback I've been getting is that the "correct" way to guarantee construction is to do it via a named factory pattern. You disable the this(), so as to "force" initialization, and make the constructors private. You'll get something along the lines of: T a; //Nope. T a = T(); //Nope T a = T.build(); //OK! T a = T(1, 2); //Nope! T a = T.build(1, 2); //OK! T a = T.init; //OK! Special explicit requrest for non-initialisation. With D's move semantics and (N)RVO, there should be 0 overhead to do this.
Feb 24 2014
parent reply "Remo" <remo4d gmail.com> writes:
On Monday, 24 February 2014 at 21:06:03 UTC, monarch_dodra wrote:
 On Monday, 24 February 2014 at 17:15:10 UTC, Remo wrote:
 So what is proper/best way to mimic default constructor for 
 struct ?
Honestly, you can't, and you shouldn't try either. There "used" to be the static opCall that allowed: ---- auto a = T(); ---- But: a) This is being phased out: If T has a constructor, it will seize to compile. b) It's not "default": "T a;" will still compile, but not construct. The feedback I've been getting is that the "correct" way to guarantee construction is to do it via a named factory pattern. You disable the this(), so as to "force" initialization, and make the constructors private. You'll get something along the lines of: T a; //Nope. T a = T(); //Nope T a = T.build(); //OK! T a = T(1, 2); //Nope! T a = T.build(1, 2); //OK! T a = T.init; //OK! Special explicit requrest for non-initialisation. With D's move semantics and (N)RVO, there should be 0 overhead to do this.
Thanks, I will try to make this this way and look how well it will work. But it could complicate connection from C++ to D and back. Where I can find more info about 'D's move semantics'? Apparently it is not the same as rvalue reference and move semantics in C++11 ?
Feb 24 2014
parent "Jesse Phillips" <Jesse.K.Phillips+D gmail.com> writes:
On Monday, 24 February 2014 at 23:34:51 UTC, Remo wrote:
 Where I can find more info about 'D's move semantics'?
 Apparently it is not the same as rvalue reference and move 
 semantics in C++11 ?
Here is the place I know of: http://dconf.org/2013/talks/cehreli.html
Feb 24 2014
prev sibling parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Monday, 24 February 2014 at 14:14:43 UTC, Tobias Pankrath 
wrote:
 On Monday, 24 February 2014 at 13:56:01 UTC, Remo wrote:
 Hi,

 right now I am truing to figure out how the constructors 
 behave in D2.

 Question 1: why it is not possible to create custom ctor for 
 struct?
The design of D relies on the fact that every type has a T.init property that is known to the compiler and used when in C++ the default ctor would get called. In constructors you can rely on (this == T.init), for example. You need to pick one T.init or default constructors and D picked T.init.
The design of D relies on Andrei opinion. He is indeed convinced that default constructors are impossible. However, you can write "default constructor" right now like: struct S { Type t; this(int) { t = whather_is_callable_in_CTFE(); } } enum E : S { A = S(0) } void main() { E e; assert (e.t == whather_is_callable_in_CTFE()); } Since compiler does this, it can also accept straight syntax and semantic: struct S { Type t; this() // proxibited default constructor now { t = whather_is_callable_in_CTFE(); } } What D really lacks is ability to call function in runtime after struct instance creation like: struct S { runtime this() {} ~this(){} } lowering to: S s; s.__runtime_ctor(); However, since compiler does this all over the place (postblits, copy constructors, destructors, etc.) There is no conceptual difference between having: S s s.__runtime_ctor(); and S s; s.__dtor(); In other words, there is no objective necessity not to have compile time and runtime default struct constructors since compiler already heavily does conceptually and technically similar things.
Feb 24 2014
parent "Remo" <remo4d gmail.com> writes:
On Tuesday, 25 February 2014 at 07:59:33 UTC, Maxim Fomin wrote:
 On Monday, 24 February 2014 at 14:14:43 UTC, Tobias Pankrath 
 wrote:
 On Monday, 24 February 2014 at 13:56:01 UTC, Remo wrote:
 Hi,

 right now I am truing to figure out how the constructors 
 behave in D2.

 Question 1: why it is not possible to create custom ctor for 
 struct?
The design of D relies on the fact that every type has a T.init property that is known to the compiler and used when in C++ the default ctor would get called. In constructors you can rely on (this == T.init), for example. You need to pick one T.init or default constructors and D picked T.init.
The design of D relies on Andrei opinion. He is indeed convinced that default constructors are impossible. However, you can write "default constructor" right now like: struct S { Type t; this(int) { t = whather_is_callable_in_CTFE(); } } enum E : S { A = S(0) } void main() { E e; assert (e.t == whather_is_callable_in_CTFE()); } Since compiler does this, it can also accept straight syntax and semantic: struct S { Type t; this() // proxibited default constructor now { t = whather_is_callable_in_CTFE(); } } What D really lacks is ability to call function in runtime after struct instance creation like: struct S { runtime this() {} ~this(){} } lowering to: S s; s.__runtime_ctor(); However, since compiler does this all over the place (postblits, copy constructors, destructors, etc.) There is no conceptual difference between having: S s s.__runtime_ctor(); and S s; s.__dtor(); In other words, there is no objective necessity not to have compile time and runtime default struct constructors since compiler already heavily does conceptually and technically similar things.
Thanks for all this replies. Now I have better idea how it supposed to work. Yes something like runtime this() {} would be really great! https://d.puremagic.com/issues/show_bug.cgi?id=3438
 I think at some point we'll need to support default 
 constructors that execute
code. This was posted 2009-10-23 and now years later it is still not possible. :( Unfortunately this problems and workarounds makes porting C++ to D2 more complicated. Here are is also a small experiment that also show that 'ref' is also necessary in D2 just like in C++. There seems to be no optimization for this so using 'in' or nothing at all is slower and may have side effects. http://melpon.org/wandbox/permlink/FyaksIPW4u1dNpwh http://d.godbolt.org/#{%22version%22%3A3%2C%22filterAsm%22%3A{%22labels%22%3Atrue%2C%22directives%22%3Atrue%2C%22commentOnly%22%3Atrue%2C%22colouriseAsm%22%3Atrue}%2C%22compilers%22%3A[{%22sourcez%22%3A%22JYWwDg9gTgLgBAZxgEwHROcCBuAULgegIFcFgA7AczjCgpgDM4AjAUwGMBDU1uYeLjwSIQnADZjWUOJwQJWIZmICecdhGS9ZcAO50YrMeVT4kUYu3gBlXAG9ccR33LxgeJ3AdOYAC2AIACno%2BAEp7Dw9gOABePncIx1p6BgCAIis1GGg4AFIogB1yVIAaYBDsJy9HAF8qzw8iKAUIADcKaiamHyktcmQ4ZCzpHWAJFl4kl1Z%2BgGY4GFBWBBMPTrgMiDAAQTlgSnIAtfVyJACrEMQwuo9JxjSN7d39uELU8sqExyjY5bcvAEh%2Fk0YMQoOR5n4EHh%2FrUPHUAH6%2BfwBK6fW4pdIDIYvIrvD5OWq1XAtCDAfqiCgoux1DIIACM8UcRFpACYYutGetEHNYlYUZz6eyEDM8NUgAAA%3D%22%2C%22compiler%22%3A%22%2Fusr%2Fbin%2Fgdc%22%2C%22options%22%3A%22-O2%22}]}
Feb 25 2014