www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Would love to override default ctor of struct

reply Dru <dru notreal.com> writes:
What is the idea behind why we can't define default ctor for 
structs?

In current situation I need to " disable this()"
then define a constructor with dummy arguments  (because my 
constructor does not need arguments)

It is a big pain on syntax
Jan 19
next sibling parent reply Alex <sascha.orlov gmail.com> writes:
On Saturday, 19 January 2019 at 10:05:22 UTC, Dru wrote:
 What is the idea behind why we can't define default ctor for 
 structs?

 In current situation I need to " disable this()"
 then define a constructor with dummy arguments  (because my 
 constructor does not need arguments)

 It is a big pain on syntax
If your constructor doesn't need any arguments, what is the reason you can't define default values to your members?
Jan 19
parent reply Basile B. <b2.temp gmx.com> writes:
On Saturday, 19 January 2019 at 10:18:34 UTC, Alex wrote:
 On Saturday, 19 January 2019 at 10:05:22 UTC, Dru wrote:
 What is the idea behind why we can't define default ctor for 
 structs?

 In current situation I need to " disable this()"
 then define a constructor with dummy arguments  (because my 
 constructor does not need arguments)

 It is a big pain on syntax
If your constructor doesn't need any arguments, what is the reason you can't define default values to your members?
I see your point but... 1. default member values (via initializer) are more like a shared this(). 2. not everything can be set using member initializer because of CTFE limitations.
Jan 19
parent reply Alex <sascha.orlov gmail.com> writes:
On Saturday, 19 January 2019 at 10:22:11 UTC, Basile B. wrote:
 On Saturday, 19 January 2019 at 10:18:34 UTC, Alex wrote:
 On Saturday, 19 January 2019 at 10:05:22 UTC, Dru wrote:
 What is the idea behind why we can't define default ctor for 
 structs?

 In current situation I need to " disable this()"
 then define a constructor with dummy arguments  (because my 
 constructor does not need arguments)

 It is a big pain on syntax
If your constructor doesn't need any arguments, what is the reason you can't define default values to your members?
I see your point but... 1. default member values (via initializer) are more like a shared this().
Ok. And why this is a problem? I mean, if the sharedness is not wanted, then, the thread id should go as a constructor parameter, shouldn't it?
 2. not everything can be set using member initializer because 
 of CTFE limitations.
Hm... ok. But even then, if something does not work in the way it should because of limitations: What prevents of setting the parameter inside a function after or on creation? Or, using a non-default constructor exactly for these params?
Jan 19
parent reply Nick Treleaven <nick geany.org> writes:
On Saturday, 19 January 2019 at 11:01:14 UTC, Alex wrote:
 2. not everything can be set using member initializer because 
 of CTFE limitations.
Hm... ok. But even then, if something does not work in the way it should because of limitations: What prevents of setting the parameter inside a function after or on creation?
Performance for repeated function calls after construction, we don't want an extra branch test on each function call and an extra bool bloating/tainting fields in the struct. A pseudo constructor function is a workaround, but is a bit ugly, adding a separate symbol you have to check for when learning a new library struct, and the function has more unnecessary boilerplate vs a constructor.
 Or, using a non-default constructor exactly for these params?
You can't have a zero argument non-default struct constructor. I've never been told why: https://wiki.dlang.org/Language_issues#Explicit_syntax_and_.40disable_this Only the default constructor is constrained by .init, explicit zero argument construction is unconstrained. It could be allowed.
Jan 21
parent reply Alex <sascha.orlov gmail.com> writes:
On Monday, 21 January 2019 at 17:06:55 UTC, Nick Treleaven wrote:
 On Saturday, 19 January 2019 at 11:01:14 UTC, Alex wrote:
 2. not everything can be set using member initializer because 
 of CTFE limitations.
Hm... ok. But even then, if something does not work in the way it should because of limitations: What prevents of setting the parameter inside a function after or on creation?
Performance for repeated function calls after construction, we don't want an extra branch test on each function call and an extra bool bloating/tainting fields in the struct. A pseudo constructor function is a workaround, but is a bit ugly, adding a separate symbol you have to check for when learning a new library struct, and the function has more unnecessary boilerplate vs a constructor.
 Or, using a non-default constructor exactly for these params?
You can't have a zero argument non-default struct constructor. I've never been told why: https://wiki.dlang.org/Language_issues#Explicit_syntax_and_.40disable_this Only the default constructor is constrained by .init, explicit zero argument construction is unconstrained. It could be allowed.
Could you give an example, where a zero argument construction has to be done, which cannot be accomplished by setting the appropriate field with a default value?
Jan 21
next sibling parent reply Meta <jared771 gmail.com> writes:
On Monday, 21 January 2019 at 19:08:49 UTC, Alex wrote:
 Could you give an example, where a zero argument construction 
 has to be done, which cannot be accomplished by setting the 
 appropriate field with a default value?
