On Thursday, 27 February 2014 at 13:07:37 UTC, Mike Parker wrote:
On 2/27/2014 9:25 PM, Szymon Gatner wrote:
This is just an example but I would think that it is something
rather
important to have... What about child objects un-registering
themselves
in d-tors from a list that parent object holds and parent is
destroyed
first? What about asynchronous completion handler object that
should
notify some other object that it finished/not finished a job
but a
notifee is already destroyed because application is being
shut-down? I
honestly can't imagine how to reason about object graph
lifetimes in GC
world and I am sure I am missing something very basic.
Probably a very
different mindset.
Two simple rules I follow.
#1 Never rely on class destructors for determinstic clean up
#2 Never rely on class destructors for releasing system
resources
I tend to add two methods to classes that need to do any sort
of set up and clean up: initialize and terminate. Then, at app
exit, I make sure that the terminate methods are called either
with scope(exit) or, if I want to log any exceptions that
percolate up to main, try-catch-finally. Something like this:
class TextureManager {
private TexContainer _loadedTextures;
public void initialize() {...}
public void terminate() {
foreach( tex; _loadedTextures[])
tex.terminate();
}
}
struct Game {
@disable this();
@disable this( this );
private static TextureManager _texMan;
public static void initialize() {
_texMan = new TextureManager();
_texMan.initialize();
}
public static void terminate() {
_texMan.terminate();
}
}
void main() {
scope( exit ) Game.terminate();
Game.initialize();
}
In some parts of a system, for objects that tend to be short
lived, I'll use structs with destructors for RAII. A good
example of this is for vertex and fragment shaders. The shader
programs are long-lived, so those are classes, but I always
destroy the shader objects as soon as any programs that need
them are built. So those are perfect candidates for the
struct-based RAII approach.
RAII is a convenient tool, but it's just that, a tool. One of
the tradeoffs you have to make with D's GC is that you can't
use RAII the same way you do in C++. You *can* use it with
structs, but for classes you're going to have to put C++ out of
your head.
Then the question is why not use structs all the time?
After using D for a bit more as a week I come to the solution
that looks like this.
strict WasAClassInCpp
{
@disable this(); //sadly we can not use ctor in D like in C++
:(
@disable this(this); //this is not a copy constructor, this is
something called after all the data in the struct was copied...
~this(){ ... } //dtor, just like in C++
}
So all my classes in C++ are ported to D structs.
I was using class only to port abstract class from C++.
Replacing it by inerface.