digitalmars.D - Default struct constructor
- Victor Porton (14/14) Jan 30 2019 Why the language specification disables default constructors for
- Stefan Koch (6/20) Jan 30 2019 the reason is that a struct should have a static initialization
- Jonathan M Davis (32/46) Jan 31 2019 One of the core concepts in D is that all types have a default value whi...
- bitwise (12/17) Feb 06 2019 If I can do this:
- Jonathan M Davis (14/31) Feb 06 2019 If that's what you want, then you can just use a static opCall. e.g.
- bitwise (11/23) Feb 07 2019 Sorry - my example was pretty bad.
- Jonathan M Davis (51/81) Feb 08 2019 So, basically, you want default constructors but are suggesting that the...
- psycha0s (4/4) Feb 08 2019 What kind of problems a default struct constructor could solve?
- Victor Porton (5/9) Feb 08 2019 In my particular case I need RAII initializing a C library
- psycha0s (6/10) Feb 08 2019 I see. Yeah, I remember I encountered with the need to have a
- Timon Gehr (3/5) Feb 12 2019 Missing by-reference passing of rvalues is a nuisance, but it has
- Dru (1/3) Feb 15 2019 Yes please
- Paul (6/16) Jan 27 2021 I'd also like to see the possibility for overriding the default
- =?UTF-8?Q?Ali_=c3=87ehreli?= (31/36) Jan 27 2021 Same here: Everytime I thought I needed a struct destructor I solved in
- Paul (13/25) Jan 27 2021 Does this mean opCall takes priority over default constructors
- =?UTF-8?Q?Ali_=c3=87ehreli?= (32/40) Jan 27 2021 Assuming static opCall is defined, there are two syntaxes:
- Cym13 (3/12) Feb 10 2019 You might like this article
- bitwise (12/21) Feb 12 2019 I've boiled my requirement down to structs having a "lifetime
Why the language specification disables default constructors for structs? It can be easily worked around: --- struct Dummy { } struct C { this(Dummy dummy) { } } --- But this workaround (using Dummy) is silly. Isn't it better to allow default constructor and call it for both of the following? C x; C y = C();
Jan 30 2019
On Wednesday, 30 January 2019 at 12:29:46 UTC, Victor Porton wrote:Why the language specification disables default constructors for structs? It can be easily worked around: --- struct Dummy { } struct C { this(Dummy dummy) { } } --- But this workaround (using Dummy) is silly. Isn't it better to allow default constructor and call it for both of the following? C x; C y = C();the reason is that a struct should have a static initialization state. In Practice this does not always work, but it is a nice enough conecpt.
Jan 30 2019
On Wednesday, January 30, 2019 5:29:46 AM MST Victor Porton via Digitalmars- d wrote:Why the language specification disables default constructors for structs? It can be easily worked around: --- struct Dummy { } struct C { this(Dummy dummy) { } } --- But this workaround (using Dummy) is silly. Isn't it better to allow default constructor and call it for both of the following? C x; C y = C();One of the core concepts in D is that all types have a default value which is known at compile time, and a number of places in the language rely on that fact (e.g. the way that arrays are initialized depends on blitting the init value of the type into the array). Because structs live directly wherever they're placed rather than requiring references like classes do, default construction for structs wouldn't play well with the semantics of the language. It wouldn't necessarily be impossible to have it, but the language was designed around default initialization and not default construction, and adding default construction into the mix would likely cause some confusing corner cases and generally not work very well. The fact that D has default initialization is extremely useful (e.g. it makes it impossible to have issues like variables being initialized with garbage unless you specifically tell the compiler to do it with something like initializing with = void;), but the fact that you can't have default constructors for structs is at times quite annoying. So, it comes at a cost, but the tradeoff is arguably well worth it. In general, it's advised to simply not declar structs that require that they be default constructed rather than default initialized, but you can declare disable this(); to disable default construction and then use a factory method to construct the type instead of using a constructor directly. However, this does have the downside that you can't use the struct anywhere that default initialization is required (e.g. in an array), and an init value still exists for the type and can be assigned to it (which functions like std.algarothim's move will do). So, if the type can't handle being assigned its init value, you may still have problems in some cases. As such, disabling default initialization really isn't something that should be done unless you really need to, but like = void;, the language does provide it for cases where it really is needed, and the programmer is careful. - Jonathan M Davis
Jan 31 2019
On Thursday, 31 January 2019 at 13:57:41 UTC, Jonathan M Davis wrote:It wouldn't necessarily be impossible to have it, but the language was designed around default initialization and not default construction, and adding default construction into the mix would likely cause some confusing corner cases and generally not work very well.If I can do this: struct A { int n; } A a = void; Maybe I should be able to do this too: struct S { this(void) { ... } // <--- make more explicit by adding void } S s = S();
Feb 06 2019
On Wednesday, February 6, 2019 7:49:28 AM MST bitwise via Digitalmars-d wrote:On Thursday, 31 January 2019 at 13:57:41 UTC, Jonathan M Davis wrote:If that's what you want, then you can just use a static opCall. e.g. struct S { static S opCall() { ... } } auto s = S(); The only real restriction is that you can't declare any constructors if you have any static opCall functions declared. - Jonathan M DavisIt wouldn't necessarily be impossible to have it, but the language was designed around default initialization and not default construction, and adding default construction into the mix would likely cause some confusing corner cases and generally not work very well.If I can do this: struct A { int n; } A a = void; Maybe I should be able to do this too: struct S { this(void) { ... } // <--- make more explicit by adding void } S s = S();
Feb 06 2019
On Wednesday, 6 February 2019 at 23:17:31 UTC, Jonathan M Davis wrote:On Wednesday, February 6, 2019 7:49:28 AM MST bitwise via Digitalmars-d wrote:Sorry - my example was pretty bad. I'm suggesting that this(void) be a dynamic alternative to T.init. Current: struct S { int n; } S s = void; s.n = 1; Suggested: struct S { int n; this(void){ n = 1; } } S s; // this(void) invoked here and T.init not used[...]If that's what you want, then you can just use a static opCall. e.g. struct S { static S opCall() { ... } } auto s = S(); The only real restriction is that you can't declare any constructors if you have any static opCall functions declared. - Jonathan M Davis
Feb 07 2019
On Thursday, February 7, 2019 8:02:09 AM MST bitwise via Digitalmars-d wrote:On Wednesday, 6 February 2019 at 23:17:31 UTC, Jonathan M Davis wrote:So, basically, you want default constructors but are suggesting that there be an explicit void parameter in the declaration to indicate that that's what you're doing rather than just having it not have any parameters. Well, that runs into all of the problems related to the fact that D was designed around the idea that everything is default-initialized. It's not an impossible hurdle, but it would be a significant shift in how the language works, and it would mean that stuff like how arrays are initialized would change drastically depending on the types involved, whereas right now, you need default initialization, or you simply can't use the type in an array, and the way that arrays are initialized is the same for all types. It also would be a problem from the standpoint of code clarity. With something like S s = void; you can clearly see that the programmer has explicitly requested that the variable not be properly initialized, whereas with your suggested default constructors, suddenly you would no longer know what S s; did unless you looked at the type. In the vast majority of cases, it would be default-iniatialized, but occasionally, it would be default constructed, and that could cause quite a bit of confusion. It also still doesn't get around the fact that _all_ types in D have an init value, and someone could use it (e.g. std.algorithm's move function uses it to give the old variable a valid value rather than garbage after the move). Even types which have disabled default initialization have init values. And it would be very disruptive to the language to try to change that if it's really possible it all (e.g. objects are always initialized to their init value before any constructors are run, so the way that constructors work in D relies on there being an init value for the type). So, with how D is designed, you really can't rely on a constructor having been run for a struct object. It can be made much harder to not have called a constructor (e.g. by using disable this();), but as long as the init value exists, there's a way around the constructor. So, fundamentally, I really don't see how default constructors for structs can fully work in D. At best, it could be made to work in the common cases and even that's only possible by complicating how a number of core things work so that they do different things depending on whether a struct is default constructed or default initialized, which would definitely complicate the language. Honestly, I really think that trying to make default constructors work in D is a mistake. Default initialization and init are just too baked into the language. Even trying to create a type with disable this(); is pretty questionable. It's useful in rare situations, but it has to be handled with care - particularly since the type's init value still exists, and some code will use it. It's true that there are use cases where the lack of a default constructor for structs is annoying, but in general, all you have to do is use a factory method instead and then make sure that the type still works if its init value is used (e.g. make sure that the destructor is aware that the factory method can be bypassed). And if you absolutely must have default construction, you can always use a class instead. - Jonathan M DavisOn Wednesday, February 6, 2019 7:49:28 AM MST bitwise via Digitalmars-d wrote:Sorry - my example was pretty bad. I'm suggesting that this(void) be a dynamic alternative to T.init. Current: struct S { int n; } S s = void; s.n = 1; Suggested: struct S { int n; this(void){ n = 1; } } S s; // this(void) invoked here and T.init not used[...]If that's what you want, then you can just use a static opCall. e.g. struct S { static S opCall() { ... } } auto s = S(); The only real restriction is that you can't declare any constructors if you have any static opCall functions declared. - Jonathan M Davis
Feb 08 2019
On Friday, 8 February 2019 at 08:25:18 UTC, Jonathan M Davis wrote:[...] So, basically, you want default constructors but are suggesting that there be an explicit void parameter in the declaration to indicate that that's what you're doing rather than just having it not have any parameters.Yeah, that *was* the general idea.Well, that runs into all of the problems related to the fact that D was designed around the idea that everything is default-initialized. It's not an impossible hurdle, but it would be a significant shift in how the language works, and it would mean that stuff like how arrays are initialized would change drastically depending on the types involved whereas right now, you need default initialization, or you simply can't use the type in an array, and the way that arrays are initialized is the same for all types. It also would be a problem from the standpoint of code clarity. With something like S s = void; you can clearly see that the programmer has explicitly requested that the variable not be properly initialized, whereas with your suggested default constructors, suddenly you would no longer know what S s; did unless you looked at the type. In the vast majority of cases, it would be default-iniatialized, but occasionally, it would be default constructed, and that could cause quite a bit of confusion. It also still doesn't get around the fact that _all_ types in D have an init value, and someone could use it (e.g. std.algorithm's move function uses it to give the old variable a valid value rather than garbage after the move). Even types which have disabled default initialization have init values. And it would be very disruptive to the language to try to change that if it's really possible at all (e.g. objects are always initialized to their init value before any constructors are run, so the way that constructors work in D relies on there being an init value for the type). So, with how D is designed, you really can't rely on a constructor having been run for a struct object. It can be made much harder to not have called a constructor (e.g. by using disable this();), but as long as the init value exists, there's a way around the constructor.This is a good point, but I think it's a workable problem. I actually wouldn't be opposed to having T.init blitted over my struct before my dynamic constructor was called. My concern here is more functional than performance-based. As long as my struct had the correct values by the beginning of it's lifetime, that would be fine. So maybe this(auto) could work ;)So, fundamentally, I really don't see how default constructors for structs can fully work in D. At best, it could be made to work in the common cases and even that's only possible by complicating how a number of core things work so that they do different things depending on whether a struct is default constructed or default initialized, which would definitely complicate the language.My (revised) concern is only that my struct have the opportunity to change it's values right before the start of it's lifetime, but without adding boiler-plate code all over the place. Essentially, I guess I'm asking for a post-init constructor, similar to how a post-blit would work. It has also been very annoying not to be able to have dynamic class/struct field initializers, and this would also be fixed by some kind of post-init constructor method.It's useful in rare situations, but it has to be handled with care - particularly since the type's init value still exists, and some code will use it.I have to strongly disagree with this. I haven't touched D in nearly a year because of these types of problems. I'm still waiting to see how the memory/resource situation shapes up with the addition of scope and possible RC classes.It's true that there are use cases where the lack of a default constructor for structs is annoying, but in general, all you have to do is use a factory method instead and then make sure that the type still works if its init value is used (e.g. make sure that the destructor is aware that the factory method can be bypassed). And if you absolutely must have default construction, you can always use a class instead.I think my comment above about wanting structs to have the appropriate values _before_ their lifetime starts applies here too. A factory method requires explicit runtime invocation and is no different than a static opCall, unary dummy constructor, or having to add initialization boilerplate. Thanks for the detailed response, Bit
Feb 08 2019
On Friday, 8 February 2019 at 08:25:18 UTC, Jonathan M Davis wrote:It's true that there are use cases where the lack of a default constructor for structs is annoying, but in general, all you have to do is use a factory method instead and then make sure that the type still works if its init value is used (e.g. make sure that the destructor is aware that the factory method can be bypassed). And if you absolutely must have default construction, you can always use a class instead. - Jonathan M DavisJust found if you need a type that can be parameterized by an alias to a function that accesses some local frame data, and you want the factory to be static on the type, you can't do it. Seems like a bug maybe? as I'm not sure what the difference is between the static construct function below or a global one that is parameterized on "alias fun" ? struct S(alias fun) { static auto construct() { auto a = S!fun(); return a; } void call() { fun(); } } void main() { int count; auto s = S!(() => count++).construct; } onlineapp.d(5): Error: cannot access frame pointer of onlineapp.main.S!(delegate () => count++).S
Feb 11 2019
What kind of problems a default struct constructor could solve? If you want to use RAII, you need to pass the resource as an argument. If you want to set some initial state, you can do it by assigning values to the struct members directly.
Feb 08 2019
On Friday, 8 February 2019 at 19:20:59 UTC, psycha0s wrote:What kind of problems a default struct constructor could solve? If you want to use RAII, you need to pass the resource as an argument. If you want to set some initial state, you can do it by assigning values to the struct members directly.In my particular case I need RAII initializing a C library object. Some of the C constructor functions don't have arguments. So the only way to do it is my silly hack with Dummy object. I would probably vote for a change in D spec.
Feb 08 2019
In my particular case I need RAII initializing a C library object. Some of the C constructor functions don't have arguments. So the only way to do it is my silly hack with Dummy object. I would probably vote for a change in D spec.I see. Yeah, I remember I encountered with the need to have a default struct constructor myself earlier, but I couldn't remember the reason. Some of the current D "features" are very inconvenient. Especially the inability to pass an rvalue by a const reference and the forbidden default constructors for structs.
Feb 08 2019
On 08.02.19 20:53, psycha0s wrote:Some of the current D "features" are very inconvenient. [...] inability to pass an rvalue by a const referenceMissing by-reference passing of rvalues is a nuisance, but it has nothing to do with const.
Feb 12 2019
pass an rvalue by a const reference and the forbidden default constructors for structs.Yes please
Feb 15 2019
On Friday, 8 February 2019 at 19:53:16 UTC, psycha0s wrote:I'd also like to see the possibility for overriding the default struct constructor. I wouldn't say it's absolutely necessary in my case, as I _could_ use a class instead, however I believe a struct is more proper in my scenario.In my particular case I need RAII initializing a C library object. Some of the C constructor functions don't have arguments. So the only way to do it is my silly hack with Dummy object. I would probably vote for a change in D spec.I see. Yeah, I remember I encountered with the need to have a default struct constructor myself earlier, but I couldn't remember the reason. Some of the current D "features" are very inconvenient. Especially the inability to pass an rvalue by a const reference and the forbidden default constructors for structs.
Jan 27 2021
On 1/27/21 7:00 AM, Paul wrote:I'd also like to see the possibility for overriding the default struct constructor.I don't have that feeling anymore. :)I wouldn't say it's absolutely necessary in my caseSame here: Everytime I thought I needed a struct destructor I solved in one way or another and never thought about it anymore.I _could_ use a class insteadClasses are so costly though: At least 2 pointers added; 16 bytes on a 64 bit system. And class variables are references. I would use something like the following and be very happy with it: struct S { int i; static S opCall() { S s; import std.random : uniform; s.i = uniform(1, 42); return s; } static S defaulted() { return S(); } } void main() { auto a = S(); assert(a.i != 0); auto b = S.defaulted; assert(b.i != 0); } Although, I've heard static opCall may have its own set of problems, which I haven't encountered yet; other than making the mistake of writing 'auto s = S();' as its first line, which causes a segmentation fault due to infinite recursion. ;)however I believe a struct is more proper in my scenario.structs boulder! :) Ali
Jan 27 2021
On Wednesday, 27 January 2021 at 18:00:02 UTC, Ali Çehreli wrote:struct S { int i; static S opCall() { S s; import std.random : uniform; s.i = uniform(1, 42); return s; } static S defaulted() { return S(); } }Does this mean opCall takes priority over default constructors (or struct literals, I have no clue what the difference is, semanthically or implementation wise), whilst it does not when arguments are included? (As https://dlang.org/spec/operatoroverloading.html states selfdefined constructors take priority) Thanks for the suggestion by the way, it seems Jonathan M Davis although I hadn't noticed. This seems like a simple solution By the way, doesnt this mean a this(){} syntax could be implemented by treating it as syntactic sugar for opCall? I noticed C++ also has non-argument struct constructors, so its a bit curious to me.
Jan 27 2021
On 1/27/21 2:05 PM, Paul wrote:Does this mean opCall takes priority over default constructors (or struct literalsAssuming static opCall is defined, there are two syntaxes: S a; // Equals S.init; no constuctor run S b = S(); // static opCall; no constuctor run foo(S()); // static opCall foo(S.init); // S.initI have no clue what the difference is, semanthically or implementation wise), whilst it does not when arguments are included?I don't know; let's try. :) import std.stdio; struct S { this(int) { writeln("ctor"); } static opCall(int) { writeln("opCall"); S s; return s; } } void foo(S) { } void main() { auto a = S(1); foo(S(2)); } Both of the calls in main go to this(int); opCall is ignored.By the way, doesnt this mean a this(){} syntax could be implemented by treating it as syntactic sugar for opCall?Being able to define a default constructor and then saying we don't have default constructors would be confusing I think.I noticed C++ also has non-argument struct constructors, so its a bit curious to me.Those are called default constructors. D thinks it differently: there are no default constructors for strtucts. opCall is a strange thing where the name of the type is callable and it looks like a default constructor in usage. Ali
Jan 27 2021
On Friday, 8 February 2019 at 19:24:00 UTC, Victor Porton wrote:On Friday, 8 February 2019 at 19:20:59 UTC, psycha0s wrote:You might like this article https://w0rp.com/blog/post/an-raii-constructor-by-another-name-is-just-as-sweet/What kind of problems a default struct constructor could solve? If you want to use RAII, you need to pass the resource as an argument. If you want to set some initial state, you can do it by assigning values to the struct members directly.In my particular case I need RAII initializing a C library object. Some of the C constructor functions don't have arguments. So the only way to do it is my silly hack with Dummy object. I would probably vote for a change in D spec.
Feb 10 2019
On Friday, 8 February 2019 at 19:24:00 UTC, Victor Porton wrote:On Friday, 8 February 2019 at 19:20:59 UTC, psycha0s wrote:I've boiled my requirement down to structs having a "lifetime starting" method of some kind. I don't mind if T.init is blitted over my object before it's called, as long as I can initialize any struct fields that couldn't be initialized inline due to their value not being CTFE'able. I suppose that for this to be worth doing though, it would need to work extern(D) as well. So creating the object in D would blit T.init to the struct first, while creating the same object in C would not. So I'm wondering if this difference would be a deal breaker (I don't believe it should be), or if what I'm suggesting creates other technical problems (I can't think of any).What kind of problems a default struct constructor could solve? If you want to use RAII, you need to pass the resource as an argument. If you want to set some initial state, you can do it by assigning values to the struct members directly.In my particular case I need RAII initializing a C library object. Some of the C constructor functions don't have arguments. So the only way to do it is my silly hack with Dummy object. I would probably vote for a change in D spec.
Feb 12 2019