I have a class like this:
class Component1 {...};
class Component2 {...};
class Component3 {...};
class Entity
{
Component1 c1;
Component2 c2;
Component3 c3;
public:
Component1& get_c1() { return c1;}
Component2& get_c2() { return c2;}
Component3& get_c3() { return c3;}
};
Basically the Entity is a container of all possible types of components (with other stuff, too). My problem is that I have more than 15 different components and I don't like to copy&paste lines this way. I'm looking for something like:
myEntity.get<Component1>();
to obtain the component I need. I took a look at boost::tuple which is cool but it allows access using an integer as key. I could use a public static const integer in each Component* class and gain access like this:
myEntity.get<Component1::id>();
but then I have to make sure to use different ids for each component and that's bad for mantainance.
Is there a way to "map" a type to a value of that type using magic (i.e. templates), so that
myEntity.get<Component1>()
works as expected?
I'd also like to have O(1) access to a component since the myEntity::get<T>
is used very often (not that with 15-20 components makes sense talking about complexity anyway) but that's not mandatory.
It might be possible to use a CRTP-based solution.
Note that I aven't actually tried this, but I think it should work. However, spamming
get()
functions like this typically shows that, well, your class design is really kind of poor.In many situation, this haves drawbacks, but in you case, may be a "selective decay" can suffice:
right now, you can use * as a "component selector" as
If you make your components available to everyone and their dog anyways, why not simply make them public? Mission accomplished without copy&paste.
I very nearly asked the same question: for me
boost::fusion::map
orboost::fusion::set
are overkill and I really don't like the extra-long template parameter lists and having to set a macro if I have more than 10 in my container. I went with something like this:Look at using typeindex and typeid. You could add components to a map by template type with its typeid being the map key. You can then get components from the map by type.
You could do it like this:
This does a linear lookup but that's O(n) in compile-time (actually in terms of template instantiations) only; it's O(1) in runtime so perhaps that's acceptable to you. Note that some compilers have O(n) template lookup so you may end up in O(n^2) compile-time; I believe C++11 will require that compilers do constant-time template lookup. You can also avoid some instantiations by not eagerly instantiating the recursion, e.g. using Boost.MPL. I avoided this for brevity and clarity.
The above relies on advanced features of Boost Tuple which aren't available for
std::tuple
(C++11). However I believe it wouldn't be too hard to implementlookup
in C++11 using variadic templates (left as an exercise to the reader ;). You'd avoid the eager instantiation without using Boost.MPL, too.Other remarks:
get
. I suppose you could still use them as individual members, and use a tie-tuple insideEntity::get
to return the proper reference. This would come at a small cost to maintenance (changeEntity::get
everytime you add/remove a component). This also left as an exercise to the reader (don't forget to take into account that the new keys will be of the formComponent&
!).