www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - ctor for stucts?

reply "Kris" <fu bar.com> writes:
I often use structs instead of classes, since one can instantiate them upon 
the stack. It's great that D structs are just like classes in many ways. 
However, I often need to initialize the struct; just as one often needs to 
initialize a class before using it.

I've taken to using opCall() to support such initialization, and I know that 
some folks have used static opCall() too. Unfortunately, the compiler does 
not generate an error where a user does not actually use the initializer. 
This can, obviously, lead to runtime failure.

What I'd like to see is a true ctor (or plural) for structs ~ if present, it 
*must* be used. This would eliminate potential for some avoidable bugs: e.g.

struct Foo
{
    this (int a, char b) {}
}

void main()
{
    Foo f;               // compile-time error!
    Foo f (1, 'v');    // good!
}

I'm not advocating a struct dtor <g> ... suspect that may be unecessary.

===================

Alternatively, the same effect could be realized by treating a static 
opCall() as a ctor instead:

struct Bar
{
    static opCall (char[] s) {}
}

void main()
{
    Bar b;                // compile-time error!
    Bar b ("doll");    // good!
}
Nov 22 2005
next sibling parent reply Hasan Aljudy <hasan.aljudy gmail.com> writes:
Kris wrote:
 I often use structs instead of classes, since one can instantiate them upon 
 the stack. It's great that D structs are just like classes in many ways. 
 However, I often need to initialize the struct; just as one often needs to 
 initialize a class before using it.
 
 I've taken to using opCall() to support such initialization, and I know that 
 some folks have used static opCall() too. Unfortunately, the compiler does 
 not generate an error where a user does not actually use the initializer. 
 This can, obviously, lead to runtime failure.
 
 What I'd like to see is a true ctor (or plural) for structs ~ if present, it 
 *must* be used. This would eliminate potential for some avoidable bugs: e.g.
 
 struct Foo
 {
     this (int a, char b) {}
 }
 
 void main()
 {
     Foo f;               // compile-time error!
     Foo f (1, 'v');    // good!
 }
 
 I'm not advocating a struct dtor <g> ... suspect that may be unecessary.
 
 ===================
 
 Alternatively, the same effect could be realized by treating a static 
 opCall() as a ctor instead:
 
 struct Bar
 {
     static opCall (char[] s) {}
 }
 
 void main()
 {
     Bar b;                // compile-time error!
     Bar b ("doll");    // good!
 }
 
 

I was under the impression that you can get the same effect by using an auto class with no dtor.
Nov 22 2005
next sibling parent reply "Kris" <fu bar.com> writes:
"Hasan Aljudy" <hasan.aljudy gmail.com> wrote ...
 I was under the impression that you can get the same effect by using an 
 auto class with no dtor.

