How can I get the index of a type in a variation class template?

I have a Variadic Engine template class:

template <typename ... Components> class Engine;

      

I would like to assign a number to each component at compile time, which is equivalent to their ordering. This will be returned when the next call is made:

template <typename Component> int ordinal();

      

So, for example, if:

Engine<PositionComponent, PhysicsComponent, InputComponent> engine;

      

call:

engine.ordinal<PhysicsComponent>();

      

will return 1, and a similar call with InputComponent instead of PhysicalComponent will return 2.

Is it possible, and if so, how would it be done?

+3


source to share


4 answers


So you want to find the index Component

in Components...

?

template <typename... >
struct index;

// found it
template <typename T, typename... R>
struct index<T, T, R...>
: std::integral_constant<size_t, 0>
{ };

// still looking
template <typename T, typename F, typename... R>
struct index<T, F, R...>
: std::integral_constant<size_t, 1 + index<T,R...>::value>
{ };

      

Using:



template <typename Component> 
size_t ordinal() { return index<Component, Components...>::value; }

      

As built, trying to get ordinal

for Component

not in Components...

is a compilation error. Seems appropriate.

+7


source


My goal is to keep things compile time as much as possible.

This is an alias for removing some templates. std::integral_constant

is a wonderful type std

that stores a specific compile-time integer type:

template<std::size_t I>
using size=std::integral_constant<std::size_t, I>;

      

Further, the type index_of

and index_of_t

, which is somewhat easier to use:

template<class...>struct types{using type=types;};
template<class T, class Types>struct index_of{};
template<class T, class...Ts>
struct index_of<T, types<T, Ts...>>:size<0>{};
template<class T, class T0, class...Ts>
struct index_of<T, types<T0, Ts...>>:size<
  index_of<T,types<Ts...>>::value +1
>{};

      

This alias returns pure std::integral_constant

instead of a type that inherits it:

template<class T, class...Ts>
using index_of_t = size< index_of<T, types<Ts...>>::value >;

      



Finally, our function:

template <class Component>
static constexpr index_of_t<Component, Components...>
ordinal() const {return{};}

      

this not only constexpr

but also returns a value that encodes its value in its type. size<?>

has constexpr operator size_t()

as well operator()

, so you can use it at most points that integer types are expected.

You can also use:

template<class Component>
using ordinal = index_of_t<Component, Components...>;

      

and now it ordinal<Component>

is a type representing the index of the component, not a function.

+2


source


UNTESTED:

template <int, typename>
constexpr int index_of() { return -1; } // type not found

template <int N, typename Component, typename Cur, typename... Components>
constexpr int index_of() {
    return std::is_same<Component, Cur>::value ? N : index_of<N+1, Component, Components...>();
}

template <typename... Components>
template <typename Component>
constexpr int engine<Components...>::ordinal() {
    return index_of<0, Component, Components...>();
}

      

I could use structs, but I find it much cleaner (without all the ::type

ugliness).

If you need a compile-time error when the type is not found, change ordinal

to:

template <typename... Components>
template <typename Component>
constexpr int engine<Components...>::ordinal() {
    static_assert(index_of<0, Component, Components...>()!=-1, "invalid component");
     return index_of<0, Component, Components...>();
}

      

0


source


I'm adding this for completeness, it uses the C ++ 11 constexpr functionality and a few stl functions. I feel like this is a little cleaner than other solutions.

//Same type
template <typename Target,typename T,typename ...Rest>
constexpr typename std::enable_if<std::is_same<Target,T>::value, size_t>
_ordinal(){
    return 0;
}

//Different types
template <typename Target,typename T,typename ...Rest>
constexpr typename std::enable_if<!std::is_same<Target,T>::value, size_t>
_ordinal(){
    return 1+_ordinal<Target,Rest...>();
}

      

0


source







All Articles