Jump to page: 1 25  
Page
Thread overview
What is the case against a struct post-blit default constructor?
Oct 08, 2012
Malte Skarupke
Oct 08, 2012
F i L
Oct 09, 2012
Gor Gyolchanyan
Oct 10, 2012
Jonathan M Davis
Oct 10, 2012
monarch_dodra
Oct 10, 2012
Jonathan M Davis
Oct 10, 2012
Don Clugston
Oct 10, 2012
Jonathan M Davis
Oct 10, 2012
foobar
Oct 10, 2012
Jonathan M Davis
Oct 10, 2012
foobar
Oct 10, 2012
Jonathan M Davis
Oct 10, 2012
Jonathan M Davis
Oct 11, 2012
Malte Skarupke
Oct 10, 2012
Timon Gehr
Oct 10, 2012
Don Clugston
Oct 11, 2012
Timon Gehr
Oct 11, 2012
deadalnix
Oct 11, 2012
deadalnix
Oct 11, 2012
Simen Kjaeraas
Oct 11, 2012
deadalnix
Oct 11, 2012
Jonathan M Davis
Oct 11, 2012
Walter Bright
Oct 11, 2012
Jonathan M Davis
Oct 11, 2012
monarch_dodra
Oct 11, 2012
Dmitry Olshansky
Oct 11, 2012
Jonathan M Davis
Oct 12, 2012
monarch_dodra
Oct 12, 2012
Jonathan M Davis
Oct 12, 2012
monarch_dodra
Oct 12, 2012
Jonathan M Davis
Oct 12, 2012
monarch_dodra
Oct 12, 2012
Jonathan M Davis
Oct 12, 2012
monarch_dodra
Oct 12, 2012
Jonathan M Davis
Oct 12, 2012
David Nadlinger
Oct 12, 2012
Jonathan M Davis
Oct 12, 2012
Era Scarecrow
Oct 12, 2012
monarch_dodra
Oct 12, 2012
Simen Kjaeraas
Oct 15, 2012
monarch_dodra
Oct 16, 2012
Jonathan M Davis
Oct 11, 2012
Timon Gehr
Oct 12, 2012
Malte Skarupke
Oct 11, 2012
Walter Bright
Oct 12, 2012
Jonathan M Davis
October 08, 2012
So this has been brought up many times (http://www.digitalmars.com/d/archives/digitalmars/D/Struct_no-arg_constructor_173172.html http://www.digitalmars.com/d/archives/digitalmars/D/learn/Default_constructor_for_structs_20997.html http://www.digitalmars.com/d/archives/digitalmars/D/struct_and_default_constructor_150016.html)

However there was never a good answer.

I would like a struct default constructor. My expected behavior for it is that the struct gets initialized to .init, then my destructor gets called so that I can do additional things if I want.
This destructor always gets called when my struct is created without arguments.

So for example

struct S
{
    this()
    {
        assert(bar == 10);
        foo = [5].ptr;
    }
    int * foo;
    int bar = 10;
}
S s;
assert(*s.foo == 5);

Possible cases against it:
- "It would be slower than just initializing to .init." My proposed solution: Make the default constructor optional. If I have a struct that doesn't define a default constructor, it is just intialized to .init. So I can have the speed if I want to. Also always run it post-blit (blitted from .init). Meaning I only need to use it for things that can only be initialized at runtime.
- "There already is a solution in @disable this(); static opCall() {...}." This is hacky and working against the language. It also makes it so that you can not allocate your struct on the heap.
- "Structs should be simple." I haven't heard this argument, but I could imagine someone making it. I think structs should not be simple. They are much too useful for that. Also them having copy constructors and opAssign indicates that structs aren't expected to be simple.
- "There would be a way around it by doing S s = S.init;" I don't think that's a problem. If users want to shoot themselves in the foot, let em. The important part is that they have to go out of their way to do it. You could also handle that case in the copy constructor or assignment operator. (which is an improvement to the current behavior where you have to handle that case in every single member function, see for example std.typecons.RefCounted)


So I really can't think of a reason for why you wouldn't want this. Yet this discussion has happened several times already. There is clear demand for it and very good reasons, such as those mentioned in all the linked discussions.

So why is this being rejected?

Cheers,
Malte
October 08, 2012
+1 to all of that.

If the only issue is performance, I think the best solution is just to Document with a warning against using default constructors in performance critical structs.
October 09, 2012
I agree. AFAIK, D's policy is "give a safe default and a back door around it".

On Tue, Oct 9, 2012 at 1:25 AM, F i L <witte2008@gmail.com> wrote:

> +1 to all of that.
>
> If the only issue is performance, I think the best solution is just to Document with a warning against using default constructors in performance critical structs.
>



-- 
Bye,
Gor Gyolchanyan.


October 10, 2012
On Monday, October 08, 2012 18:47:43 Malte Skarupke wrote:
> So I really can't think of a reason for why you wouldn't want this. Yet this discussion has happened several times already. There is clear demand for it and very good reasons, such as those mentioned in all the linked discussions.
> 
> So why is this being rejected?

It buys you pretty much nothing. There are plenty of places in the language where init is required (e.g. member variables that can't be directly initialized and the elements in an array). So, init _will_ be used regardless of what you do with the default constructor. If you want to prevent that, then you need to disable init, which we can already do. But you're not going to get those things initialized with the default constructor, which kind of defeats the purpose of the default constructor. If you can't guarantee that every instance which isn't explicitly constructed is default constructed, then what's the point?

And if you want to have a way to explicitly construct a struct with no arguments, then you can use static opCall. Voila. Problem solved. What else do you need, given that you can't possibly have the struct call a default constructor everywhere that it's default initialized?

There's the issue of

S s;

being S.init, but that's _normal_. If you were to make it the same as S() when a default constructor were declared, then structs wouldn't act like every other single type in the language (which are all default initialized to their init values). It would also make it _far_ more likely for people to write a default constructor thinking that it was _always_ used and then end up with serious problems when it wasn't - e.g. when it wasn't called for the elements or arrays or for member variables or for module-level variables or for any other instance of the struct which has to use init. By making

S s;

use S.init and disallowing default constructors, we make it clear to people _far_ faster that they can't rely on any kind of default construction of structs in D. Adding a default constructor which worked with

S s;

would just hide the problem and give people the false impression that they could rely on default construction. static opCall provides a means of constructing a struct with no arguments if that's what you want. So, as far as I can see, it pretty much just comes down to whether

S s;

is changed to be default constructed rather than default initialized when a default constructor is declared. And I think that that's truly counterproductive. It makes structs act inconsistently with other types and misleads people as to how default initialization works in D, which will only cause more bugs.

The _only_ way that this could be reasonably solved is if you could somehow make it so that every time that a struct is default-initialized, it's default- constructed instead, and D just doesn't work that way. Too many things rely on init and the fact that it's known at compile time.

So, IMHO adding default constructors to structs would be not only pointless but counterproductive.

- Jonathan M Davis
October 10, 2012
On Wednesday, 10 October 2012 at 09:48:27 UTC, Jonathan M Davis wrote:
>
> [SNIP]
>
> - Jonathan M Davis

After thinking about it, a lot, I think that would actually be the correct solution. I was an advocate of the "no-arg" constructor (not necessarily default), but I think it would end up being ambiguous in things like "emplace(&t)": "Do you want .init, or .__ctor()?"

This, I think makes sense:
T t; //T.init
T t = T(); //opCall

As for "new()", one can just two line it:
p = new T(); //T.init
p = T();     //opCall

It takes two lines, but it makes a perfect distinction between .init and T().

--------

But are you telling is that the "opCall trick" is now official sanctioned as the way of initializing things that may need initialization, even though no arguments are passed?

Can I roll it out in std.container? In RefCounted? In random? They are all "payload" structs, and they are in desperate need of some formal way to say:

"I want you initialized with your payloads and ready for use, but I have nothing to give you."
October 10, 2012
On Wednesday, October 10, 2012 11:43:53 monarch_dodra wrote:
> But are you telling is that the "opCall trick" is now official sanctioned as the way of initializing things that may need initialization, even though no arguments are passed?

I believe that it has been that way for ages. There's really no other way to do it unless you provide another function that does exactly the same thing, which. e.g.

auto s = constructS();

And I don't see how that buys you anything over using static opCall.

- Jonathan M Davis
October 10, 2012
On 10/10/12 11:21, Jonathan M Davis wrote:
> On Monday, October 08, 2012 18:47:43 Malte Skarupke wrote:
>> So I really can't think of a reason for why you wouldn't want
>> this. Yet this discussion has happened several times already.
>> There is clear demand for it and very good reasons, such as those
>> mentioned in all the linked discussions.
>>
>> So why is this being rejected?
>
> It buys you pretty much nothing. There are plenty of places in the language
> where init is required (e.g. member variables that can't be directly
> initialized and the elements in an array). So, init _will_ be used regardless
> of what you do with the default constructor. If you want to prevent that, then
> you need to disable init, which we can already do. But you're not going to get
> those things initialized with the default constructor, which kind of defeats
> the purpose of the default constructor. If you can't guarantee that every
> instance which isn't explicitly constructed is default constructed, then
> what's the point?

Of course there would be no point.
You have not answered the question. The issue is, WHY can we not guarantee that that the struct default constructor is called?

I have a vague memory that Walter mentioned a technical difficulty once but I don't remember anything about what it was.

I can't imagine what it would be. Even in the worst case, it would be possible to run CTFE on the default constructor in order to create .init. This would limit the default constructor to things which are CTFEable, but even that would still be useful for templated structs.

Really, there does not seem to me to be any point in having an invariant for a struct, without a default constructor.

BTW .init doesn't really work for nested structs anyway. There are several open bugs related to that.

October 10, 2012
On Wednesday, October 10, 2012 12:45:20 Don Clugston wrote:
> Of course there would be no point.
> You have not answered the question. The issue is, WHY can we not
> guarantee that that the struct default constructor is called?
> 
> I have a vague memory that Walter mentioned a technical difficulty once but I don't remember anything about what it was.
> 
> I can't imagine what it would be.

I know that one of the issues was exceptions, though that could be solved by forcing them to be nothrow. There were others, but I don't remember the details. Certainly, from what I recall, the situation is such that you'd have to restrict a default construtor so thoroughly that it would be pretty much pointless to have one at all.

> Even in the worst case, it would be
> possible to run CTFE on the default constructor in order to create
> .init. This would limit the default constructor to things which are
> CTFEable, but even that would still be useful for templated structs.

Of what real value is a default construct that must be CTFEable? If you can construct everything at compile time, then you can directly initialize each of the member variables. It's being able to run a default constructor at runtime that's useful, and since init must be known at compile time, you get into the whole issue of needing init when you can't use a default constructor. For default constructors to be of any real value, it would have to be possible to replace all instances of init with a call to the default constructor _at runtime_. As long as you're restricted to CTFE, you might as well just directly initialize the member variables to set the values appropriately in the init property.

- Jonathan M Davis
October 10, 2012
On 10/10/2012 12:45 PM, Don Clugston wrote:
> On 10/10/12 11:21, Jonathan M Davis wrote:
>> On Monday, October 08, 2012 18:47:43 Malte Skarupke wrote:
>>> So I really can't think of a reason for why you wouldn't want
>>> this. Yet this discussion has happened several times already.
>>> There is clear demand for it and very good reasons, such as those
>>> mentioned in all the linked discussions.
>>>
>>> So why is this being rejected?
>>
>> It buys you pretty much nothing. There are plenty of places in the
>> language
>> where init is required (e.g. member variables that can't be directly
>> initialized and the elements in an array). So, init _will_ be used
>> regardless
>> of what you do with the default constructor. If you want to prevent
>> that, then
>> you need to disable init, which we can already do. But you're not
>> going to get
>> those things initialized with the default constructor, which kind of
>> defeats
>> the purpose of the default constructor. If you can't guarantee that every
>> instance which isn't explicitly constructed is default constructed, then
>> what's the point?
>
> Of course there would be no point.
> You have not answered the question. The issue is, WHY can we not
> guarantee that that the struct default constructor is called?
>

Because the current language does not. :o)

Because that would imply disabling .init.
 - Which defeats the .init idiom in generic code. I am not convinced
   that we need that though. (T x)=>... does the job just fine, and
   .init has issues, as eg. int.init has special implicit conversion
   rules that not all members of the type int share.
 - Need a new (unsafe) language feature in order to be able to
   implement eg. 'emplace'.


> I have a vague memory that Walter mentioned a technical difficulty once
> but I don't remember anything about what it was.
>
> I can't imagine what it would be.

+1.

> Even in the worst case, it would be
> possible to run CTFE on the default constructor in order to create
> .init. This would limit the default constructor to things which are
> CTFEable, but even that would still be useful for templated structs.
>

You could run CTFE on the default constructor iff .init is requested,
but I don't really see the point. What would be the benefit?

> Really, there does not seem to me to be any point in having an invariant
> for a struct, without a default constructor.
>

One can use a dented invariant.

struct S{
    bool valid = false;
    // ...
    invariant(){ if(valid) assert(...); }
    void establishInvariant()out{assert(valid);}body{...}
}

> BTW .init doesn't really work for nested structs anyway. There are
> several open bugs related to that.
>

That is true, what are the plans for that? Not being able to have a
field of a local struct type is a serious limitation. eg:

struct Delay(T){ // where 'T' could easily be a struct from eg.
                 // std.algorithm which is instantiated locally.
    T delegate() dg;
    T value;     // ???
    // invariant !dg ==> value.__context
    ref T compute(){
        if(dg){ value = dg(); dg = null; }
        return value;
    }
    alias compute this;
}
auto delay(T)(T delegate() dg){ return Delay!T(dg); }


(Built-in nullable types would solve the issue of course.)
October 10, 2012
On Wednesday, 10 October 2012 at 11:23:11 UTC, Jonathan M Davis wrote:
> On Wednesday, October 10, 2012 12:45:20 Don Clugston wrote:
>> Of course there would be no point.
>> You have not answered the question. The issue is, WHY can we not
>> guarantee that that the struct default constructor is called?
>> 
>> I have a vague memory that Walter mentioned a technical difficulty once
>> but I don't remember anything about what it was.
>> 
>> I can't imagine what it would be.
>
> I know that one of the issues was exceptions, though that could be solved by
> forcing them to be nothrow. There were others, but I don't remember the
> details. Certainly, from what I recall, the situation is such that you'd have
> to restrict a default construtor so thoroughly that it would be pretty much
> pointless to have one at all.
>
>> Even in the worst case, it would be
>> possible to run CTFE on the default constructor in order to create
>> .init. This would limit the default constructor to things which are
>> CTFEable, but even that would still be useful for templated structs.
>
> Of what real value is a default construct that must be CTFEable? If you can
> construct everything at compile time, then you can directly initialize each of
> the member variables. It's being able to run a default constructor at runtime
> that's useful, and since init must be known at compile time, you get into the
> whole issue of needing init when you can't use a default constructor. For
> default constructors to be of any real value, it would have to be possible to
> replace all instances of init with a call to the default constructor _at
> runtime_. As long as you're restricted to CTFE, you might as well just
> directly initialize the member variables to set the values appropriately in
> the init property.
>
> - Jonathan M Davis

Can you please elaborate on where the .init property is being relied on? This is an aspect of D I don't really understand. What's the difference between a no-arg ctor and one with args in relation to this requirement?

Is this used somewhere in template code? I vaguely remember the clear() function is supposed to be related to this.

Personally, I don't agree with Walter's stance on default initialization. An explicit init by the user is IMO better design since it is _explicit_ and therefore documents the programmer's intention in the code itself.
i.e.:
int x = 0; // this is explicit and states my intention
int x; // is it intentionally == 0? Did I forget to set a meaningful value?
« First   ‹ Prev
1 2 3 4 5