I had the very same problem and I'm using a composite helper struct for this purpose:

struct DefaultCtor {}; //call default ctor type

struct composite(T)
{
  static assert(is(T == class),"can only composite classes");
  void[__traits(classInstanceSize, T)] _classMemory = void;
  bool m_destructed = false;
  debug {
    T _instance;
  }
  else
  {
    @property T _instance()
    {
      return cast(T)_classMemory.ptr;
    }

    @property const(T) _instance() const
    {
      return cast(const(T))_classMemory.ptr;
    }
  }

  alias _instance this;

  @disable this();
  @disable this(this); //prevent evil stuff from happening

  this(DefaultCtor c){
  };

void construct(ARGS...)(ARGS args) //TODO fix: workaround because constructor can not be a template
  {
    _classMemory[] = typeid(T).init[];
    T result = (cast(T)_classMemory.ptr);
    static if(is(typeof(result.__ctor(args))))
    {
      result.__ctor(args);
    }
    else
    {
      static assert(args.length == 0 && !is(typeof(T.__ctor)),
                    "Don't know how to initialize an object of type "
~ T.stringof ~ " with arguments:\n" ~ ARGS.stringof ~ "\nAvailable ctors:\n" ~ ListAvailableCtors!T() );
    }
    debug _instance = result;
  }

  void destruct()
  {
    assert(!m_destructed);
    Destruct(_instance);
    debug _instance = null;
    m_destructed = true;
  }

  ~this()
  {
    if(!m_destructed)
    {
      Destruct(_instance);
      m_destructed = true;
    }
  }
}


I and I use it often (especially with containers, I always tend to forget newing containers, which can not happen with this helper struct because of @disable this();

Usage is something like the following:

class Foo
{
  int m_i;
  this()
  {
    m_i = 5;
  }

  this(int i)
  {
    m_i = i;
  }
}

class Bar
{
  composite!Foo m_foo;

  this()
  {
    m_foo = typeof(m_foo)();
    m_foo.construct(DefaultCtor()); // would call Foo.this()
    // alternative
    m_foo.construct(5); // would call Foo.this(int)
  }
}

I wrote this before constructors of structs could be templated, I plan on updating it so you can write

m_foo = typeof(m_foo)(5);

The m_destructed member is kind of optional, if the default destruction order is ok, you can omit it and save the additional bytes of overhead it imposes. A advantage is also that Foo is a normal class, you can inherit from it and you don't have to write any boilerplate code to use the value type of Foo.

Reply via email to