Thread overview |
---|
September 23, 2016 Meta-programming: iterating over a container with different types | ||||
---|---|---|---|---|
| ||||
It's more a general meta-programming question than a specific D stuff. For an entity-component engine, I am trying to do some run-time composition: registering a certain type (component) to a structure (entity). I would like to know how I can iterate an entity and get the different type instances registered to it. Here is a simple example to clarify: class Entity { void register!Component(Component val); void unregister!Component(); Component getComponent!Component(); //iterating over the components (?!??) void opApply(blabla); } 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 // the code below is wrong, but how can I make that work?? foreach (c; e) { writeln(c); // it would display magically 42, "loire" and 3.14 // and c would have the correct type at each step } } |
September 23, 2016 Re: Meta-programming: iterating over a container with different types | ||||
---|---|---|---|---|
| ||||
Posted in reply to Claude | On Friday, 23 September 2016 at 09:21:56 UTC, Claude wrote: > ... // Maybe you can try using std.variant? import std.variant; alias Component = Variant; class Entity { void register (Component v) { components ~= v; } void unregister (T) () { foreach (i, c; components) if (c.type == typeid(T)) components = components[0..i] ~ components[i+1 .. $]; } Component getComponent (T) () { foreach (i, c; components) if (c.type == typeid(T)) return components[i]; } Component[] components; // iterating over the components alias components this; // or you can provide an inputRange interface by implementing // // bool empty () {} // Component front () {} // void popFront () {} // // to support whatever type of backing data structure you have. // (see http://ddili.org/ders/d.en/ranges.html) } unittest { import std.stdio; Entity e = new Entity(); e.register(Component(42)); e.register(Component("loire")); e.register(Component(3.14)); foreach (c; e) write(c, " "); writeln; // Prints 42 "loire" 3.14 e.unregister!string; foreach (c; e) write(c, " "); writeln; // Prints 42 3.14 e.unregister!double; foreach (c; e) write(c, " "); writeln; // Prints 42 e.unregister!int; assert(e.components.length == 0); } |
October 20, 2016 Re: Meta-programming: iterating over a container with different types | ||||
---|---|---|---|---|
| ||||
Posted in reply to deed | 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(); } |
Copyright © 1999-2021 by the D Language Foundation