On Friday, 23 September 2016 at 12:55:42 UTC, deed wrote:
// Maybe you can try using std.variant?


Thanks for your answer.
However I cannot use variants, as I have to store the components natively in a void[] array (for cache coherency reasons).

So I found a way to solve that problem: delegate callbacks.
There may be more elegant solutions but well, it works.

Basically I register some kind of accessor delegate of the form:

void accessor(Entity e, Component* c)
{
// Do stuff, like save the component struct for that entity in a file
}

And it is stored in the entity class in an array of delegates:

  void delegate(Entity e, void* c);


Here's a very basic implementation:

class Entity
{
public:
  void register!Component(Component val);
  void unregister!Component();
  Component getComponent!Component();

  alias CompDg = void delegate(Entity e, void* c);

void accessor!Component(void delegate(Entity e, Component* c) dg) @property
  {
    auto compId = getComponentId!Component;
    mCompDg[compId] = cast(CompDg)dg;
  }


  // Iterating over the components
  void iterate()
  {
    // For every possible components
    foreach (compId; 0..mMaxNbComponents)
      if (isRegistered(compId))
        if (mCompDg[compId] !is null)
          mCompDg[compId](this, getComponentStoragePtr(compId));
  }

private:
  void* getComponentStoragePtr(uint compId);
  bool isRegistered(uint compId);

void[] mComponentStorage; // Flat contiguous storage of all components
  CompDg[] mCompDg;
  // ...
}

unittest
{
  // registering
  auto e = new Entity;
  e.register!int(42);
  e.register!string("loire");
  e.register!float(3.14);

  assert(e.getComponent!float() == 3.14); // that is OK

e.accessor!int = (Entity et, int i) { writefln("%d", i); }; e.accessor!string = (Entity et, string s) { writefln("%s", s); }; e.accessor!float = (Entity et, float f) { writefln("%f", f); };

  // Print the values
  e.iterate();
}

Reply via email to