View mode: basic / threaded / horizontal-split · Log in · Help
July 25, 2012
Struct no-arg constructor?
According to TDPL, the rationale for structs not having default 
constructors is "T.init", were T.init is defined as:
*A static, known at compile time, mem-copyable value.
*The value that gets mem-copied into structs before the 
constructors are called
*The value that gets mem-copied into structs when moving stuff 
out of them.
**Destructors must support being called on a T.init element.

The thing is, I can't, for the life of me, understand how that 
interferes with having a default constructor. Wouldn't keeping 
the current definition of init, but allowing a call to a "no-arg" 
constructor after after the mem-copy of T.init also work? As long 
as the destructor keeps supporting calls to T.init objects, then 
all is fine. Why hold out on a very useful functionality?

Am I missing a case where having a default constructor could 
actually mess things up? The restriction just feels gratuitous 
for me.

----------------------------------------------------------------
----------------------------------------------------------------

This has been bringing me problems recently, with structs that 
need to allocate data, but don't really require any meaningful 
arguments to passed to them at construction. Array is a perfect 
example of the kind of struct I'm talking about. To force 
initialized them, I have the choice of:
*Forcing a call to an "initialize" manually: No way of 
enforcement at client side.
*Putting a call to "initialize" inside EACH AND EVERY function. 
Inefficient and error prone.
*Enforce the use of a factory.
Not only is all the above far from perfect, it really pushes me 
to use a "two pass" initialization scheme (we all know how 
horrible those are), but what makes it the worse is that it feels 
the language could handle this just fine.

The tipping point for me was investigating a bug with Array, 
which led me into RefCounted:

----
alias RefCounted!(int, RefCountedAutoInitialize.yes) RCIAI;
void main() {
    alias RefCounted!(int, RefCountedAutoInitialize.yes) RCIAI;
    RCIAI a;
    a = 5
}
----

You know what this does? An access violation. Do you know why? 
Because at this point, a, of type "autoAnitialize.yes" ISN'T 
initialized! WTF, right? This is actually a bug inside opAssign, 
which *should* enforce initialization (when the static type is 
autoInitialize.yes, of course). A bug fix I will be pushing soon.

But this is just a partial fix: The truth is that the next 
snippet will plain and simply NEVER work. EVER. Never Ever. No 
matter what we do.

----
RCIAI a;
RCIAI b = a;
b = 5;
writeln(a);
----

While one might think that a should hold the value "5", NOTHING 
in this snippet even has the potential of touching a, until we 
get to the last line, and call the "alias refCountedPayload 
this;" function (inside writeln). At this point, a has forgotten 
all about b.

You might be thinking: Cute, but that works as expected, a is a 
reference counted object. Maybe.

But the same behavior will happens to ALL structs. Would this be 
the expected behavior for Array?
July 25, 2012
Re: Struct no-arg constructor?
On Wednesday, July 25, 2012 11:34:12 monarch_dodra wrote:
> According to TDPL, the rationale for structs not having default
> constructors is "T.init", were T.init is defined as:
> *A static, known at compile time, mem-copyable value.
> *The value that gets mem-copied into structs before the
> constructors are called
> *The value that gets mem-copied into structs when moving stuff
> out of them.
> **Destructors must support being called on a T.init element.
> 
> The thing is, I can't, for the life of me, understand how that
> interferes with having a default constructor. Wouldn't keeping
> the current definition of init, but allowing a call to a "no-arg"
> constructor after after the mem-copy of T.init also work? As long
> as the destructor keeps supporting calls to T.init objects, then
> all is fine. Why hold out on a very useful functionality?
> 
> Am I missing a case where having a default constructor could
> actually mess things up? The restriction just feels gratuitous
> for me.

If you want to be able to do S(), then declare a static opCall for S which 
returns an S constructed in the way that you want.

struct S
{
   static S opCall()
   {
       //do whatever you do
       return s;
   }
}

auto s = S();

but

S s;

will always be init. But you can't have a default constructor, because then it 
conflicts with what init would be doing. e.g. would

S s;

be initialized with init or with the default constructor? But the static 
opCall does let you construct a struct without any arguments. You just can't 
have anything which isn't explicitly constructed using a default constructor, 
because that's init's job.

Also, because all types must have an init value, if you can't guarantee that 
your struct is valid and usable with its init property, then you'll either 
have to disable the struct's init value so that it cannot be use

struct S
{
   @disable this();
}

or you'll have to live with the fact that using it could blow up in your face 
- which really isn't all that different from other types (e.g. float.init is NAN 
and pointers default to null).

- Jonathan M Davis
July 25, 2012
Re: Struct no-arg constructor?
On Wednesday, 25 July 2012 at 09:51:49 UTC, Jonathan M Davis 
wrote:
> If you want to be able to do S(), then declare a static opCall 
> for S which
> returns an S constructed in the way that you want.
>
> struct S
> {
>     static S opCall()
>     {
>         //do whatever you do
>         return s;
>     }
> }
>
> auto s = S();
>
> but
>
> S s;
>
> will always be init. But you can't have a default constructor, 
> because then it
> conflicts with what init would be doing. e.g. would
>
> S s;
> [SNIP]

This makes *so* much sense now. I couldn't understand the point 
of "S a = S();" up until now. Thanks a lot.

I read TDPL back to back like 5 times before posting this, but it 
never mentions opCall. I just checked the index right now, it is 
not in there. Is this new?

Provided the existence of "opCall", though why would I keep 
writing constructors?

Also, while I understand why we can't have a "default" 
constructor, your explanations perfectly proves we *can* have a 
"no-arg" (subtle difference) one:
S s = S(); //Initialize s to this();
S s;       //Leave s as S.init

