Search code examples
c++metaprogrammingboost-hana

Change runtime research for a compile time one


I'm trying to implement a generic ECS library in C++ for learning purpose. I was thinking about a lot of way to implement things but I always run into a problem. So if you could help me with this one :

Let say I have a constexpr hana::tuple of hana::type_c Components, something like :

struct C1 {};
struct C2 {};
struct C3 {};

constexpr auto components = hana::to_tuple(hana::tuple_t<C1, C2, C3>);

And now I have a component storage type, which is not a problem here, so let's call it Storage (the type differ for each component):

struct Storage {};

I want to link each component or each component group, with their Storage type. So the easy way is to do something like that:

constexpr auto component_storage = hana::make_tuple(
hana::make_pair(hana::to_tuple(hana::tuple_t<C1, C2>), type_c<Storage>),
hana::make_pair(hana::to_tuple(hana::tuple_t<C3>), type_c<Storage>)
);

But the problem now is runtime. If I initialize that tuple but with the real Storage and no longer type_c<Storage>, I'll have to loop through the tuple to find the Storage that I need. All of this at runtime no? And this is really bad, my last version had something like Component::getStorage() and it was free (but more restrictive).

So the question is : how can I manage to have some getStorage<Component>() function which will cost nothing at runtime? Well by nothing I mean just return the reference of the Storage.

EDIT: The only way I have think so far is quite simple (sounds like a good point).

Pseudo-Code

struct LinkedStorage {

  hana::tuple<...>            storages;
  hana::tuple<hana::pair...>  index;
};

At lest something like:

constexpr auto components = hana::to_tuple(hana::tuple_t<C1, C2, C3>);
constexpr auto storage = hana::to_tuple(hana::tuple_t<Storage, Storage>);
constexpr auto index = hana::make_tuple(
hana::make_pair(hana::to_tuple(hana::tuple_t<C1>, 0),
hana::make_pair(hana::to_tuple(hana::tuple_t<C2, C3>, 1)
);

Like that I should be able to found the index at compile time and just access the right element at runtime. But I'm new at metaprogramming, so I guess someone could make something far better.


Solution

  • First of all, no need to use to_tuple(tuple_t<...>); you can just use tuple_t<...>. Now, I think what you actually want to do (since you seem to need runtime storage, which makes sense) is:

    // "map" of a set of types to a storage of some type
    using StorageMap = hana::tuple<
      hana::pair<hana::tuple<hana::type<C1>, hana::type<C2>>, StorageA>,
      hana::pair<hana::tuple<hana::type<C3>>, StorageB>
    >;
    // Actual object that contains the runtime storage (and the free mapping between types)
    StorageMap map;
    

    Now, you can implement your getStorage<Component>() function like this:

    template <typename Component>
    decltype(auto) getStorage() {
      auto found = index_if(map, [](auto const& pair) {
        return hana::contains(hana::first(pair), hana::type<Component>{});
      });
      return hana::second(hana::at(map, found));
    }
    

    where index_if is a trivial variant of the function presented in this answer that would work on an arbitrary predicate instead of a specific element. This functionality will be added to Hana when I get some free time (see related ticket).