January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On 1/31/13 10:29 AM, Steven Schveighoffer wrote:
> On Thu, 31 Jan 2013 10:21:04 -0500, Andrei Alexandrescu
> <SeeWebsiteForEmail@erdani.org> wrote:
>
>> On 1/31/13 10:18 AM, Steven Schveighoffer wrote:
>>> On Thu, 31 Jan 2013 10:12:53 -0500, Andrei Alexandrescu
>>>> As far as I can tell classes have the same problem.
>>>
>>> Nope.
>>>
>>> void foo(someclass aa, int x, int y)
>>> {
>>> aa[x] = y;
>>> }
>>>
>>> void main()
>>> {
>>> someclass aa;
>>> foo(aa, 1, 2); // segfault
>>> ...
>>> }
>>
>> We could easily arrange things to segfault just the same with a
>> struct-based implementation.
>
> So you want to make a struct that acts just like a class? I'm not seeing
> the point.
It has a destructor.
Andrei
|
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Thu, 31 Jan 2013 10:40:15 -0500, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote: > On 1/31/13 10:29 AM, Steven Schveighoffer wrote: >> On Thu, 31 Jan 2013 10:21:04 -0500, Andrei Alexandrescu >> <SeeWebsiteForEmail@erdani.org> wrote: >> >>> On 1/31/13 10:18 AM, Steven Schveighoffer wrote: >>>> On Thu, 31 Jan 2013 10:12:53 -0500, Andrei Alexandrescu >>>>> As far as I can tell classes have the same problem. >>>> >>>> Nope. >>>> >>>> void foo(someclass aa, int x, int y) >>>> { >>>> aa[x] = y; >>>> } >>>> >>>> void main() >>>> { >>>> someclass aa; >>>> foo(aa, 1, 2); // segfault >>>> ... >>>> } >>> >>> We could easily arrange things to segfault just the same with a >>> struct-based implementation. >> >> So you want to make a struct that acts just like a class? I'm not seeing >> the point. > > It has a destructor. One which is not called if allocated on the heap. It's possible to make a class reference that is destroyed when going out of scope. Then you have the option, heap destroyed or stack destroyed. Not possible with structs (at least for now). -Steve |
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Thursday, January 31, 2013 10:39:46 Andrei Alexandrescu wrote:
> On 1/31/13 10:27 AM, monarch_dodra wrote:
> > On Thursday, 31 January 2013 at 15:12:52 UTC, Andrei Alexandrescu wrote:
> >> As far as I can tell classes have the same problem.
> >>
> >>
> >> Andrei
> >
> > Regarding classes, would there be any chance of being to create a class instance that isn't a child of "object"?
>
> Using classes entails buying into an entire object model with its own pluses and minuses. Adding classes that don't inherit Object would wreck havoc all over the place.
And what would be the gain, anyway?
I don't understand what the downside is to having a single base object besides the issues with toString, toHash, opEquals, and opCmp. And we decided to remove all of them from Object (though no actual progress has been made beyond the decision), and with those gone, Object will have next to nothing on it anyway. But not having Object be the base of all classes would definitely cause quite a few problems.
- Jonathan M Davis
|
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On 1/31/13 11:00 AM, Steven Schveighoffer wrote: > On Thu, 31 Jan 2013 10:40:15 -0500, Andrei Alexandrescu > <SeeWebsiteForEmail@erdani.org> wrote: >> It has a destructor. > > One which is not called if allocated on the heap. That is correct. My point was that with structs we get to implement full-fledged reference counting for containers. Reference counting makes a lot of sense for containers. They inherently own their internals, don't have cycles, and are massive enough to make pass by value effectively an anti-pattern (as it is for C++). At the same time memory reclamation is of high interest. Reference counting is really the sweet spot for containers. > It's possible to make a class reference that is destroyed when going out > of scope. Then you have the option, heap destroyed or stack destroyed. > > Not possible with structs (at least for now). I don't understand this. Is it about the deprecated meaning of scope? Andrei |
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jonathan M Davis | On Thursday, 31 January 2013 at 16:17:09 UTC, Jonathan M Davis wrote:
> On Thursday, January 31, 2013 10:39:46 Andrei Alexandrescu wrote:
>> On 1/31/13 10:27 AM, monarch_dodra wrote:
>> > On Thursday, 31 January 2013 at 15:12:52 UTC, Andrei Alexandrescu wrote:
>> >> As far as I can tell classes have the same problem.
>> >>
>> >>
>> >> Andrei
>> >
>> > Regarding classes, would there be any chance of being to create a class
>> > instance that isn't a child of "object"?
>>
>> Using classes entails buying into an entire object model with its own
>> pluses and minuses. Adding classes that don't inherit Object would wreck
>> havoc all over the place.
>
> And what would be the gain, anyway?
>
> I don't understand what the downside is to having a single base object besides
> the issues with toString, toHash, opEquals, and opCmp. And we decided to
> remove all of them from Object (though no actual progress has been made beyond
> the decision), and with those gone, Object will have next to nothing on it
> anyway. But not having Object be the base of all classes would definitely cause
> quite a few problems.
>
> - Jonathan M Davis
I guess it still has the vtable and monitor.
In any case, the question was mostly asked to "test the waters" in regards to "class usage best practice", and to probe the consequences of such a change.
Please disregard the question in further discussion.
|
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Thu, 31 Jan 2013 11:17:14 -0500, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote: > On 1/31/13 11:00 AM, Steven Schveighoffer wrote: >> On Thu, 31 Jan 2013 10:40:15 -0500, Andrei Alexandrescu >> <SeeWebsiteForEmail@erdani.org> wrote: >>> It has a destructor. >> >> One which is not called if allocated on the heap. > > That is correct. My point was that with structs we get to implement full-fledged reference counting for containers. But that won't work if the struct is on the heap. You will leak the memory. Not only that, but if the container uses the GC to allocate nodes, calling the destructor during the GC cycle would be a bad thing. > Reference counting makes a lot of sense for containers. They inherently own their internals, don't have cycles, and are massive enough to make pass by value effectively an anti-pattern (as it is for C++). At the same time memory reclamation is of high interest. Reference counting is really the sweet spot for containers. Reference counting makes a lot of sense for any reference type. Including classes. I think your point is that we can do reference counting with structs, and we can't with classes. I think: a) you can do reference counting with both (at the moment, this requires a wrapper struct for the class) b) structs that depend on destruction to free their memory do not work with the current runtime implementation. >> It's possible to make a class reference that is destroyed when going out >> of scope. Then you have the option, heap destroyed or stack destroyed. >> >> Not possible with structs (at least for now). > > I don't understand this. Is it about the deprecated meaning of scope? It means, classes have a mechanism for destruction via the heap (GC) or via the stack (scope, or whatever currently replaces scope (Scoped? I can't remember) ). Structs by default are easily destroyed from the stack, but lack the fundamental cogs to get GC-based destruction. In other words, you can't put a reference-counted struct on the heap, it won't work correctly (the destructor won't get called). This is not to say this can't be fixed, but if we are talking about current implementation limitations, classes are superior in how they can be used. -Steve |
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | 31-Jan-2013 19:21, Andrei Alexandrescu пишет: > On 1/31/13 10:18 AM, Steven Schveighoffer wrote: >> On Thu, 31 Jan 2013 10:12:53 -0500, Andrei Alexandrescu >>> As far as I can tell classes have the same problem. >> >> Nope. >> >> void foo(someclass aa, int x, int y) >> { >> aa[x] = y; >> } >> >> void main() >> { >> someclass aa; >> foo(aa, 1, 2); // segfault >> ... >> } > > We could easily arrange things to segfault just the same with a > struct-based implementation. > Structs are quite borked in this regard e.g. without extra efforts the following: somclass aa = someclass(); foor(aa, 1, 2); // segfault, surprize someclass() is someclass.init The current workaround I find the most sensible is: - @disable this(); - make all constructors private - define opCall and forward it to private constructors. 0-arg versions have to pass dummy and/or default values to get struct constructed - automate this boilerplate until something in language is fixed? :) The advantage is that the following is illegal: someclass aa; and the following works as expected: auto aa = someclass(); //create empty container damn it! -- Dmitry Olshansky |
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Thursday, 31 January 2013 at 18:59:20 UTC, Steven Schveighoffer wrote:
> Structs by default are easily destroyed from the stack, but lack the fundamental cogs to get GC-based destruction.
>
> -Steve
Perhaps _d_newitemT() and buddies can check whether it creates structs, store a collection of pointers to structs it allocates and at the end of program some druntime routine loops through out of the collection to run dtors?
|
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Dmitry Olshansky | On Thursday, 31 January 2013 at 19:17:56 UTC, Dmitry Olshansky wrote: > 31-Jan-2013 19:21, Andrei Alexandrescu пишет: >> On 1/31/13 10:18 AM, Steven Schveighoffer wrote: >>> On Thu, 31 Jan 2013 10:12:53 -0500, Andrei Alexandrescu >>>> As far as I can tell classes have the same problem. >>> >>> Nope. >>> >>> void foo(someclass aa, int x, int y) >>> { >>> aa[x] = y; >>> } >>> >>> void main() >>> { >>> someclass aa; >>> foo(aa, 1, 2); // segfault >>> ... >>> } >> >> We could easily arrange things to segfault just the same with a >> struct-based implementation. >> > > Structs are quite borked in this regard e.g. without extra efforts the following: > > somclass aa = someclass(); > foor(aa, 1, 2); // segfault, surprize someclass() is someclass.init > > The current workaround I find the most sensible is: > - @disable this(); > - make all constructors private > - define opCall and forward it to private constructors. 0-arg versions have to pass dummy and/or default values to get struct constructed > > - automate this boilerplate until something in language is fixed? :) > > The advantage is that the following is illegal: > someclass aa; > > and the following works as expected: > auto aa = someclass(); //create empty container damn it! If desired default struct value is constant for all instances of that struct, there is another workaround by changing init values: ------------------- import std.stdio; class A { int x = 5; } struct S { int x = 5; A a; } void main() { S s; // defaults to preallocated a, a is not null assert(S.x is 100 && s.a !is null && s.a.x is 100); } import runtime; mixin(declareExternInitZPointer!S); static this() { A a = new A; a.x = 100; S s = S(100, a); rtSetDefaultHeapInitializer(s); rtSetDefaultStackInitializer(s, mixin(passExternInitZPointer!S)); } ---------------------- Where runtime module is http://dpaste.dzfl.pl/40b59a5d This does not fully replace default ctor, since all instances of S are affected. |
January 31, 2013 Re: pass-by-ref semantics for structs (was Deque impl.) | ||||
---|---|---|---|---|
| ||||
Posted in reply to Maxim Fomin | 31-Jan-2013 23:32, Maxim Fomin пишет: > On Thursday, 31 January 2013 at 19:17:56 UTC, Dmitry Olshansky wrote: >> 31-Jan-2013 19:21, Andrei Alexandrescu пишет: >>> On 1/31/13 10:18 AM, Steven Schveighoffer wrote: >>>> On Thu, 31 Jan 2013 10:12:53 -0500, Andrei Alexandrescu >>>>> As far as I can tell classes have the same problem. >>>> >>>> Nope. >>>> >>>> void foo(someclass aa, int x, int y) >>>> { >>>> aa[x] = y; >>>> } >>>> >>>> void main() >>>> { >>>> someclass aa; >>>> foo(aa, 1, 2); // segfault >>>> ... >>>> } >>> >>> We could easily arrange things to segfault just the same with a >>> struct-based implementation. >>> >> >> Structs are quite borked in this regard e.g. without extra efforts the >> following: >> >> somclass aa = someclass(); >> foor(aa, 1, 2); // segfault, surprize someclass() is someclass.init >> >> The current workaround I find the most sensible is: >> - @disable this(); >> - make all constructors private >> - define opCall and forward it to private constructors. 0-arg versions >> have to pass dummy and/or default values to get struct constructed >> >> - automate this boilerplate until something in language is fixed? :) >> >> The advantage is that the following is illegal: >> someclass aa; >> >> and the following works as expected: >> auto aa = someclass(); //create empty container damn it! > > If desired default struct value is constant for all instances of that > struct, there is another workaround by changing init values: > ------------------- > import std.stdio; > > class A { int x = 5; } > > struct S { int x = 5; A a; } > > void main() > { > S s; // defaults to preallocated a, a is not null > assert(S.x is 100 && s.a !is null && s.a.x is 100); > } This is fine except that it doesn't solve the problem of: Container a = Container(); //should be empty container, not illegal null-container Basically @dsiable this() is optional, but I like to help user avoid bugs. > > import runtime; > > mixin(declareExternInitZPointer!S); > > static this() > { > A a = new A; > a.x = 100; > S s = S(100, a); > rtSetDefaultHeapInitializer(s); > rtSetDefaultStackInitializer(s, mixin(passExternInitZPointer!S)); > } > ---------------------- > > Where runtime module is http://dpaste.dzfl.pl/40b59a5d > > This does not fully replace default ctor, since all instances of S are > affected. -- Dmitry Olshansky |
Copyright © 1999-2021 by the D Language Foundation