digitalmars.D.learn - Destructor order
- eles (24/24) Oct 22 2014 C++ versions:
- eles (9/9) Oct 22 2014 On Wednesday, 22 October 2014 at 15:45:02 UTC, eles wrote:
- Regan Heath (18/27) Oct 22 2014 Structs are special, compare:
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (9/20) Oct 22 2014 But Scoped!A is on the stack?
- anonymous (38/48) Oct 22 2014 `foo` should be a `Scoped!A`. When it's typed as `A`, the
- ketmar via Digitalmars-d-learn (6/23) Oct 22 2014 On Wed, 22 Oct 2014 18:03:44 +0000
- "Ola Fosheim =?UTF-8?B?R3LDuHN0YWQi?= (3/7) Oct 22 2014 This is too dangerous to be used in real programs…
- ketmar via Digitalmars-d-learn (6/13) Oct 22 2014 there is nothing really dangerous if the feature is used properly. i'm
- eles (2/6) Oct 22 2014 Just tell me that's a feature, not a bug...
- eles (5/10) Oct 22 2014 And the compiler swallows this without even barking? Why Scoped!A
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (11/15) Oct 22 2014 The compiler must obey an alias this inside Scoped.
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (2/17) Oct 23 2014 Yet another use case for borrowing.
- Marco Leise (7/8) Oct 23 2014 Cool, how does it keep the original struct alive though, if it
- "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> (15/21) Oct 23 2014 It needs to error out. Artificial life support for the original
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (17/39) Oct 22 2014 I confirm this. If you put writeln() expressions inside typecons.scoped,...
- eles (3/10) Oct 22 2014 Write it on this page http://ddili.org/ders/d.en/destroy.html
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (3/5) Oct 22 2014 Don't worry, it's already in my short todo list. ;)
C++ versions: { //displays ~C~B~A A foo; B bar; C *caz = new C(); delete caz; } std::cout << std::endl; { //displays ~C~B~A std::unique_ptr<A> foo = std::make_unique<A>(); std::unique_ptr<B> bar = std::make_unique<B>(); C *caz = new C(); delete caz; } D version: { //displays ~A~B~C A foo = scoped!(A)(); B bar = scoped!(B)(); C caz = new C(); destroy(caz); } Why the objects are not destroyed in the inverse order of their creation? Case in point, destroying foo releases a lock for bar and caz.
Oct 22 2014
On Wednesday, 22 October 2014 at 15:45:02 UTC, eles wrote: D version with structs: { //display ~C~B~A A foo; B bar; C *caz = new C(); delete caz; } as expected.
Oct 22 2014
On Wed, 22 Oct 2014 16:49:20 +0100, eles <eles eles.com> wrote:On Wednesday, 22 October 2014 at 15:45:02 UTC, eles wrote: D version with structs: { //display ~C~B~A A foo; B bar; C *caz = new C(); delete caz; } as expected.Structs are special, compare: http://dlang.org/struct.html#struct-destructor with: http://dlang.org/class.html#destructors Specifically: "The garbage collector is not guaranteed to run the destructor for all unreferenced objects. Furthermore, the order in which the garbage collector calls destructors for unreference objects is not specified. This means that when the garbage collector calls a destructor for an object of a class that has members that are references to garbage collected objects, those references may no longer be valid. This means that destructors cannot reference sub objects. This rule does not apply to auto objects or objects deleted with the DeleteExpression, as the destructor is not being run by the garbage collector, meaning all references are valid." Regan -- Using Opera's revolutionary email client: http://www.opera.com/mail/
Oct 22 2014
On Wednesday, 22 October 2014 at 16:55:41 UTC, Regan Heath wrote:"The garbage collector is not guaranteed to run the destructor for all unreferenced objects. Furthermore, the order in which the garbage collector calls destructors for unreference objects is not specified. This means that when the garbage collector calls a destructor for an object of a class that has members that are references to garbage collected objects, those references may no longer be valid. This means that destructors cannot reference sub objects. This rule does not apply to auto objects or objects deleted with the DeleteExpression, as the destructor is not being run by the garbage collector, meaning all references are valid."But Scoped!A is on the stack? So why wasn't the eles' destructor order in reverse if Scoped is a struct and calls explicit destroy(B) then destroy(A)? https://github.com/D-Programming-Language/phobos/blob/master/std/typecons.d#L4628 ~this() { .destroy(Scoped_payload); }
Oct 22 2014
On Wednesday, 22 October 2014 at 17:13:35 UTC, Ola Fosheim Grøstad wrote:On Wednesday, 22 October 2014 at 16:55:41 UTC, Regan Heath wrote:But Scoped!A is on the stack?Exactly. From Ali's book: "scoped() wraps the class object inside a struct and the destructor of that struct object destroys the class object when itself goes out of scope. " Destroy! ;) http://ddili.org/ders/d.en/destroy.html
Oct 22 2014
On Wednesday, 22 October 2014 at 17:13:35 UTC, Ola Fosheim Grøstad wrote:On Wednesday, 22 October 2014 at 16:55:41 UTC, Regan HeathSo why wasn't the eles' destructor order in reverse if Scoped is a struct and calls explicit destroy(B) then destroy(A)?Maybe it's the writeln() inside the destructor which behaves badly, albeit the same should have happened for structs too.
Oct 22 2014
On Wednesday, 22 October 2014 at 15:45:02 UTC, eles wrote:D version: { //displays ~A~B~C A foo = scoped!(A)(); B bar = scoped!(B)(); C caz = new C(); destroy(caz); } Why the objects are not destroyed in the inverse order of their creation? Case in point, destroying foo releases a lock for bar and caz.`foo` should be a `Scoped!A`. When it's typed as `A`, the `Scoped!A` that is returned by `scoped`, is destructed immediately (and the reference leaks, I guess). Compare: import std.stdio; import std.typecons; class A {~this() {writeln("~A");}} class B {~this() {writeln("~B");}} class C {~this() {writeln("~C");}} void main() { { writeln("bad:"); A foo = scoped!(A)(); writeln("1"); B bar = scoped!(B)(); writeln("2"); } { writeln("good:"); auto foo = scoped!(A)(); writeln("1"); auto bar = scoped!(B)(); writeln("2"); } } prints: bad: ~A 1 ~B 2 good: 1 2 ~B ~A
Oct 22 2014
On Wed, 22 Oct 2014 18:03:44 +0000 anonymous via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:On Wednesday, 22 October 2014 at 15:45:02 UTC, eles wrote:yes, this is the case. i believe that this should be explicitly documented in `scoped` ddocs. documentation examples using 'auto', but there is no mention that this is what *must* be used.D version: { //displays ~A~B~C A foo =3D scoped!(A)(); B bar =3D scoped!(B)(); C caz =3D new C(); destroy(caz); } Why the objects are not destroyed in the inverse order of their=20 creation? Case in point, destroying foo releases a lock for bar=20 and caz.=20 `foo` should be a `Scoped!A`. When it's typed as `A`, the `Scoped!A` that is returned by `scoped`, is destructed immediately (and the reference leaks, I guess).
Oct 22 2014
On Wednesday, 22 October 2014 at 18:14:54 UTC, ketmar via Digitalmars-d-learn wrote:yes, this is the case. i believe that this should be explicitly documented in `scoped` ddocs. documentation examples using 'auto', but there is no mention that this is what *must* be used.This is too dangerous to be used in real programs…
Oct 22 2014
On Wed, 22 Oct 2014 18:44:26 +0000 via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:On Wednesday, 22 October 2014 at 18:14:54 UTC, ketmar via=20 Digitalmars-d-learn wrote:there is nothing really dangerous if the feature is used properly. i'm using `scoped` in my software without any troubles. yes, there might be better ways to do what `scoped` does, but for now `scoped` is... satisfactory. it just need some care. ;-)yes, this is the case. i believe that this should be explicitly documented in `scoped` ddocs. documentation examples using=20 'auto', but there is no mention that this is what *must* be=20 used.This is too dangerous to be used in real programs=E2=80=A6
Oct 22 2014
On Wednesday, 22 October 2014 at 18:03:44 UTC, anonymous wrote:On Wednesday, 22 October 2014 at 15:45:02 UTC, eles wrote:`foo` should be a `Scoped!A`. When it's typed as `A`, the `Scoped!A` that is returned by `scoped`, is destructed immediately (and the reference leaks, I guess).Just tell me that's a feature, not a bug...
Oct 22 2014
On Wednesday, 22 October 2014 at 18:03:44 UTC, anonymous wrote:On Wednesday, 22 October 2014 at 15:45:02 UTC, eles wrote:D version:`foo` should be a `Scoped!A`. When it's typed as `A`, the `Scoped!A` that is returned by `scoped`, is destructed immediately (and the reference leaks, I guess).And the compiler swallows this without even barking? Why Scoped!A is convertible to A, then? And what the resulting A-typed variable contains if the object gets destroyed. And what use for that content?
Oct 22 2014
On 10/22/2014 04:05 PM, eles wrote:And the compiler swallows this without even barking?The compiler must obey an alias this inside Scoped. I've thinking for a way do disallow this but haven't been able to spend much time on it today.Why Scoped!A is convertible to A, then?So that Scoped!A can conveniently be used as an A.And what the resulting A-typed variable contains if the object gets destroyed.Note that the A is not the object but the class reference to it. The 'alias this' hands out a reference to the object that is on the stack. The reference is valid when that occurs. Then, the compiler destroys the temporary Scoped!A object as it should, leaving behind a dangling reference. There must be a way to disallow this. Ali
Oct 22 2014
On Wednesday, 22 October 2014 at 23:11:14 UTC, Ali Çehreli wrote:On 10/22/2014 04:05 PM, eles wrote:Yet another use case for borrowing.And the compiler swallows this without even barking?The compiler must obey an alias this inside Scoped. I've thinking for a way do disallow this but haven't been able to spend much time on it today.Why Scoped!A is convertible to A, then?So that Scoped!A can conveniently be used as an A.And what the resulting A-typed variable contains if the object gets destroyed.Note that the A is not the object but the class reference to it. The 'alias this' hands out a reference to the object that is on the stack. The reference is valid when that occurs. Then, the compiler destroys the temporary Scoped!A object as it should, leaving behind a dangling reference. There must be a way to disallow this.
Oct 23 2014
Am Thu, 23 Oct 2014 12:15:13 +0000 schrieb "Marc Sch=C3=BCtz" <schuetzm gmx.net>:Yet another use case for borrowing.Cool, how does it keep the original struct alive though, if it isn't stored anywhere? Or will it error out when you attempt to use the dangling pointer to the object? --=20 Marco
Oct 23 2014
On Thursday, 23 October 2014 at 13:03:08 UTC, Marco Leise wrote:Am Thu, 23 Oct 2014 12:15:13 +0000 schrieb "Marc Schütz" <schuetzm gmx.net>:It needs to error out. Artificial life support for the original object would be too much magic for my taste. struct Scoped(T) if(is(T == class)) { private T _payload; // <insert constructor and destructor here> scope!this(T) borrow() { return _payload; } alias borrow this; } void foo() { A a = scoped!A(); // ERROR: cannot assign Scope!A to A scope b = scoped!A(); // ERROR: local `b` outlives temporary }Yet another use case for borrowing.Cool, how does it keep the original struct alive though, if it isn't stored anywhere? Or will it error out when you attempt to use the dangling pointer to the object?
Oct 23 2014
On 10/22/2014 08:45 AM, eles wrote:> C++ versions:{ //displays ~C~B~A A foo; B bar; C *caz = new C(); delete caz; } std::cout << std::endl; { //displays ~C~B~A std::unique_ptr<A> foo = std::make_unique<A>(); std::unique_ptr<B> bar = std::make_unique<B>(); C *caz = new C(); delete caz; } D version: { //displays ~A~B~C A foo = scoped!(A)(); B bar = scoped!(B)(); C caz = new C(); destroy(caz); } Why the objects are not destroyed in the inverse order of their creation? Case in point, destroying foo releases a lock for bar and caz.I confirm this. If you put writeln() expressions inside typecons.scoped, you will realize that the destroys are happening right after each scoped line: constructed A destroying ~A constructed B destroying ~B ~C Extremely dangerous and very tricky! The solution? Define the objects as 'auto' (or 'const', etc.), which you want anyway: auto foo = scoped!(A)(); auto bar = scoped!(B)(); You do not want to be left with A or B. You want Scoped!A and Scoped!B. Ali
Oct 22 2014
On Wednesday, 22 October 2014 at 18:03:59 UTC, Ali Çehreli wrote:On 10/22/2014 08:45 AM, eles wrote:> C++ versions:Extremely dangerous and very tricky! The solution? Define the objects as 'auto' (or 'const', etc.), which you want anyway: auto foo = scoped!(A)(); auto bar = scoped!(B)(); You do not want to be left with A or B. You want Scoped!A and Scoped!B.Write it on this page http://ddili.org/ders/d.en/destroy.html with red big font, please.
Oct 22 2014
On 10/22/2014 03:59 PM, eles wrote:Write it on this page http://ddili.org/ders/d.en/destroy.html with red big font, please.Don't worry, it's already in my short todo list. ;) Ali
Oct 22 2014