www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - struct and default constructor

reply deadalnix <deadalnix gmail.com> writes:
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
next sibling parent reply deadalnix <deadalnix gmail.com> writes:
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
next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
That's not a workaround, that ctor never gets called unless you pass
an argument.
Nov 27 2011
next sibling parent deadalnix <deadalnix gmail.com> writes:
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
prev sibling parent Steve Teale <steve.teale britseyeview.com> writes:
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
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
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
prev sibling parent reply =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= <simen.kjaras gmail.com> writes:
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
parent deadalnix <deadalnix gmail.com> writes:
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:

 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
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 . . .
Nov 27 2011
prev sibling next sibling parent reply "dcrepid" <dcrepid none.com> writes:
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
next sibling parent reply ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
parent reply "dcrepid" <dcrepid none.com> writes:
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:

 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.
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).
Oct 10 2014
parent reply ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
parent reply "dcrepid" <dcrepid none.com> writes:
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:

 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. ;-)
Great, thanks for the tips!
 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..
  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.
Haha.. I'll try not to (I think?)
 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).
you can pass pointers to structs. pointers can be null. ;-)
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 time
Oct 10 2014
parent reply ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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 checks
 Its 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/15381d5828f8
will 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.
 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\
i've never used nullable myself. i'm just constantly forgetting about it (and about ~80% of Phobos for that matter ;-).
 Thanks for your time
your posts made me think about some things ;-), so thanks for your time too.
Oct 10 2014
next sibling parent reply "Nicolas Sicard" <dransic gmail.com> writes:
On Friday, 10 October 2014 at 22:34:45 UTC, ketmar via
Digitalmars-d wrote:
 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..
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.
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. -- Nicolas
Oct 11 2014
parent ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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:
 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.
=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.
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.
Oct 11 2014
prev sibling parent "dcrepid" <dcrepid none.com> writes:
On Friday, 10 October 2014 at 22:34:45 UTC, ketmar via 
Digitalmars-d wrote:
 On Fri, 10 Oct 2014 21:45:32 +0000
 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.
Ah, 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..
 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.
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)
 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
prev sibling parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Friday, 10 October 2014 at 01:32:54 UTC, dcrepid wrote:
 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.
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.
Oct 10 2014
parent "Dicebot" <public dicebot.lv> writes:
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
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
next sibling parent reply "Szymon Gatner" <noemail gmail.com> writes:
On Friday, 10 October 2014 at 09:58:54 UTC, Walter Bright wrote:
 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.
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.
Oct 10 2014
parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling next sibling parent reply ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
parent Walter Bright <newshound2 digitalmars.com> writes:
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
prev sibling next sibling parent reply "Simon A" <simon_a example.com> writes:
On Friday, 10 October 2014 at 09:58:54 UTC, Walter Bright wrote:
 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.
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.
Oct 10 2014
parent reply Walter Bright <newshound2 digitalmars.com> writes:
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
parent reply "Dicebot" <public dicebot.lv> writes:
On Saturday, 11 October 2014 at 04:41:52 UTC, Walter Bright wrote:
 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.
There is ER somewhere in bugzilla AFAIR about allowing CTFE-only struct default constructors.
Oct 10 2014
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 10/10/2014 9:43 PM, Dicebot wrote:
 On Saturday, 11 October 2014 at 04:41:52 UTC, Walter Bright wrote:
 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.
There 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
parent Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Friday, October 10, 2014 22:49:13 Walter Bright via Digitalmars-d wrote:
 On 10/10/2014 9:43 PM, Dicebot wrote:
 There 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.
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 Davis
Oct 10 2014
prev sibling parent reply "dcrepid" <dcrepid none.com> writes:
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
parent reply ketmar via Digitalmars-d <digitalmars-d puremagic.com> writes:
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
parent "dcrepid" <dcrepid none.com> writes:
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
prev sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
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
parent Nick Treleaven <ntrel-pub mybtinternet.com> writes:
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