It seems like opCall is a bypass to a language restriction...
July 25, 2012
Re: Struct no-arg constructor?
Le 25/07/2012 11:51, Jonathan M Davis a écrit :
> On Wednesday, July 25, 2012 11:34:12 monarch_dodra wrote:
>> According to TDPL, the rationale for structs not having default
>> constructors is "T.init", were T.init is defined as:
>> *A static, known at compile time, mem-copyable value.
>> *The value that gets mem-copied into structs before the
>> constructors are called
>> *The value that gets mem-copied into structs when moving stuff
>> out of them.
>> **Destructors must support being called on a T.init element.
>>
>> The thing is, I can't, for the life of me, understand how that
>> interferes with having a default constructor. Wouldn't keeping
>> the current definition of init, but allowing a call to a "no-arg"
>> constructor after after the mem-copy of T.init also work? As long
>> as the destructor keeps supporting calls to T.init objects, then
>> all is fine. Why hold out on a very useful functionality?
>>
>> Am I missing a case where having a default constructor could
>> actually mess things up? The restriction just feels gratuitous
>> for me.
>
> If you want to be able to do S(), then declare a static opCall for S which
> returns an S constructed in the way that you want.
>
> struct S
> {
>      static S opCall()
>      {
>          //do whatever you do
>          return s;
>      }
> }
>
> auto s = S();
>
> but
>
> S s;
>
> will always be init. But you can't have a default constructor, because then it
> conflicts with what init would be doing. e.g. would
>
> S s;
>
> be initialized with init or with the default constructor? But the static
> opCall does let you construct a struct without any arguments. You just can't
> have anything which isn't explicitly constructed using a default constructor,
> because that's init's job.
>
> Also, because all types must have an init value, if you can't guarantee that
> your struct is valid and usable with its init property, then you'll either
> have to disable the struct's init value so that it cannot be use
>
> struct S
> {
>      @disable this();
> }
>
> or you'll have to live with the fact that using it could blow up in your face
> - which really isn't all that different from other types (e.g. float.init is NAN
> and pointers default to null).
>
> - Jonathan M Davis

Static opCall isn't a solution. You can't new on it. And it seems weird 
that you can disable something that don't possibly exists in the first 
place.

This topic comes back again and again on the NG, it have to be considered.

If I get a concrete example, let say RefCounted, I can see in the source 
code that many checks are performed to handle the case where the struct 
is .init . It have a runtime cost and make the code more complex.

It is more error prone because specific cases must be handled all over 
the place. Having .init is certainly an interesting capability of D, but 
in this specific case, it isn't sure it worth it.
July 25, 2012
Re: Struct no-arg constructor?
Le 25/07/2012 12:30, monarch_dodra a écrit :
> On Wednesday, 25 July 2012 at 09:51:49 UTC, Jonathan M Davis wrote:
>> If you want to be able to do S(), then declare a static opCall for S
>> which
>> returns an S constructed in the way that you want.
>>
>> struct S
>> {
>> static S opCall()
>> {
>> //do whatever you do
>> return s;
>> }
>> }
>>
>> auto s = S();
>>
>> but
>>
>> S s;
>>
>> will always be init. But you can't have a default constructor, because
>> then it
>> conflicts with what init would be doing. e.g. would
>>
>> S s;
>> [SNIP]
>
> This makes *so* much sense now. I couldn't understand the point of "S a
> = S();" up until now. Thanks a lot.
>
> I read TDPL back to back like 5 times before posting this, but it never
> mentions opCall. I just checked the index right now, it is not in there.
> Is this new?
>
> Provided the existence of "opCall", though why would I keep writing
> constructors?
>

new S(args); // Allocate on the heap instead of the stack

But forget about new S();

> Also, while I understand why we can't have a "default" constructor, your
> explanations perfectly proves we *can* have a "no-arg" (subtle
> difference) one:
> S s = S(); //Initialize s to this();
> S s; //Leave s as S.init
>
> It seems like opCall is a bypass to a language restriction...

It is. If something should go, it is for me the static opCall hack.
July 25, 2012
Re: Struct no-arg constructor?
On Wed, 25 Jul 2012 12:30:56 +0200, monarch_dodra <monarchdodra@gmail.com>  
wrote:

> I read TDPL back to back like 5 times before posting this, but it never  
> mentions opCall. I just checked the index right now, it is not in there.  
> Is this new?

Hardly. Once upon a time, in the bad old days, structs didn't have
constructors. In their stead, we used static opCall.

Then, many moons later, we got @disable this();, and now I sorta think
a struct default constructor should simply be syntactic sugar for

@disable this();

static typeof(this) opCall( ) {
    // body
}


I'm not completely sold, though. It feels a bit too much like magic.

--
Simen
July 25, 2012
Re: Struct no-arg constructor?
On Wednesday, 25 July 2012 at 10:40:22 UTC, deadalnix wrote:
> Static opCall isn't a solution. You can't new on it. And it 
> seems weird that you can disable something that don't possibly 
> exists in the first place.
>
> This topic comes back again and again on the NG, it have to be 
> considered.
>
> If I get a concrete example, let say RefCounted, I can see in 
> the source code that many checks are performed to handle the 
> case where the struct is .init . It have a runtime cost and 
> make the code more complex.
>
> It is more error prone because specific cases must be handled 
> all over the place. Having .init is certainly an interesting 
> capability of D, but in this specific case, it isn't sure it 
> worth it.

I think a good solution would be to simply allow structs to have 
a "no-argument constructor".

However, we'd have to hammer in the fact that this "no-argument 
constructor" is NOT a "default constructor" that always gets 
called. It is an explicit constructor, just like any other, but 
doesn't take any arguments.

It seems straight forward, simple and intuitive to me actually.
Top | Discussion index | About this forum | D home