class destruction
Hi. I'm playing around with D for a while and I would like to switch. But here is one thing, I need an answer for. In the Docs is mentioned that it is not sure that the DTor of a class is called. But what if I have struct, which holds a C Handle which is destroyed as soon as the struct gets destroyed (more or less like a unique pointer) and I stick this struct into a class (because I strongly need polymorphism)? Can I be sure that the Handle is destroyed as soon as the class is destroyed? Or will the handle leak? If so there are only two ways that come to mind: 1. the class holds only a pointer to the struct, which is unsafe since I must guarantee that the struct lives as long as the class 2. the class gets a close/finalize/destroy method (or is called with the built in destroy method), which is a absolute nogo, because it is absolutly sure that this can be forgotten. Besides, we live in 2015 and that is not C where I have to clean my code manually, so this option would be ridiculous :D I'm gratefull for any answers and ideas.
Re: class destruction
On Wednesday, 9 September 2015 at 07:19:58 UTC, Q wrote: Hi. I'm playing around with D for a while and I would like to switch. But here is one thing, I need an answer for. In the Docs is mentioned that it is not sure that the DTor of a class is called. But what if I have struct, which holds a C Handle which is destroyed as soon as the struct gets destroyed (more or less like a unique pointer) and I stick this struct into a class (because I strongly need polymorphism)? Then you have to make sure the class destruction happens and not rely on the GC. Can I be sure that the Handle is destroyed as soon as the class is destroyed? I think that yes, struct members are destroyed. But you need to make sure the class is destroyed. If so there are only two ways that come to mind: 1. the class holds only a pointer to the struct, which is unsafe since I must guarantee that the struct lives as long as the class 2. the class gets a close/finalize/destroy method (or is called with the built in destroy method), which is a absolute nogo, because it is absolutly sure that this can be forgotten. Besides, we live in 2015 and that is not C where I have to clean my code manually, so this option would be ridiculous :D I'm gratefull for any answers and ideas. I'm using the manual method after much hair-pulling: http://p0nce.github.io/d-idioms/#GC-proof-resource-class It absolutely is worse than the C++ situation, however with the above pattern leaks will be reported by the GC, which is a nice consolation prize. Alternatively, stick your class in Unique! / Refcounted! / scoped!
Re: class destruction
On Wednesday, 9 September 2015 at 07:19:58 UTC, Q wrote: Besides, we live in 2015 and that is not C where I have to clean my code manually, so this option would be ridiculous :D The difference is that in C you need to manage POD memory too. Manual management of non-POD resources is a problem of a smaller size, and you can use RAII too.
Re: class destruction
But if others use my code they must think about finalizing the classes? That is very awkward. I'll take a look at Rust and otherwise I will stick with C++. Thanks for you answer. :)
Re: class destruction
On Wednesday, 9 September 2015 at 07:19:58 UTC, Q wrote: Can I be sure that the Handle is destroyed as soon as the class is destroyed? It will do that automatically. Like the others said, you won't be sure when the class is destroyed unless you have the user code take ownership of it. (This btw is the same as C++, just in C++ the ownership syntax is a wee bit shorter.) C++: class Foo { public: Foo() { /* acquire */ } ~Foo() { /* destroy */ } }; void useFoo(Foo* foo) { } // pretend I did the header separation here int main() { Foo foo; useFoo(&foo); return 0; } D: -- module foo; class Foo { private this() { /* acquire */ } ~this() { /* destroy */ } } struct OwnedFoo(F) { F foo; alias foo this; ~this() { .destroy(foo); } @disable this(this) {} } auto create(F, T...)(T t) if(is(F : Foo)) { return OwnedFoo!F(new F(t)); } -- -- module main; import foo; void useFoo(Foo foo) {} void main() { auto foo = create!Foo(); } --- The code is a bit longer since the owned pointer in the standard library isn't exactly what I wanted, but you could use std.typecons.Unique too. (Actually, it does offer a bit more safety in reference escaping than mine. But mine is the closest to C++ default). The private constructor means it won't let you bypass the owned factory when creating it. A bit extra code in foo means all uses of it will be simple like in main. Just don't store the reference after it is destroyed, just like you would need to be careful with in C++. Using the library Unique will help you get this right too if you want to learn that.
Re: class destruction
On Wednesday, 9 September 2015 at 14:36:24 UTC, Q wrote: But if others use my code they must think about finalizing the classes? That is very awkward. classes, yes but structs, no. I'll take a look at Rust and otherwise I will stick with C++. Thanks for you answer. :) Well, resource management is only part of the story.
Re: class destruction
On Wednesday, 9 September 2015 at 14:57:26 UTC, Adam D. Ruppe wrote: On Wednesday, 9 September 2015 at 07:19:58 UTC, Q wrote: Can I be sure that the Handle is destroyed as soon as the class is destroyed? It will do that automatically. Like the others said, you won't be sure when the class is destroyed unless you have the user code take ownership of it. (This btw is the same as C++, just in C++ the ownership syntax is a wee bit shorter.) C++: class Foo { public: Foo() { /* acquire */ } ~Foo() { /* destroy */ } }; void useFoo(Foo* foo) { } // pretend I did the header separation here int main() { Foo foo; useFoo(&foo); return 0; } D: -- module foo; class Foo { private this() { /* acquire */ } ~this() { /* destroy */ } } struct OwnedFoo(F) { F foo; alias foo this; ~this() { .destroy(foo); } @disable this(this) {} } auto create(F, T...)(T t) if(is(F : Foo)) { return OwnedFoo!F(new F(t)); } -- -- module main; import foo; void useFoo(Foo foo) {} void main() { auto foo = create!Foo(); } --- The code is a bit longer since the owned pointer in the standard library isn't exactly what I wanted, but you could use std.typecons.Unique too. (Actually, it does offer a bit more safety in reference escaping than mine. But mine is the closest to C++ default). The private constructor means it won't let you bypass the owned factory when creating it. A bit extra code in foo means all uses of it will be simple like in main. Just don't store the reference after it is destroyed, just like you would need to be careful with in C++. Using the library Unique will help you get this right too if you want to learn that. But in C++, classes and structs are value types _and_ extendable.There is no pressure to heap allocate a class. Of course, if I do that, I use a unique_ptr (shared_ptr is almost everytime misplaced). But since D has a GC and (per default) force to heap allocate a class. So IMO the GC should also destroy it, everything else is just awkward. I don't want to hack around in a new language. I think Rust offers my all I want. But thanks for the explanation.
Re: class destruction
And sorry if that sounds rude, I'm just in a hurry. I just think D is not mature enough for serious stuff. :) That is of course only my personal opinion.
Re: class destruction
On Wednesday, 9 September 2015 at 15:10:33 UTC, Q wrote: But since D has a GC and (per default) force to heap allocate a class. So IMO the GC should also destroy it, everything else is just awkward. Well, it *does* by default. If that's what you want, just do it the plain way with simple `new`. It will be destroyed (and any structs inside also destroyed) when the GC gets it.
Re: class destruction
On Wednesday, 9 September 2015 at 15:19:04 UTC, Adam D. Ruppe wrote: On Wednesday, 9 September 2015 at 15:10:33 UTC, Q wrote: But since D has a GC and (per default) force to heap allocate a class. So IMO the GC should also destroy it, everything else is just awkward. Well, it *does* by default. If that's what you want, just do it the plain way with simple `new`. It will be destroyed (and any structs inside also destroyed) when the GC gets it. I thought that is not guaranteed, according to the docs?
Re: class destruction
On Wednesday, 9 September 2015 at 15:24:57 UTC, Q wrote: I thought that is not guaranteed, according to the docs? It is possible that the GC will never actually run, but you can force it to if you need it to by calling GC.collect at any time.
Re: class destruction
On Wednesday, 9 September 2015 at 15:32:58 UTC, Adam D. Ruppe wrote: On Wednesday, 9 September 2015 at 15:24:57 UTC, Q wrote: I thought that is not guaranteed, according to the docs? It is possible that the GC will never actually run, but you can force it to if you need it to by calling GC.collect at any time. Yes, but according to the specs it is not guaranteed that the GC calls the DTor if the Object is collected.
Re: class destruction
On Wednesday, 9 September 2015 at 15:37:50 UTC, Q wrote: Yes, but according to the specs it is not guaranteed that the GC calls the DTor if the Object is collected. Where? This page says pretty plainly: http://dlang.org/class.html#destructors "The garbage collector calls the destructor function when the object is deleted." (this is arguably a mistake, dtor and finalizer being the same thing have been a problem before, but it does say that) This sentence: "The garbage collector is not guaranteed to run the destructor for all unreferenced objects. " is because the GC is conservative and has a few exceptions in what it covers. But if you aren't one of those exceptions (data segment, or pinned by a false pointer (very rare in 64 bit btw), it will be collected and if it is collected, the dtor is run.
Re: class destruction
On Wednesday, 9 September 2015 at 15:37:50 UTC, Q wrote: Yes, but according to the specs it is not guaranteed that the GC calls the DTor if the Object is collected. Note that I believe this is the same as in Java (though not C#, interestingly enough). In Java an object's destructor is not guaranteed to run.