Afraid not. That still involves the GC. ============== BTW I noticed a subtle difference in the use of auto: class Bar {~this(){}} void main() { auto b = new Bar; // dtor not invoked auto Bar c = new Bar; // dtor always invoked; implicit try/catch } Thought that was worth noting, cos' it really is subtle given the implications.
Nov 22 2005
parent reply Sean Kelly <sean f4.ca> writes:
Kris wrote:
 
 BTW I noticed a subtle difference in the use of auto:
 
 class Bar {~this(){}}
 
 void main()
 {
    auto b = new Bar;        // dtor not invoked
 
    auto Bar c = new Bar; // dtor always invoked; implicit try/catch
 }
 
 Thought that was worth noting, cos' it really is subtle given the 
 implications. 

This is a side-effect of the "new" meaning of auto in D. The old meaning which indicates stack-based behavior will eventually be deprecated and replaced with a more value-oriented declaration syntax: Bar b = Bar(); // on stack Bar c = new Bar(); // on heap In the meantime, I'm afraid we're going to have to live with the confusing double-meaning of "auto." Sean
Nov 22 2005
parent Hasan Aljudy <hasan.aljudy gmail.com> writes:
Sean Kelly wrote:
 Kris wrote:
 
 BTW I noticed a subtle difference in the use of auto:

 class Bar {~this(){}}

 void main()
 {
    auto b = new Bar;        // dtor not invoked

    auto Bar c = new Bar; // dtor always invoked; implicit try/catch
 }

 Thought that was worth noting, cos' it really is subtle given the 
 implications. 

This is a side-effect of the "new" meaning of auto in D. The old meaning which indicates stack-based behavior will eventually be deprecated and replaced with a more value-oriented declaration syntax: Bar b = Bar(); // on stack Bar c = new Bar(); // on heap

Who said it'll eventually be that? Plus, I'd rather let the compiler decide where to put my object, IMO having to decide (or being given the choice) between stack and heap object kinda defies the point of using references and enforcing "new" on objects. I don't know if it's just me or what, but one of the confusing points about c++ is having the option of putting an object on the stack or on the heap, and using different syntax for each one, etc. I love how all objects in D are references, like Java!! If D starts to expose this low-level stuff to the programmer and give him options .. well let's just say that I feel it kills the point.
 
 In the meantime, I'm afraid we're going to have to live with the 
 confusing double-meaning of "auto."
 
 
 Sean

Nov 22 2005
prev sibling parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Hasan Aljudy" <hasan.aljudy gmail.com> wrote in message 
news:dm00lq$2o0r$1 digitaldaemon.com...
 I was under the impression that you can get the same effect by using an 
 auto class with no dtor.

Then you run into the issue of the member arrangement. Sometimes you need the strict arrangement of a struct but still want to be able to use a ctor. An example would be with, say, a 3D vector that has x, y, and z components. These have to be kept in the correct order and need to be able to be passed to a graphics API which doesn't know about D (i.e. DirectX, OpenGL), but it would be nice to be able to write Vector v = Vector(5, 10, 15);
Nov 23 2005
prev sibling next sibling parent John Reimer <terminal.node gmail.com> writes:
Kris wrote:
 I often use structs instead of classes, since one can instantiate them upon 
 the stack. It's great that D structs are just like classes in many ways. 
 However, I often need to initialize the struct; just as one often needs to 
 initialize a class before using it.
 
 I've taken to using opCall() to support such initialization, and I know that 
 some folks have used static opCall() too. Unfortunately, the compiler does 
 not generate an error where a user does not actually use the initializer. 
 This can, obviously, lead to runtime failure.
 
 What I'd like to see is a true ctor (or plural) for structs ~ if present, it 
 *must* be used. This would eliminate potential for some avoidable bugs: e.g.
 
 struct Foo
 {
     this (int a, char b) {}
 }
 
 void main()
 {
     Foo f;               // compile-time error!
     Foo f (1, 'v');    // good!
 }
 
 I'm not advocating a struct dtor <g> ... suspect that may be unecessary.
 
 ===================
 
 Alternatively, the same effect could be realized by treating a static 
 opCall() as a ctor instead:
 
 struct Bar
 {
     static opCall (char[] s) {}
 }
 
 void main()
 {
     Bar b;                // compile-time error!
     Bar b ("doll");    // good!
 }
 
 

I don't have much to say, other than that I completely agree. Unfortunately, struct ctors have been oft brought up here. I really wish that they were implemented. -JJR
Nov 22 2005
prev sibling parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
Kris wrote:
 I often use structs instead of classes, since one can instantiate them upon 
 the stack. It's great that D structs are just like classes in many ways. 
 However, I often need to initialize the struct; just as one often needs to 
 initialize a class before using it.
 
 I've taken to using opCall() to support such initialization, and I know that 
 some folks have used static opCall() too. Unfortunately, the compiler does 
 not generate an error where a user does not actually use the initializer. 
 This can, obviously, lead to runtime failure.

I'm guessing that a struct invariant could solve part of this problem (though it would still be a runtime check). But as I try it (GDC 0.16), there seems to be a bug whereby struct invariants don't work at all.
 What I'd like to see is a true ctor (or plural) for structs ~ if present, it 
 *must* be used. This would eliminate potential for some avoidable bugs: e.g.

What would happen if you then try to use such a struct as a global variable or a member of a class or struct? Stewart. -- -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GCS/M d- s:- C++ a->--- UB P+ L E W++ N+++ o K- w++ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++>++++ h-- r-- !y ------END GEEK CODE BLOCK------ My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Nov 25 2005
next sibling parent Stewart Gordon <smjg_1998 yahoo.com> writes:
Stewart Gordon wrote:
 Kris wrote:

 I've taken to using opCall() to support such initialization, and I 
 know that some folks have used static opCall() too. Unfortunately, the 
 compiler does not generate an error where a user does not actually use 
 the initializer. This can, obviously, lead to runtime failure.

I'm guessing that a struct invariant could solve part of this problem (though it would still be a runtime check). But as I try it (GDC 0.16), there seems to be a bug whereby struct invariants don't work at all.

Correction: It seems that invariants are only called on entry or exit from a member function, not on setting a member manually. Stewart. -- -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GCS/M d- s:- C++ a->--- UB P+ L E W++ N+++ o K- w++ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++>++++ h-- r-- !y ------END GEEK CODE BLOCK------ My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Nov 25 2005
prev sibling parent reply kris <fu bar.org> writes:
Stewart Gordon wrote:
 Kris wrote:
 
 I often use structs instead of classes, since one can instantiate them 
 upon the stack. It's great that D structs are just like classes in 
 many ways. However, I often need to initialize the struct; just as one 
 often needs to initialize a class before using it.

 I've taken to using opCall() to support such initialization, and I 
 know that some folks have used static opCall() too. Unfortunately, the 
 compiler does not generate an error where a user does not actually use 
 the initializer. This can, obviously, lead to runtime failure.

I'm guessing that a struct invariant could solve part of this problem (though it would still be a runtime check). But as I try it (GDC 0.16), there seems to be a bug whereby struct invariants don't work at all.
 What I'd like to see is a true ctor (or plural) for structs ~ if 
 present, it *must* be used. This would eliminate potential for some 
 avoidable bugs: e.g.

<snip> What would happen if you then try to use such a struct as a global variable or a member of a class or struct? Stewart.

D doesn't currently support assignment at declaration for anything but constant data. When that changes, down the road, it would presumably support the condition you note? Alternatively, you might be indicating that the construction should be delayed? If so, then don't put a ctor in the struct. It would operate in the same two phase (init + setup) approach as it does today?
Nov 25 2005
parent reply Stewart Gordon <smjg_1998 yahoo.com> writes:
kris wrote:
<snip>
 What would happen if you then try to use such a struct as a global 
 variable or a member of a class or struct?

 Stewart.

D doesn't currently support assignment at declaration for anything but constant data. When that changes, down the road, it would presumably support the condition you note?

It allows you to assign anything, constant or not, when declaring a local variable. Just not in other circumstances. Would changing this really be easy to implement, and can it be done without leading to confusing or undefined behaviour when initialisers may depend on each other?
 Alternatively, you might be indicating that the construction should be 
 delayed? If so, then don't put a ctor in the struct. It would operate in 
 the same two phase (init + setup) approach as it does today?

So (for now at least) if you put a constructor in a struct, it'll become illegal to declare one except at function level? Stewart. -- -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GCS/M d- s:- C++ a->--- UB P+ L E W++ N+++ o K- w++ O? M V? PS- PE- Y? PGP- t- 5? X? R b DI? D G e++>++++ h-- r-- !y ------END GEEK CODE BLOCK------ My e-mail is valid but not my primary mailbox. Please keep replies on the 'group where everyone may benefit.
Nov 25 2005
parent kris <fu bar.org> writes:
Stewart Gordon wrote:
 kris wrote:
 <snip>
 
 What would happen if you then try to use such a struct as a global 
 variable or a member of a class or struct?

 Stewart.

D doesn't currently support assignment at declaration for anything but constant data. When that changes, down the road, it would presumably support the condition you note?

It allows you to assign anything, constant or not, when declaring a local variable. Just not in other circumstances. Would changing this really be easy to implement, and can it be done without leading to confusing or undefined behaviour when initialisers may depend on each other?
 Alternatively, you might be indicating that the construction should be 
 delayed? If so, then don't put a ctor in the struct. It would operate 
 in the same two phase (init + setup) approach as it does today?

So (for now at least) if you put a constructor in a struct, it'll become illegal to declare one except at function level?

You might not be able to use the suggested ctor at, for example, global scope, if you don't already have the arguments to pass to it. Conceptually, the syntax might be something like this: struct Foo { this (int a, char b) {} } Foo foo(1, 'd'); From purely a dependency standpoint, this is no different that class construction. Do you have another approach? The problem is ensuring the struct is correctly initialised, and only when that is needed, using a convenient syntax. The invarient thing, whilst indeed useful, does not really address the latter. ============== To illustrate the current situation: struct Foo { void ctor(int a, char b) {} } Foo foo; foo.ctor (1, 'd');
Nov 25 2005