digitalmars.D - struct and default constructor
- deadalnix (26/26) Nov 27 2011 Hi,
- deadalnix (6/6) Nov 27 2011 In addition, here is a workaround :
- Andrej Mitrovic (2/2) Nov 27 2011 That's not a workaround, that ctor never gets called unless you pass
- deadalnix (4/6) Nov 27 2011 So if you @disable this(); you shouldn't get an error. Actually, you
- Steve Teale (2/4) Nov 28 2011 But it lets you pass a Range test.
- Andrej Mitrovic (3/5) Nov 27 2011 Wait nevermind, I thought you mean't the ctor *actually runs*. What
- =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= (21/27) Nov 27 2011 I believe your "workaround" will disappoint you, as it simply does not r...
- deadalnix (8/37) Nov 27 2011 So that is even worse that what I though. This struct situation is very
- dcrepid (22/24) Oct 09 2014 I know this is an old thread, but I've run into this same problem
- ketmar via Digitalmars-d (25/30) Oct 10 2014 that is what we have to pay for the rule "type must always has
- dcrepid (25/39) Oct 10 2014 What I mean with the scoped objects is that you still need to
- ketmar via Digitalmars-d (65/86) Oct 10 2014 only the syntax `scope auto a =3D new A();` is deprecated, not the whole
- dcrepid (38/75) Oct 10 2014 Great, thanks for the tips!
- ketmar via Digitalmars-d (38/62) Oct 10 2014 you're welcome. ;-)
- Nicolas Sicard (7/18) Oct 11 2014 It's not a "Voldemort type" if the struct A is static, is it? You
- ketmar via Digitalmars-d (5/24) Oct 11 2014 yes, my bad. i'm so used to write "static" to avoid the things that
- dcrepid (26/42) Oct 11 2014 Ah, I see. Lazy initialization is a nice approach to some things.
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (30/41) Oct 10 2014 You can use `static opCall` as a workaround. The following prints
- Dicebot (2/3) Oct 10 2014 Please don't. It is never worth the gain. Never.
- Walter Bright (20/22) Oct 10 2014 Having a .init instead of a default constructor has all kinds of useful ...
- Szymon Gatner (7/29) Oct 10 2014 Bit OT: What is The D code style then? It would be very useful
- Walter Bright (7/12) Oct 10 2014 You're right, but it's a bit of a difficult question to answer. Other ex...
- ketmar via Digitalmars-d (8/11) Oct 10 2014 second this. people also keep forgetting about default values and want
- Walter Bright (2/8) Oct 10 2014 That's a good observation.
- Simon A (18/45) Oct 10 2014 I've often thought that most of the hidden pain of arbitrary
- Walter Bright (3/6) Oct 10 2014 I suspect that CTFE can accomplish most of that today - with the excepti...
- Dicebot (3/10) Oct 10 2014 There is ER somewhere in bugzilla AFAIR about allowing CTFE-only
- Walter Bright (7/17) Oct 10 2014 Note that you can do (as pointed out upthread):
- Jonathan M Davis via Digitalmars-d (6/16) Oct 10 2014 The only reason that I can think of to have default constructors for fil...
- dcrepid (55/55) Oct 11 2014 Okay, I'm still kind of new to this language, so forgive me if I
- ketmar via Digitalmars-d (6/6) Oct 11 2014 On Sat, 11 Oct 2014 08:53:08 +0000
- dcrepid (3/7) Oct 11 2014 Yeah, thats one of the things I meant by "with special limits". =)
- monarch_dodra (13/23) Oct 10 2014 Think the argument is that declaring `T t;` must be CTFE, which
- Nick Treleaven (7/18) Oct 12 2014 Totally agree, and it doesn't help that some D proponents ignore this
Hi, I wonder why struct can't have a default constructor. TDPL state that it is required to allow every types to have a constant .init . That is true, however not suffiscient. A struct can has a void[constant] as a member and this doesn't have a .init . So this limitation does not ensure that the benefit claimed is garanteed. Additionnaly, if it is the only benefit, this is pretty thin compared to the drawback of not having a default constructor. We already have disable this(); for structs. This cause the struct to behave differently and cause the error « initializer required for type xxx » when a variable of type xxx is defined without initialization. This doesn't prevent the struct to get a constant .init and to use it (even if this .init is probably garbage in this case, the whole point of the disable is to force the user to use a constructor or to copy). The whole stuff seems very unclear and limitations arbitrary. Note that a static opCall and a disabel default constructor doesn't do the trick as opCall can't be used with new to allocate on the heap. This is only a cheap workaround. Note also that xxx myvar = xxx.init; worls and doesn't trigger postblit. So the disable this(); is also a structure thatd oesn't garantee anything. The point of classes and struct beeing different in D is slicing. Default constructor is unrelated to that problem. Why not allow both disable this(); and default constructor construct, both triggering the error « initializer required for type xxx » ? This would make much more sense. Postblit should also be triggered on assiagnation with .init to ensure the consistency of the whole construct.
Nov 27 2011
In addition, here is a workaround : // Wonderfull ! disable this(); // Default constructor workaround. this(int dummy = 0) { ... } But that look very dirty and it feels like working against the language.
Nov 27 2011
That's not a workaround, that ctor never gets called unless you pass an argument.
Nov 27 2011
Le 27/11/2011 21:39, Andrej Mitrovic a écrit :That's not a workaround, that ctor never gets called unless you pass an argument.So if you disable this(); you shouldn't get an error. Actually, you have an implicit constructor, even if it's only to set the variable as equal to .init property of the struct.
Nov 27 2011
On Sun, 27 Nov 2011 21:39:05 +0100, Andrej Mitrovic wrote:That's not a workaround, that ctor never gets called unless you pass an argument.But it lets you pass a Range test.
Nov 28 2011
Wait nevermind, I thought you mean't the ctor *actually runs*. What you meant was disable this() doesn't work with that ctor in place. On 11/27/11, Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:That's not a workaround, that ctor never gets called unless you pass an argument.
Nov 27 2011
On Sun, 27 Nov 2011 21:19:38 +0100, deadalnix <deadalnix gmail.com> wrote:In addition, here is a workaround : // Wonderfull ! disable this(); // Default constructor workaround. this(int dummy = 0) { ... } But that look very dirty and it feels like working against the language.I believe your "workaround" will disappoint you, as it simply does not run. import std.stdio; struct A { int value; disable this(); this(int dummy = 0) { value = 3; writeln("Hello from struct default constructor!"); // Never happens. } } void main() { A a = A(); writeln( a.value ); // Writes 0 } More importantly, this shows a bug in DMD - namely that structs with disabled default constructors can be created without a call to any constructor and with no error message. In fact, if one removes the constructor with dummy arguments, the program still compiles. http://d.puremagic.com/issues/show_bug.cgi?id=7021
Nov 27 2011
Le 27/11/2011 22:24, Simen Kjærås a écrit :On Sun, 27 Nov 2011 21:19:38 +0100, deadalnix <deadalnix gmail.com> wrote:So that is even worse that what I though. This struct situation is very messy and inconsistent. We should definie what we want for that and have a descent spec. I just found something new : if you disable this(this), then opAssign should be allowed (it is allowed, according to the website, when implicit cast doesn't exists). The copy don't work with opAssign and disabled postblit . . .In addition, here is a workaround : // Wonderfull ! disable this(); // Default constructor workaround. this(int dummy = 0) { ... } But that look very dirty and it feels like working against the language.I believe your "workaround" will disappoint you, as it simply does not run. import std.stdio; struct A { int value; disable this(); this(int dummy = 0) { value = 3; writeln("Hello from struct default constructor!"); // Never happens. } } void main() { A a = A(); writeln( a.value ); // Writes 0 } More importantly, this shows a bug in DMD - namely that structs with disabled default constructors can be created without a call to any constructor and with no error message. In fact, if one removes the constructor with dummy arguments, the program still compiles. http://d.puremagic.com/issues/show_bug.cgi?id=7021
Nov 27 2011
On Sunday, 27 November 2011 at 19:50:24 UTC, deadalnix wrote:Hi, I wonder why struct can't have a default constructor...I know this is an old thread, but I've run into this same problem recently and search yielded this result. I myself have tried working around the default-constructor problem with things like this(bool bInit = true) - which of course doesn't get invoked with MyStruct(), even with disable this. This seems like a language limitation to me. I have a few templated resource management objects that rely on RAII, but sometimes there's no real use for arguments, especially if a template defines what needs to be created. But now I'm forced to both disable the default constructor AND to require unnecessary parameters. Why? Another workaround is to use some object factory mechanism, but that's just extra code to maintain. Also, apparently doing cleanup in class object destructors is a big no-no as resources that it may need to clean up could potentially already be destroyed, depending on when the GC kicks in and what objects are cleared by it. So no possibility of using objects as resource acquire/release mechanisms. I assume using scoped with class objects will have a similar problem..
Oct 09 2014
On Fri, 10 Oct 2014 01:32:51 +0000 dcrepid via Digitalmars-d <digitalmars-d puremagic.com> wrote:This seems like a language limitation to me.that is what we have to pay for the rule "type must always has well-defined initial value". the thing is that struct must have initial value that can be calculated without executing any code. i.e. `T.init`. with default struct constructor we can't have such initial state anymore. this is highly unsafe. yet compiler ignores constructor with default args when creating struct. i.e. for this: struct A { disable this (); this (int v=3D42) { ... } ... } ... auto a =3D A(); compiler will not call this(42), but will generate error. i'm not sure if this must be changed though, 'cause `this (int)` now becames default constructor and we have no well-defined initial value anymore.So no possibility of using=20 objects as resource acquire/release mechanisms. I assume using scoped with class objects will have a similar=20 problem..no, stack-allocated object is GC root, so anything it holds reference to will not be destroyed until stack object is alive. so destructor of stack-allocated object *can* be used for doing cleanup. but you can use templates to build your "anchor" structs. it's hard to say without concrete code samples.
Oct 10 2014
On Friday, 10 October 2014 at 08:03:49 UTC, ketmar via Digitalmars-d wrote:On Fri, 10 Oct 2014 01:32:51 +0000 dcrepid via Digitalmars-d <digitalmars-d puremagic.com> wrote:What I mean with the scoped objects is that you still need to provide a constructor with parameters, yes? I will have to try this out myself, but I've avoided it since some people seem to indicate this is going to be deprecated.. Basically what I was doing code-wise was creating a 'FixedBuffer' structure, which would have its size passed as a template parameter. The constructor would do a malloc on it, and all the checks for bounds would only need to rely on that template parameter. Net result is that the cost of having a pretty safe bounds-checked buffer was just the size of one pointer. I *sorta* get the whole concept of being able to easily reason and work with structures in D, but why then does it have a destructor and a way to disable a default constructor, along with operator overloads and whatnot. It seems to me its trying to be object-like and POD-like at the same time which doesn't mesh well. As far as the 'scoped' object, I was referring to default construction - is it possible? Or do I need to use Walter's factory production workaround everytime I want something with deterministic create/destroy properties in this context? I would very readily accept using objects over structs if they had a guarantee of when they are destroyed, and weren't as risky to pass around as C pointers (i.e. possibility of them being null).So no possibility of using objects as resource acquire/release mechanisms. I assume using scoped with class objects will have a similar problem..no, stack-allocated object is GC root, so anything it holds reference to will not be destroyed until stack object is alive. so destructor of stack-allocated object *can* be used for doing cleanup. but you can use templates to build your "anchor" structs. it's hard to say without concrete code samples.
Oct 10 2014
On Fri, 10 Oct 2014 18:50:29 +0000 dcrepid via Digitalmars-d <digitalmars-d puremagic.com> wrote:What I mean with the scoped objects is that you still need to=20 provide a constructor with parameters, yes? I will have to try=20 this out myself, but I've avoided it since some people seem to=20 indicate this is going to be deprecated..only the syntax `scope auto a =3D new A();` is deprecated, not the whole concept. you just have to write it this now: import std.typecons : scoped; // yep, it's in a library now ... class A { this () { ... } ~this () { ... } } ... { // allocate class instance on the stack auto a =3D scoped!A(); ... } // here destructor for 'a' will be called just be careful and don't let 'a' escape the scope. ;-)Basically what I was doing code-wise was creating a 'FixedBuffer'=20 structure, which would have its size passed as a template=20 parameter. The constructor would do a malloc on it, and all the=20 checks for bounds would only need to rely on that template=20 parameter. Net result is that the cost of having a pretty safe=20 bounds-checked buffer was just the size of one pointer.it's better to use slices for that. you can create struct like this: struct A(size_t asize) { private ubyte* mData/* =3D null*/; // '=3Dnull' is the default private enum mSize =3D asize; disable this (this); ~this () { import core.stdc.stdlib; if (mData !is null) free(mData); } // and allocate memory for data in getter property auto data () { import core.stdc.stdlib; if (mData is null) { import core.exception : onOutOfMemoryError; mData =3D cast(typeof(mData))malloc(mSize); if (mData is null) onOutOfMemoryError(); } return mData[0..mSize]; } property size_t length () const safe pure nothrow nogc { return mSiz= e; } size_t opDollar () const safe pure nothrow nogc { return mSize; } ubyte opIndex (size_t ofs) trusted { import core.exception : RangeError; if (ofs >=3D mSize) throw new RangeError(); return data[ofs]; } ubyte opIndexAssign (ubyte v, size_t ofs) trusted { import core.exception : RangeError; if (ofs >=3D mSize) throw new RangeError(); return (data[ofs] =3D v); } } but then you will not be able to copy it (or you'll need additional field to keep "don't free" flag, and with it you can use slice instead, 'cause they both will not fit into the pointer). and you will not be able to pass it to functions anyway, this will not work: void doSomethingOnBuffer (ref A buf); only this: void doSomethingOnBuffer (ref A!1024 buf); i.e. you'll have to write the size in function args.I *sorta* get the whole concept of being able to easily reason=20 and work with structures in D, but why then does it have a=20 destructor and a way to disable a default constructor, along with=20 operator overloads and whatnot. It seems to me its trying to be=20 object-like and POD-like at the same time which doesn't mesh well.'cause there are two kinds of structs, actually. one kind is POD, and another is object-like, with methods, destructors and so on. this may be confusing a little. ;-) just don't mix 'em.As far as the 'scoped' object, I was referring to default=20 construction - is it possible?you can use classes, as i written above. just don't store such instance anywhere, 'cause it will be overwritten with another data after exiting the scope and all hell breaks loose.I would very readily accept using objects over structs if they=20 had a guarantee of when they are destroyed, and weren't as risky=20 to pass around as C pointers (i.e. possibility of them being=20 null).you can pass pointers to structs. pointers can be null. ;-)
Oct 10 2014
On Friday, 10 October 2014 at 20:25:16 UTC, ketmar via Digitalmars-d wrote:On Fri, 10 Oct 2014 18:50:29 +0000 dcrepid via Digitalmars-d <digitalmars-d puremagic.com> wrote:Great, thanks for the tips!What I mean with the scoped objects is that you still need to provide a constructor with parameters, yes? I will have to try this out myself, but I've avoided it since some people seem to indicate this is going to be deprecated..only the syntax `scope auto a = new A();` is deprecated, not the whole concept. you just have to write it this now: import std.typecons : scoped; // yep, it's in a library now ... class A { this () { ... } ~this () { ... } } ... { // allocate class instance on the stack auto a = scoped!A(); ... } // here destructor for 'a' will be called just be careful and don't let 'a' escape the scope. ;-)it's better to use slices for that. you can create struct like this: (snipped)Thanks for the effort of providing a proof of concept, even though I don't agree with what the data property should do. Shouldn't properties not mutate the data? But more than that I think its a waste to check whether the buffer is there every time you need to access it. Its better to allocate at the start, throw an exception if it can't be allocated, then provide access to it without any wasteful checks between. At least, this is the RAII type of idiom I'm after. How I am currently using it is using either .ptr, .data, or slices to create the necessary access or D slices. It works pretty well for what I'm using it for. I'm interfacing with the Windows API with the .ptr property, and then when I need to use it with D functions I typically use opSlice since the data is often variable lengths. I've pasted most of the struct I've been using here: http://dpaste.dzfl.pl/15381d5828f8 I would use it in say, a call to Windows' WinHttpReadData() using OutBuffer.ptr, then work with the received data (stored in dwDownloaded) using OutBuffer[0 .. dwDownloaded], for example. It works pretty nicely even if its not up to D's standards. About escaping scope - I'm aware this is possible, but the idea is that this is supposed to be used temporarily in a certain scope, then discarded (as the Scope prefix indicates).. there's better methods to doing it safely, for sure. But to do the same with only a single pointer? I think I like the idea of the factory method now though, as I've learned you can hide a struct inside a function, and then call the function to set the struct up properly and return it. At least, I'm sure I've seen code that does that..Haha.. I'll try not to (I think?)It seems to me its trying to be object-like and POD-like at the same time which doesn't mesh well.'cause there are two kinds of structs, actually. one kind is POD, and another is object-like, with methods, destructors and so on. this may be confusing a little. ;-) just don't mix 'em.I thought ref gives us the same guarantee that C++'s references do? Pointers are so.. 90's =P But yeah, the nullable object thing has bitten my ass a few times as I'm learning D, which really frustrates me =\ Thanks for your timeI would very readily accept using objects over structs if they had a guarantee of when they are destroyed, and weren't as risky to pass around as C pointers (i.e. possibility of them being null).you can pass pointers to structs. pointers can be null. ;-)
Oct 10 2014
On Fri, 10 Oct 2014 21:45:32 +0000 dcrepid via Digitalmars-d <digitalmars-d puremagic.com> wrote:Great, thanks for the tips!you're welcome. ;-)Thanks for the effort of providing a proof of concept, even=20 though I don't agree with what the data property should do.=20 Shouldn't properties not mutate the data?it depends. ;-) i think that doing such "lazy initialization" in property is acceptable. maybe not the best style, but acceptable. yet i may be wrong, of course.I think its a waste to check whether the buffer is there every time=20 you need to access it.this check costs almost nothing. and when you need buffer, most of the time you need it for some lengthy operation anyway, so checking cost can be ignored. and you can do `auto mypointer =3D a.data.ptr;` to avoid further checks. actually, it's meant to be used like this: auto d =3D a.data; // here we'll work with d, so no more checksIts better to allocate at the start, throw=20 an exception if it can't be allocated, then provide access to it=20 without any wasteful checks between. At least, this is the RAII=20 type of idiom I'm after.the main caveat with lazy initialization is that your program can fail at arbitrary place (where you'll first access .data), not when you declaring buffer this can be bad 'cause you may already execute some preparation code thinking that you have your buffer at hand.I've pasted most of the struct I've been using here: http://dpaste.dzfl.pl/15381d5828f8will take a closer look at it later, i'm sleepy now. ;-) btw, i'm used to call core.exception.onOutOfMemoryError when malloc() fails. We can't be sure that we still have memory to construct new Error object. sure, we'll lose linenumber info this way. it's not a rule of thumb, though (Phobos tend to throw new OutOfMemoryError instances, for example). but if we are working with memory on such low level...better methods to doing it safely, for sure. But to do the same=20 with only a single pointer?why do you insisting on having single pointer? sure you can use all sort of tricks to pack alot of data in single pointer, but i can't see any practical sense in it. today when smartphones have gigabytes of RAM, i'll trade some more pointers for ease of using and safety.I think I like the idea of the factory method now though, as I've=20 learned you can hide a struct inside a function, and then call=20 the function to set the struct up properly and return it. At=20 least, I'm sure I've seen code that does that..ah, yes, it's "Voldemort type". ;-) auto thatWhoCantBeNamed () { static struct A { ... } return A(); } voila. you have type that you can use but cannot create without factory. but you need to have postblit enabled with this.i've never used nullable myself. i'm just constantly forgetting about it (and about ~80% of Phobos for that matter ;-).you can pass pointers to structs. pointers can be null. ;-)I thought ref gives us the same guarantee that C++'s references=20 do? Pointers are so.. 90's =3DP But yeah, the nullable object=20 thing has bitten my ass a few times as I'm learning D, which=20 really frustrates me =3D\Thanks for your timeyour posts made me think about some things ;-), so thanks for your time too.
Oct 10 2014
On Friday, 10 October 2014 at 22:34:45 UTC, ketmar via Digitalmars-d wrote:It's not a "Voldemort type" if the struct A is static, is it? You can just alias A = typeof(thatWhoCantBeNamed()); and then create a new object of the same type. -- NicolasI think I like the idea of the factory method now though, as I've learned you can hide a struct inside a function, and then call the function to set the struct up properly and return it. At least, I'm sure I've seen code that does that..ah, yes, it's "Voldemort type". ;-) auto thatWhoCantBeNamed () { static struct A { ... } return A(); } voila. you have type that you can use but cannot create without factory. but you need to have postblit enabled with this.
Oct 11 2014
On Sat, 11 Oct 2014 08:41:15 +0000 Nicolas Sicard via Digitalmars-d <digitalmars-d puremagic.com> wrote:On Friday, 10 October 2014 at 22:34:45 UTC, ketmar via Digitalmars-d wrote:yes, my bad. i'm so used to write "static" to avoid the things that compiler can remove for me that my code is polluted with it. thank you.=20 It's not a "Voldemort type" if the struct A is static, is it? You can just alias A =3D typeof(thatWhoCantBeNamed()); and then create a new object of the same type.I think I like the idea of the factory method now though, as=20 I've learned you can hide a struct inside a function, and then=20 call the function to set the struct up properly and return it.=20 At least, I'm sure I've seen code that does that..ah, yes, it's "Voldemort type". ;-) auto thatWhoCantBeNamed () { static struct A { ... } return A(); } voila. you have type that you can use but cannot create without factory. but you need to have postblit enabled with this.
Oct 11 2014
On Friday, 10 October 2014 at 22:34:45 UTC, ketmar via Digitalmars-d wrote:On Fri, 10 Oct 2014 21:45:32 +0000Ah, I see. Lazy initialization is a nice approach to some things. But I'm not entirely sure it belongs in the structure or object itself.. however, if I were too I might put it in a 'length' or 'rezise' member function.. but that'd be better for a resizable object..Shouldn't properties not mutate the data?it depends. ;-) i think that doing such "lazy initialization" in property is acceptable. maybe not the best style, but acceptable. yet i may be wrong, of course.Actually, I think memory efficiency and speed are pretty key today, especially with embedded systems, server farms, and mobile devices. And it doesn't just have to do with available memory, but cache lines, register usage, etc. But that's another debate for another time. As far as the safety tradeoff though.. I prevent copying and default construction, so at least two problems should theoretically be solved. The possibility of using ranges or pointers outside of the function or scope is the only thing that would need to be controlled for. And there I suppose the GC or reference counting mechanisms would be the only other options. But hopefully if I document it right, and suggest the correct alternatives to use, I can prevent careless programmers from making those mistakes. (I think std.container.array.Array is a nice fallback)better methods to doing it safely, for sure. But to do the same with only a single pointer?why do you insisting on having single pointer? sure you can use all sort of tricks to pack alot of data in single pointer, but i can't see any practical sense in it. today when smartphones have gigabytes of RAM, i'll trade some more pointers for ease of using and safety.ah, yes, it's "Voldemort type". ;-)Ah, I had wondered why/where/what context I heard that under! Makes more sense now. =) Also, thanks for the other pointers (I didnt quote everything here)
Oct 11 2014
On Friday, 10 October 2014 at 01:32:54 UTC, dcrepid wrote:On Sunday, 27 November 2011 at 19:50:24 UTC, deadalnix wrote:You can use `static opCall` as a workaround. The following prints "S(0)" and "S(3)": struct S { int x; static S opCall() { S s; s.x = 3; return s; } } void main() { import std.stdio; S s; writeln(s); S t = S(); writeln(t); } But if you add a constructor with parameters, you get an error: struct S { int x; static S opCall() { ... } this(int y) { x = y; } } xx.d(4): Error: struct xx.S static opCall is hidden by constructors and can never be called xx.d(4): Please use a factory method instead, or replace all constructors with static opCall. IMO this is too restrictive, as obviously, the static opCall is _not_ hidden by the constructor.Hi, I wonder why struct can't have a default constructor...I know this is an old thread, but I've run into this same problem recently and search yielded this result. I myself have tried working around the default-constructor problem with things like this(bool bInit = true) - which of course doesn't get invoked with MyStruct(), even with disable this.
Oct 10 2014
On Friday, 10 October 2014 at 09:14:16 UTC, Marc Schütz wrote:You can use `static opCall` as a workaround.Please don't. It is never worth the gain. Never.
Oct 10 2014
On 11/27/2011 11:53 AM, deadalnix wrote:I wonder why struct can't have a default constructor. TDPL state that it is required to allow every types to have a constant .init .Having a .init instead of a default constructor has all kinds of useful properties: 1. the default object is trivially constructable and cannot fail 2. an easy way for other constructors to not have overlooked field initializers, so they get garbage initialized like in C++ 3. generic code can rely on the existence of trivial construction that cannot fail 4. dummy objects can be created, useful for "does this compile" semantics 5. an object can be "destroyed" by overwriting it with .init (overwriting with 0 is not the same thing) 6. when you see: T t; in code, you know it is trivially constructed and will succeed 7. objects can be created using TypeInfo Default constructors are baked into C++. I can't escape the impression that the desire for D default constructors comes from more or less trying to write C++ style code in D. I feel that non-trivial default construction is a bad idea, as are the various methods people try to get around the restriction. For non-trivial construction, one can easily just make a constructor with an argument, or call a factory method that returns a constructed object.
Oct 10 2014
On Friday, 10 October 2014 at 09:58:54 UTC, Walter Bright wrote:On 11/27/2011 11:53 AM, deadalnix wrote:Bit OT: What is The D code style then? It would be very useful for those coming from C++ to have a wiki/article on how to translate C++ idioms and practices to D. I too am writing D code like I do my C++ and often find myself puzzled (deterministic d-tors being perfect example). Missing default struct c-tor is also one of such examples - and adding opCall() feels hacky.I wonder why struct can't have a default constructor. TDPL state that it is required to allow every types to have a constant .init .Having a .init instead of a default constructor has all kinds of useful properties: 1. the default object is trivially constructable and cannot fail 2. an easy way for other constructors to not have overlooked field initializers, so they get garbage initialized like in C++ 3. generic code can rely on the existence of trivial construction that cannot fail 4. dummy objects can be created, useful for "does this compile" semantics 5. an object can be "destroyed" by overwriting it with .init (overwriting with 0 is not the same thing) 6. when you see: T t; in code, you know it is trivially constructed and will succeed 7. objects can be created using TypeInfo Default constructors are baked into C++. I can't escape the impression that the desire for D default constructors comes from more or less trying to write C++ style code in D.
Oct 10 2014
On 10/10/2014 3:23 AM, Szymon Gatner wrote:Bit OT: What is The D code style then? It would be very useful for those coming from C++ to have a wiki/article on how to translate C++ idioms and practices to D. I too am writing D code like I do my C++ and often find myself puzzled (deterministic d-tors being perfect example). Missing default struct c-tor is also one of such examples - and adding opCall() feels hacky.You're right, but it's a bit of a difficult question to answer. Other examples that cause people grief: 1. using ref as a type constructor 2. multiple inheritance 3. using a struct as both a value and a polymorphic type These are idiomatic C++ usages that need to be rethought for D.
Oct 10 2014
On Fri, 10 Oct 2014 02:58:39 -0700 Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:Default constructors are baked into C++. I can't escape the impression that the desire for D default constructors comes from more or less trying to write C++ style code in D.second this. people also keep forgetting about default values and want default constructors to simulate this: struct { int n =3D 42; string s =3D "default string"; }
Oct 10 2014
On 10/10/2014 3:25 AM, ketmar via Digitalmars-d wrote:people also keep forgetting about default values and want default constructors to simulate this: struct { int n = 42; string s = "default string"; }That's a good observation.
Oct 10 2014
On Friday, 10 October 2014 at 09:58:54 UTC, Walter Bright wrote:On 11/27/2011 11:53 AM, deadalnix wrote:I've often thought that most of the hidden pain of arbitrary constructors in C++ could be avoided if C++ could take advantage of functional purity. D has native functional purity. Couldn't you get the same benefits that you listed by allowing default constructors but requiring them to be pure? Of course, you can get the same outcome by initialising members using static pure functions or various other helpers, so you could say that pure default constructors are just syntactic sugar. Pure default constructors do have some advantages for more complex construction, though. For the sake of example, say I have a struct that uses a table of complex numbers, and for unrelated reasons the real and imaginary parts are stored in separate arrays. I.e., I have to initialise multiple members using one calculation. Adding pure default constructors to D would allow this to be implemented more cleanly and more intuitively.I wonder why struct can't have a default constructor. TDPL state that it is required to allow every types to have a constant .init .Having a .init instead of a default constructor has all kinds of useful properties: 1. the default object is trivially constructable and cannot fail 2. an easy way for other constructors to not have overlooked field initializers, so they get garbage initialized like in C++ 3. generic code can rely on the existence of trivial construction that cannot fail 4. dummy objects can be created, useful for "does this compile" semantics 5. an object can be "destroyed" by overwriting it with .init (overwriting with 0 is not the same thing) 6. when you see: T t; in code, you know it is trivially constructed and will succeed 7. objects can be created using TypeInfo Default constructors are baked into C++. I can't escape the impression that the desire for D default constructors comes from more or less trying to write C++ style code in D. I feel that non-trivial default construction is a bad idea, as are the various methods people try to get around the restriction. For non-trivial construction, one can easily just make a constructor with an argument, or call a factory method that returns a constructed object.
Oct 10 2014
On 10/10/2014 5:25 PM, Simon A wrote:D has native functional purity. Couldn't you get the same benefits that you listed by allowing default constructors but requiring them to be pure?I suspect that CTFE can accomplish most of that today - with the exception that CTFE will not allocate runtime memory for you.
Oct 10 2014
On Saturday, 11 October 2014 at 04:41:52 UTC, Walter Bright wrote:On 10/10/2014 5:25 PM, Simon A wrote:There is ER somewhere in bugzilla AFAIR about allowing CTFE-only struct default constructors.D has native functional purity. Couldn't you get the same benefits that you listed by allowing default constructors but requiring them to be pure?I suspect that CTFE can accomplish most of that today - with the exception that CTFE will not allocate runtime memory for you.
Oct 10 2014
On 10/10/2014 9:43 PM, Dicebot wrote:On Saturday, 11 October 2014 at 04:41:52 UTC, Walter Bright wrote:Note that you can do (as pointed out upthread): struct S { int x = 7; string s = "hello"; } which then has default initialization. Of course, CTFE will work on those rvalues.On 10/10/2014 5:25 PM, Simon A wrote:There is ER somewhere in bugzilla AFAIR about allowing CTFE-only struct default constructors.D has native functional purity. Couldn't you get the same benefits that you listed by allowing default constructors but requiring them to be pure?I suspect that CTFE can accomplish most of that today - with the exception that CTFE will not allocate runtime memory for you.
Oct 10 2014
On Friday, October 10, 2014 22:49:13 Walter Bright via Digitalmars-d wrote:On 10/10/2014 9:43 PM, Dicebot wrote:The only reason that I can think of to have default constructors for filling in the member variables during CTFE would be if you wanted to calculate some of the values based on other values, and while that might be nice upon occasion, I don't think that not having it is much of a loss. - Jonathan M DavisThere is ER somewhere in bugzilla AFAIR about allowing CTFE-only struct default constructors.Note that you can do (as pointed out upthread): struct S { int x = 7; string s = "hello"; } which then has default initialization. Of course, CTFE will work on those rvalues.
Oct 10 2014
Okay, I'm still kind of new to this language, so forgive me if I see things a little differently than long time users.. Here's what I see though. With Walter's description of what structures should be, it seems to me to be slightly at odds with what D structures are capable of, and in fact what they are being used for in a number of places ('fixing' GC, adding reference counting, providing RAII, etc). Structs are classically POD types, as we know from C. But with D you've added overloaded constructors, destructors, member functions etc. To me that more or less defines an object, albeit one with special limits. If you don't want to call them objects, thats fine. But the fact that they have customizable behavior in creation, copying, moving, and destruction means that they are much more than just a plain old data type with generic T.init states. They are a kind of living object, yea? And it seems to me that the default constructor is just the last component left to cement the structure as an object. It seems curious that you would allow the ability to disable default construction and copy construction, and allow hiding of constructors, etc.. but then determine that default constructors are going too far? Sure, having a guarantee that a struct will always be created without a fail state is a great concept. But why then haven't they just been kept as a collection of data types, and another 'scope object' been created instead? It seems what has happened is a FrankenStruct monster was created in the midst of trying to find some midway point between D objects and 'scope objects'.. I would myself love to use objects all the time, but I see these problems that currently only the FrankenStructs resolve: 1. Objects are basically pointers, and can be null pointers at that! (To me, that's C-levels of safety there) However: Structs are always created. 2. Without assigning something to an object, there's obviously no real initialization. However: Structs are initialized on creation. 3. Object Destructors are called 'sometime in the future' when GC runs. Cleaning up resources is discouraged as those resources might have been cleaned up by the GC? However: Struct destructors run when they are out of scope. 4. Objects always have at least one level of indirection (and a lot of extra data) Structs are there where you define them. (with no extra data) I may not be 100% on all of this, I'm still learning. But many times browsing the forums, reading the D books etc, I see the solutions to D object problems becoming this: wrap it with a struct. So whether its something like RefCounted, Scoped, NonNullable, UniqueArray etc.. the problems with D objects are fixed by relying on the guarantees provided by structures to solve them. I dunno, to me it all seems a bit discordant with what Walter seems to want them to be? (Sorry in advance if any of this comes off as mean or provoking.. I'm just sharing how I view the language design)
Oct 11 2014
On Sat, 11 Oct 2014 08:53:08 +0000 dcrepid via Digitalmars-d <digitalmars-d puremagic.com> wrote: structs also can't be inherited and can't have virtual methods (obviously, as they have no VMT). just a comment. ;-) ah, and don't forget another great aspect of structs: confusing C++ users! ;-)
Oct 11 2014
On Saturday, 11 October 2014 at 09:19:47 UTC, ketmar via Digitalmars-d wrote:On Sat, 11 Oct 2014 08:53:08 +0000 dcrepid via Digitalmars-d <digitalmars-d puremagic.com> wrote: structs also can't be inherited and can't have virtual methods (obviously, as they have no VMT). just a comment. ;-)Yeah, thats one of the things I meant by "with special limits". =)
Oct 11 2014
On Sunday, 27 November 2011 at 19:50:24 UTC, deadalnix wrote:Hi, I wonder why struct can't have a default constructor. TDPL state that it is required to allow every types to have a constant .init . That is true, however not suffiscient. A struct can has a void[constant] as a member and this doesn't have a .init . So this limitation does not ensure that the benefit claimed is garanteed. Additionnaly, if it is the only benefit, this is pretty thin compared to the drawback of not having a default constructor.Think the argument is that declaring `T t;` must be CTFE, which kind of implies a T.init state (which may have non-deterministic values in the presence of " = void"). This is mostly for declaring things static, and the whole "constructors are run after the .init blit". But even then: T t; //value is T.init, if not disable this() T t = T(); //Potentially run-time I've started threads and tried to start discussions about this before, but to no avail. It's a relativelly recurrent complain, especially from "newer" C++ users. The older D users have either of thrown in the towel, or implemented "workarounds".
Oct 10 2014
On 10/10/2014 22:19, monarch_dodra wrote:Think the argument is that declaring `T t;` must be CTFE, which kind of implies a T.init state (which may have non-deterministic values in the presence of " = void"). This is mostly for declaring things static, and the whole "constructors are run after the .init blit". But even then: T t; //value is T.init, if not disable this() T t = T(); //Potentially run-time I've started threads and tried to start discussions about this before, but to no avail. It's a relativelly recurrent complain, especially from "newer" C++ users.Totally agree, and it doesn't help that some D proponents ignore this point and just repeat "it doesn't work with the design of D". Whenever there is a weak explanation for something it will damage D, to some extent. In those cases it would be much better if a convincing argument can be written up as a website FAQ, or at least state that more research is needed before a solution can be tried.
Oct 12 2014