For the following reason, although the default argument constructor hack no longer works: import std.stdio; struct Test(bool useFieldDefaultVal) { static if (useFieldDefaultVal) { int[] arr = [1, 2, 3]; } else { int[] arr; this(int dummy = 0) { arr = [1, 2, 3]; } } void doTest() { writeln("Address of arr: ", arr.ptr); } } void main() { Test!true t1; Test!true t2; // Deprecation: constructor `onlineapp.Test!false.Test.this` // all parameters have default arguments, but structs cannot // have default constructors. auto t3 = Test!false(); t1.doTest(); // Prints "Address of arr: 5622DAA0F010" t2.doTest(); // Prints "Address of arr: 5622DAA0F010" t3.doTest(); // Prints "Address of arr: null" }
Jan 21
next sibling parent Alex <sascha.orlov gmail.com> writes:
On Monday, 21 January 2019 at 20:50:45 UTC, Meta wrote:
 On Monday, 21 January 2019 at 19:08:49 UTC, Alex wrote:
 Could you give an example, where a zero argument construction 
 has to be done, which cannot be accomplished by setting the 
 appropriate field with a default value?
For the following reason, although the default argument constructor hack no longer works: [...]
Ok, and I'm wondering what of this initialization cannot be done without a constructor?
Jan 21
prev sibling parent reply aliak <something something.com> writes:
On Monday, 21 January 2019 at 20:50:45 UTC, Meta wrote:
 On Monday, 21 January 2019 at 19:08:49 UTC, Alex wrote:
 Could you give an example, where a zero argument construction 
 has to be done, which cannot be accomplished by setting the 
 appropriate field with a default value?
For the following reason, although the default argument constructor hack no longer works: import std.stdio; struct Test(bool useFieldDefaultVal) { static if (useFieldDefaultVal) { int[] arr = [1, 2, 3]; } else { int[] arr; this(int dummy = 0) { arr = [1, 2, 3]; } } void doTest() { writeln("Address of arr: ", arr.ptr); } } void main() { Test!true t1; Test!true t2; // Deprecation: constructor `onlineapp.Test!false.Test.this` // all parameters have default arguments, but structs cannot // have default constructors. auto t3 = Test!false(); t1.doTest(); // Prints "Address of arr: 5622DAA0F010" t2.doTest(); // Prints "Address of arr: 5622DAA0F010" t3.doTest(); // Prints "Address of arr: null" }
Ok, so this confused me a bit, I seem to remember that when you static inisialize a class in the declaration scope of a struct, the same thing would happen as with arrays, i.e. they'd have the same address. But this turned out different: import std.stdio; class C {} struct Test(bool useFieldDefaultVal) { static if (useFieldDefaultVal) { C c = new C; } else { C c; this(int dummy = 0) { c = new C; } } void doTest() { writeln("Address of c: ", &c); } } void main() { Test!true t1; Test!true t2; // Deprecation: constructor `onlineapp.Test!false.Test.this` // all parameters have default arguments, but structs cannot // have default constructors. auto t3 = Test!false(); t1.doTest(); // Prints "Address of arr: 7FFE1322A7E8" t2.doTest(); // Prints "Address of arr: 7FFE1322A7F0" t3.doTest(); // Prints "Address of arr: 7FFE1322A7F8" } t1 and t2 have different addresses and t3 has t1's address?? Huh? Vat Khappened Khere?
Jan 21
next sibling parent reply Meta <jared771 gmail.com> writes:
On Tuesday, 22 January 2019 at 01:18:24 UTC, aliak wrote:
 On Monday, 21 January 2019 at 20:50:45 UTC, Meta wrote:
 On Monday, 21 January 2019 at 19:08:49 UTC, Alex wrote:
 Could you give an example, where a zero argument construction 
 has to be done, which cannot be accomplished by setting the 
 appropriate field with a default value?
For the following reason, although the default argument constructor hack no longer works: import std.stdio; struct Test(bool useFieldDefaultVal) { static if (useFieldDefaultVal) { int[] arr = [1, 2, 3]; } else { int[] arr; this(int dummy = 0) { arr = [1, 2, 3]; } } void doTest() { writeln("Address of arr: ", arr.ptr); } } void main() { Test!true t1; Test!true t2; // Deprecation: constructor `onlineapp.Test!false.Test.this` // all parameters have default arguments, but structs cannot // have default constructors. auto t3 = Test!false(); t1.doTest(); // Prints "Address of arr: 5622DAA0F010" t2.doTest(); // Prints "Address of arr: 5622DAA0F010" t3.doTest(); // Prints "Address of arr: null" }
Ok, so this confused me a bit, I seem to remember that when you static inisialize a class in the declaration scope of a struct, the same thing would happen as with arrays, i.e. they'd have the same address. But this turned out different: import std.stdio; class C {} struct Test(bool useFieldDefaultVal) { static if (useFieldDefaultVal) { C c = new C; } else { C c; this(int dummy = 0) { c = new C; } } void doTest() { writeln("Address of c: ", &c); } } void main() { Test!true t1; Test!true t2; // Deprecation: constructor `onlineapp.Test!false.Test.this` // all parameters have default arguments, but structs cannot // have default constructors. auto t3 = Test!false(); t1.doTest(); // Prints "Address of arr: 7FFE1322A7E8" t2.doTest(); // Prints "Address of arr: 7FFE1322A7F0" t3.doTest(); // Prints "Address of arr: 7FFE1322A7F8" } t1 and t2 have different addresses and t3 has t1's address?? Huh? Vat Khappened Khere?
You're printing the address of the object reference, not the object itself (similar to printing the address of a pointer instead of the actual address it points to). Do `cast(void*)c` instead.
Jan 21
parent aliak <something something.com> writes:
On Tuesday, 22 January 2019 at 03:30:34 UTC, Meta wrote:
 You're printing the address of the object reference, not the 
 object itself (similar to printing the address of a pointer 
 instead of the actual address it points to). Do `cast(void*)c` 
 instead.
Doh! Thanks.
Jan 22
prev sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
On Tue, 22 Jan 2019 01:18:24 +0000, aliak wrote:
      t1.doTest(); // Prints "Address of arr: 7FFE1322A7E8"
      t2.doTest(); // Prints "Address of arr: 7FFE1322A7F0"
      t3.doTest(); // Prints "Address of arr: 7FFE1322A7F8"
 }
 
 t1 and t2 have different addresses and t3 has t1's address?? Huh?\
E8 != F8.
Jan 21
parent aliak <something something.com> writes:
On Tuesday, 22 January 2019 at 03:36:31 UTC, Neia Neutuladh wrote:
 On Tue, 22 Jan 2019 01:18:24 +0000, aliak wrote:
      t1.doTest(); // Prints "Address of arr: 7FFE1322A7E8"
      t2.doTest(); // Prints "Address of arr: 7FFE1322A7F0"
      t3.doTest(); // Prints "Address of arr: 7FFE1322A7F8"
 }
 
 t1 and t2 have different addresses and t3 has t1's address?? 
 Huh?\
E8 != F8.
lol. True.
Jan 22
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, January 21, 2019 12:08:49 PM MST Alex via Digitalmars-d wrote:
 On Monday, 21 January 2019 at 17:06:55 UTC, Nick Treleaven wrote:
 On Saturday, 19 January 2019 at 11:01:14 UTC, Alex wrote:
 2. not everything can be set using member initializer because
 of CTFE limitations.
Hm... ok. But even then, if something does not work in the way it should because of limitations: What prevents of setting the parameter inside a function after or on creation?
Performance for repeated function calls after construction, we don't want an extra branch test on each function call and an extra bool bloating/tainting fields in the struct. A pseudo constructor function is a workaround, but is a bit ugly, adding a separate symbol you have to check for when learning a new library struct, and the function has more unnecessary boilerplate vs a constructor.
 Or, using a non-default constructor exactly for these params?
You can't have a zero argument non-default struct constructor. I've never been told why: https://wiki.dlang.org/Language_issues#Explicit_syntax_and_.40disable_th is Only the default constructor is constrained by .init, explicit zero argument construction is unconstrained. It could be allowed.
Could you give an example, where a zero argument construction has to be done, which cannot be accomplished by setting the appropriate field with a default value?
A classic case would be some uses of RAII such as what MFC does with its hourglass. You get code something like Hourglass hg; and that does everything. The constructor pops up the hourglass, and the destructor takes it down. If you wanted an object like that in D, you would have to use a factory function. e.g. auto hg = Hourglass.create(); and you would then have to make it so that the object can deal with the fact that the init value never when through the factory function and thus must do nothing in its destructor (using disable this() to disable default initialization reduces that problem, but since the init value can still be used explicitly, it really doesn't eliminate the problem). Similarly, as Meta alludes to in his example, having dynamic arrays of mutable elements as member variables is problematic with structs, because all of the instances of that struct on the same thread have a dynamic array that is a slice of the same chunk of memory, meaning that mutating an element in one mutates all of them (at least all of them which haven't ended up with their array being reallocated due to appending or whatnot). In most cases, you'd really want a separate dynamic array for each instance of the struct, and D doesn't give a good way to do that with init values, forcing you to use a factory function instead of a constructor if you want to try and force it. And now that we can actually directly initialize member variables which are pointers or class references, those join the ranks of potentially having problems due to all of the instances of that struct on a particular thread having the same value for those pointers or references. So, it can become a bit error-prone for those types of member variables. And really, any and all situations where you're looking to have a piece of code run when an instance of the struct is created but where you don't necessarily have arguments for the constructor is going to be harder to deal with cleanly in D thanks to the lack of default constructors. In general, you just learn to live with it and use factory functions to deal with the problem in those cases where it pops up. So, it usually isn't a big deal, and it's mostly just something that folks new to D complain about, but having init values such that we cannot have default constructors for structs is an area in D that's a tradeoff, not a complete win. Many of us think that the tradeoffs involved are well worth it, but that doesn't change the fact that there are times when having a default constructor would be really nice, and the fact that you can't is then annoying. - Jonathan M Davis
Jan 21
parent Alex <sascha.orlov gmail.com> writes:
On Monday, 21 January 2019 at 22:48:41 UTC, Jonathan M Davis 
wrote:
 A classic case would be some uses of RAII such as what MFC does 
 with its hourglass. You get code something like

 Hourglass hg;

 and that does everything. The constructor pops up the 
 hourglass, and the destructor takes it down. If you wanted an 
 object like that in D, you would have to use a factory 
 function. e.g.

 auto hg = Hourglass.create();

 and you would then have to make it so that the object can deal 
 with the fact that the init value never when through the 
 factory function and thus must do nothing in its destructor 
 (using  disable this() to disable default initialization 
 reduces that problem, but since the init value can still be 
 used explicitly, it really doesn't eliminate the problem).

 Similarly, as Meta alludes to in his example, having dynamic 
 arrays of mutable elements as member variables is problematic 
 with structs, because all of the instances of that struct on 
 the same thread have a dynamic array that is a slice of the 
 same chunk of memory, meaning that mutating an element in one 
 mutates all of them (at least all of them which haven't ended 
 up with their array being reallocated due to appending or 
 whatnot).
Ah!!
 In most cases, you'd really want a separate dynamic array for 
 each instance of the struct, and D doesn't give a good way to 
 do that with init values, forcing you to use a factory function 
 instead of a constructor if you want to try and force it. And 
 now that we can actually directly initialize member variables 
 which are pointers or class references, those join the ranks of 
 potentially having problems due to all of the instances of that 
 struct on a particular thread having the same value for those 
 pointers or references. So, it can become a bit error-prone for 
 those types of member variables.

 And really, any and all situations where you're looking to have 
 a piece of code run when an instance of the struct is created 
 but where you don't necessarily have arguments for the 
 constructor is going to be harder to deal with cleanly in D 
 thanks to the lack of default constructors.

 In general, you just learn to live with it and use factory 
 functions to deal with the problem in those cases where it pops 
 up. So, it usually isn't a big deal, and it's mostly just 
 something that folks new to D complain about, but having init 
 values such that we cannot have default constructors for 
 structs is an area in D that's a tradeoff, not a complete win. 
 Many of us think that the tradeoffs involved are well worth it, 
 but that doesn't change the fact that there are times when 
 having a default constructor would be really nice, and the fact 
 that you can't is then annoying.
Ok... see it now...
Jan 21
prev sibling next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Saturday, January 19, 2019 3:05:22 AM MST Dru via Digitalmars-d wrote:
 What is the idea behind why we can't define default ctor for
 structs?

 In current situation I need to " disable this()"
 then define a constructor with dummy arguments  (because my
 constructor does not need arguments)

 It is a big pain on syntax
Structs in D don't actually have default constructors. Rather, they have a default value that they're initialized to - namely T.init (where T is the type name). In fact, all types in D have a default value so that they're guaranteed to not be garbage if you forget to initialize them with a specific value. This gets taken advantage of in a number of places. One obvious one is arrays. The init value can simply be blitted into all of the elements of the array. A number of features in D are built around that and really don't work without it. In fact, if you disable this() to disable default-initialization, it makes it so that you can't do a number of things with that type that you can normally do (e.g. put it in an array), because those language features require default-initialization. In fact, default values are so critical in D that a struct with disable this() still has an init value (e.g. it's used to initialize the struct before the constructor is called). It's just that you can't use the struct in places where default initialization is required, because you told the compiler that you didn't want that to be allowed. However, code may still explicitly use the init value, and some functions will use it (e.g move uses it to ensure that the memory that's left behind after the move isn't in a garbage state). So, while disable this _can_ be useful, it tends to just be begging for problems, because D is very much designed with the idea that everything can be default-initialized, and disable this() was only really added as a hack for rare cases where you really needed to not have something be default-initialized. If you really need something akin to a default constructor, then the normal thing to do is to use a factory function, not do something funky like have dummy arguments. And it may be appropriate in such a situation to disable this() so that you don't end up with the object being default-initialized (theoretically forcing folks to use the factory function), but even then, there are cases where the init value may still get used, so relying on it never being used can be problematic. And yes, sometimes, the fact that you can't have default constructors on structs in D like you would in C++ can be annoying, but it's a consequence of the fact that D insists on not having types created with garbage values (as frequently happens in C/C++), and the rest of the language was then built with the assumption that everything is default-initialized. So, while it can be annoying, it does prevent a whole class of bugs that are common in other languages. All of the various ways around it were added later and are used at your own risk. Alternatively, unlike structs, because classes are reference types (and thus default-initialized to null), they are able to have default constructors. So, if allocating your objects is fine, then classes may be a better option for whatever you're trying to do that requires a default constructor. Or they may not - that would depend on what the code needs to do - but it's something to consider. - Jonathan M Davis
Jan 19
parent reply aliak <something something.com> writes:
On Saturday, 19 January 2019 at 11:13:22 UTC, Jonathan M Davis 
wrote:
 On Saturday, January 19, 2019 3:05:22 AM MST Dru via 
 Digitalmars-d wrote:
 What is the idea behind why we can't define default ctor for 
 structs?

 In current situation I need to " disable this()"
 then define a constructor with dummy arguments  (because my
 constructor does not need arguments)

 It is a big pain on syntax
Structs in D don't actually have default constructors. Rather, they have a default value that they're initialized to - namely T.init (where T is the type name). In fact, all types in D have a default value so that they're guaranteed to not be garbage if you forget to initialize them with a specific value. This gets taken advantage of in a number of places. One obvious one is arrays. The init value can simply be blitted into all of the elements of the array. A number of features in D are built around that and really don't work without it.
I've always wondered why T.init can't be it's own thing and separate from this(). So if someone defined a this() then D just treats it like a custom non-default constructor? Not possible?
Jan 21
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, January 21, 2019 6:23:21 PM MST aliak via Digitalmars-d wrote:
 On Saturday, 19 January 2019 at 11:13:22 UTC, Jonathan M Davis

 wrote:
 On Saturday, January 19, 2019 3:05:22 AM MST Dru via

 Digitalmars-d wrote:
 What is the idea behind why we can't define default ctor for
 structs?

 In current situation I need to " disable this()"
 then define a constructor with dummy arguments  (because my
 constructor does not need arguments)

 It is a big pain on syntax
Structs in D don't actually have default constructors. Rather, they have a default value that they're initialized to - namely T.init (where T is the type name). In fact, all types in D have a default value so that they're guaranteed to not be garbage if you forget to initialize them with a specific value. This gets taken advantage of in a number of places. One obvious one is arrays. The init value can simply be blitted into all of the elements of the array. A number of features in D are built around that and really don't work without it.
I've always wondered why T.init can't be it's own thing and separate from this(). So if someone defined a this() then D just treats it like a custom non-default constructor? Not possible?
If D allowed struct's to have a constructor without any parameters, it would not be and could not be a default constructor because of how init works. It would only be a constructor that was called when used explicitly. As such, it wouldn't really add much over just using a factory function, and it would run a serious risk of confusing people, because newcomers would expect this() to be a default constructor when it wasn't. It also would be bad for porting code to D from languages like C++, because without extra work from the programmer, the code would assume that this() was a default constructor when it wasn't, making it easy to end up with subtle bugs. By outright making this() illegal, D forces the programmer to deal with the situation rather than allowing silent bugs. If it hadn't, I can guarantee that there would have been tons of confusion and complaints over how this() wasn't working correctly, because everyone coming to D would expect it to be a default constructor and then be very confused when it wasn't being called in code like Foo foo; or auto arr = new Foo[](42); The only downside that I can think of at the moment for using a factory function over having this() as a non-default constructor is that when constructing immutable objects, the constructor usually has to do it (though in some situations, casts could be used - that depends primarily on whether the data is guaranteed to be unique). So, such a factory function would require a special constructor with dummy arguments or something similar in order to construct immutable objects. But aside from that, a factory function is just as good as a constructor, and it doesn't carry with it the confusion of whether this() is a default constructor or not. - Jonathan M Davis
Jan 21
next sibling parent reply Dru <dru notreal.com> writes:
 If D allowed struct's to have a constructor without any 
 parameters, it would not be and could not be a default 
 constructor because of how init works. It would only be a 
 constructor that was called when used explicitly.
the problem is construction of static variables right? we could allow to define a default ctor and then give an error in case it is used for a static variable. A a; //would error if A has a runtime default ctor these would still work: A a = A.init; A a = void;
Jan 22
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, January 22, 2019 2:38:41 PM MST Dru via Digitalmars-d wrote:
 If D allowed struct's to have a constructor without any
 parameters, it would not be and could not be a default
 constructor because of how init works. It would only be a
 constructor that was called when used explicitly.
the problem is construction of static variables right? we could allow to define a default ctor and then give an error in case it is used for a static variable. A a; //would error if A has a runtime default ctor these would still work: A a = A.init; A a = void;
It's far from just static variables. For instance, init is a core part of how arrays are initialized. You can't even put something in an array if default initialization is disabled for that type. The language in general assumes that everything is default-initialized, and when it can't be, you start running running into stray places where you can't do stuff that you can normally do. Introducing any kind of default construction to structs would be a massive shift, and I doubt that I could come up with all of the stuff that would be affected off the top of my head. - Jonathan M Davis
Jan 23
prev sibling parent reply Nick Treleaven <nick geany.org> writes:
On Tuesday, 22 January 2019 at 02:18:37 UTC, Jonathan M Davis 
wrote:
 It also would be bad for porting code to D from languages like 
 C++, because without extra work from the programmer, the code 
 would assume that this() was a default constructor when it 
 wasn't, making it easy to end up with subtle bugs. By outright 
 making this() illegal, D forces the programmer to deal with the 
 situation rather than allowing silent bugs.
D could solve this by only allowing a nullary constructor when there's ` disable this();`. The fact that ` disable this()` exists undermines the reason for a blanket ban on `this(){...}`, and I expect it's this way just because disable this was added later. This is worth having and would support Mutex, HourGlass, etc. Besides runtime immutable initialization not working in a free function 'constructor' as you mentioned, not allowing a nullary constructor breaks the variadic constructor pattern in generic code when no arguments are passed.
Jan 31
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, January 31, 2019 4:42:35 AM MST Nick Treleaven via Digitalmars-
d wrote:
 On Tuesday, 22 January 2019 at 02:18:37 UTC, Jonathan M Davis

 wrote:
 It also would be bad for porting code to D from languages like
 C++, because without extra work from the programmer, the code
 would assume that this() was a default constructor when it
 wasn't, making it easy to end up with subtle bugs. By outright
 making this() illegal, D forces the programmer to deal with the
 situation rather than allowing silent bugs.
D could solve this by only allowing a nullary constructor when there's ` disable this();`. The fact that ` disable this()` exists undermines the reason for a blanket ban on `this(){...}`, and I expect it's this way just because disable this was added later. This is worth having and would support Mutex, HourGlass, etc. Besides runtime immutable initialization not working in a free function 'constructor' as you mentioned, not allowing a nullary constructor breaks the variadic constructor pattern in generic code when no arguments are passed.
The main problem with allowing a constructor with no parameters when you disable this(); is that it wouldn't act like a default constructor and really couldn't act like one in general, because too much is designed around using init. To make it work, you'd have to start making stuff work one way with init normally and another way when disable this() was present, which would be confusing and likely error-prone. And yes, the issues with disable this(); stem from the fact that it was shoehorned into the language later. D was very much designed around the idea that all types are always default initialized, and any attempt to work around that starts causing problems. disable this(); is rather similar to = void; in that regard. There are cases where it makes sense to use them, but you need to know what you're doing, and their use should be quite rare. They're basically backdoors to force the language to do something that it's really not designed to do. Also, it's perfectly possible to use a factory function to create immutable objects. Worst case, you have to cast to immutable in the factory function, but it works just fine. AFAIK, aside from stuff that requires that the type be default-initialized, the only thing that you can't really do with disable this(); is to completely guarantee that all objects are constructed via the factory method, because the init value for the type still exists and can still be used. It just isn't used for default initialization. So, having a type with disable this(); that does not take the init value into account can cause bugs. But all of the other stuff with regards to immutable and the like can still be done. It just can't necessarily be done in an safe manner. - Jonathan M Davis
Jan 31
parent reply Nick Treleaven <nick geany.org> writes:
On Thursday, 31 January 2019 at 15:41:28 UTC, Jonathan M Davis 
wrote:
 The main problem with allowing a constructor with no parameters 
 when you  disable this(); is that it wouldn't act like a 
 default constructor and really couldn't act like one in 
 general, because too much is designed around using init. To
I'm not asking for a default constructor, only an explicit nullary argument ctor.
 Also, it's perfectly possible to use a factory function to 
 create immutable objects. Worst case, you have to cast to 
 immutable in the factory function, but it works just fine.
Have you changed your opinion? You said only that "in some situations" casts can be used: "The only downside that I can think of at the moment for using a factory function over having this() as a non-default constructor is that when constructing immutable objects, the constructor usually has to do it (though in some situations, casts could be used - that depends primarily on whether the data is guaranteed to be unique). So, such a factory function would require a special constructor with dummy arguments or something similar in order to construct immutable objects."
 AFAIK, aside from stuff that requires that the type be 
 default-initialized, the only thing that you can't really do 
 with  disable this(); is to completely guarantee that all 
 objects are constructed via the factory method, because the 
 init value for the type still exists and can still be used. It 
 just isn't used for default initialization. So, having a type 
 with  disable this(); that does not take the init value into 
 account can cause bugs.
struct S { static disable S init; ...
 But all of the other stuff with regards to immutable and the 
 like can still be done. It just can't necessarily be done in an 
  safe manner.
This is a significant downside, you can't have a safe struct that requires runtime calls for its construction but no arguments.
Feb 07
next sibling parent aliak <something something.com> writes:
On Thursday, 7 February 2019 at 11:14:47 UTC, Nick Treleaven 
wrote:
 On Thursday, 31 January 2019 at 15:41:28 UTC, Jonathan M Davis 
 wrote:
 The main problem with allowing a constructor with no 
 parameters when you  disable this(); is that it wouldn't act 
 like a default constructor and really couldn't act like one in 
 general, because too much is designed around using init. To
I'm not asking for a default constructor, only an explicit nullary argument ctor.
 Also, it's perfectly possible to use a factory function to 
 create immutable objects. Worst case, you have to cast to 
 immutable in the factory function, but it works just fine.
Have you changed your opinion? You said only that "in some situations" casts can be used: "The only downside that I can think of at the moment for using a factory function over having this() as a non-default constructor is that when constructing immutable objects, the constructor usually has to do it (though in some situations, casts could be used - that depends primarily on whether the data is guaranteed to be unique). So, such a factory function would require a special constructor with dummy arguments or something similar in order to construct immutable objects."
 AFAIK, aside from stuff that requires that the type be 
 default-initialized, the only thing that you can't really do 
 with  disable this(); is to completely guarantee that all 
 objects are constructed via the factory method, because the 
 init value for the type still exists and can still be used. It 
 just isn't used for default initialization. So, having a type 
 with  disable this(); that does not take the init value into 
 account can cause bugs.
struct S { static disable S init; ...
 But all of the other stuff with regards to immutable and the 
 like can still be done. It just can't necessarily be done in 
 an  safe manner.
This is a significant downside, you can't have a safe struct that requires runtime calls for its construction but no arguments.
It seems to me that the only argument I've heard to not allow default construction is because people will get confused as to why array initialization doesn't use the constructor, and maybe some other cases. But, at the same time, the difference between construction and initialization is a said to be a "core concept" of D. So this seems quite reasonably solved with some documentation and just being more explicit about the separation between construction and initialization? It certainly beat the (exaggerated) weekly forum posts asking for default struct construction and why it's not allowed (speaking of confusing people) :) Cheers, - Ali
Feb 07
prev sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, February 7, 2019 4:14:47 AM MST Nick Treleaven via Digitalmars-
d wrote:
 On Thursday, 31 January 2019 at 15:41:28 UTC, Jonathan M Davis

 wrote:
 The main problem with allowing a constructor with no parameters
 when you  disable this(); is that it wouldn't act like a
 default constructor and really couldn't act like one in
 general, because too much is designed around using init. To
I'm not asking for a default constructor, only an explicit nullary argument ctor.
 Also, it's perfectly possible to use a factory function to
 create immutable objects. Worst case, you have to cast to
 immutable in the factory function, but it works just fine.
Have you changed your opinion? You said only that "in some situations" casts can be used: "The only downside that I can think of at the moment for using a factory function over having this() as a non-default constructor is that when constructing immutable objects, the constructor usually has to do it (though in some situations, casts could be used - that depends primarily on whether the data is guaranteed to be unique). So, such a factory function would require a special constructor with dummy arguments or something similar in order to construct immutable objects."
At the moment, the only situations that I can think of where casting to immutable would not work are actually situations where you couldn't use a constructor anyway. The problem is that you can't cast data that's not guaranteed to be unique to immutable, or you risk violating the type system. With a constructor that did no casting, that would mean that you could only accept data that was either immutable or implicitly convertible to immutable. Anything else wouldn't compile. So, to get around that, you would have to cast just like you'd need to do with a factory function, and you'd then be at risk of violating the type system whenever the data was not actually unique. Because the factory function would have to cast, the programmer would have to be more careful about making sure that they didn't cast anything that wasn't guaranteed to be unique (whereas in a constructor with no casts, you wouldn't have to worry about that), so it's trusted and riskier, but it works so long as you're appropriately careful. Having a pure factory function would potentially fix the need for casting, but if you're trying to create a default constructor, odds are that it can't be pure anyway. If it could, then it's almost guaranteed that you could just put everything in the init value and not need a constructor - though there are some situations where that still wouldn't work - e.g. initializing a member variable that was an AA (since AA's don't currently travel from compile time to runtime) or when you wanted a dynamic array with known values but where each instance of the struct gets a dynamic array referring to unique memory rather than all instances sharing the same memory like you get with the init value.
 AFAIK, aside from stuff that requires that the type be
 default-initialized, the only thing that you can't really do
 with  disable this(); is to completely guarantee that all
 objects are constructed via the factory method, because the
 init value for the type still exists and can still be used. It
 just isn't used for default initialization. So, having a type
 with  disable this(); that does not take the init value into
 account can cause bugs.
struct S { static disable S init; ...
I'm surprised that you can do that at the moment. I thought that that was at least deprecated by now. TypeInfo was specifically fixed a while back so that it didn't have an init function so that we could deprecate the ability to declare any kind of override for init, because allowing it causes problems. One example is that it causes serious problems with metaprogramming, because init is _the_ reliable way to get your hands on an instance of a type. It was quite purposefully the case that disable this(); had no effect on the ability to reference this, because too much relies on being able to access it. Trying to make it so that you cannot rely on init existing would be extremely disruptive. Now, even then, disabling init doesn't actually get rid of the init value. It's still there and used to do stuff like initialize the struct prior to the constructor being called. Also, interestingly, I just tried struct S { disable this(); static disable S init; } and it won't compile, because that init variable uses default initialization. That can be gotten around be declaring something like static disable S init(); instead, but really, trying to get around the fact that types have init values in D is like trying to plug holes in a cheese grater. Anyone who wants to try to add default constructors to D is free to create a DIP to do so, but I expect that anyone who attempted it would have a serious battle ahead of them. You would need an extremely good understanding of exactly how the init value works and everywhere that it is used and why to even attempt to come up with a proposal to inject default constructors that would work (assuming that it's even possible to make init and default constructors interact in a sane manner). And even assuming that you had the appropriate technical knowledge to correctly indicate exactly how default constructors would work to be able to integrate with the existing language, I expect that convincing Walter and Andrei that it was worth it would not be an easy task.
 But all of the other stuff with regards to immutable and the
 like can still be done. It just can't necessarily be done in an
  safe manner.
This is a significant downside, you can't have a safe struct that requires runtime calls for its construction but no arguments.
It could be trusted. It's just that it's up to the programmer to verify it. And sure, that's not ideal, but safe can't be used everywhere without the programmer having to deal with trusted to make it work occasionally, and we're talking about a pretty niche situation here with trying to have something similar to a default constructor for an immutable object. Regardless, with how D relies on init, that's just the way that it is. - Jonathan M Davis
Feb 08
prev sibling parent reply Guillaume Piolat <first.last gmail.com> writes:
On Saturday, 19 January 2019 at 10:05:22 UTC, Dru wrote:
 What is the idea behind why we can't define default ctor for 
 structs?

 In current situation I need to " disable this()"
 then define a constructor with dummy arguments  (because my 
 constructor does not need arguments)

 It is a big pain on syntax
Short answer: make T.init a value, and have your destructor be able to deal with T.init
Jan 23
parent Guillaume Piolat <first.last gmail.com> writes:
On Wednesday, 23 January 2019 at 13:02:43 UTC, Guillaume Piolat 
wrote:
 make T.init a value
Erratum: a _valid_ value
Jan 23