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.

Reply via email to