如何在可变 class 模板中获取类型的索引?
How can I get the index of a type in a variadic class template?
我有一个可变引擎模板class:
template <typename ... Components> class Engine;
我想在编译时为每个组件分配一个编号,这相当于它们的顺序。这将在进行以下调用时 returned:
template <typename Component> int ordinal();
例如如果:
Engine<PositionComponent, PhysicsComponent, InputComponent> engine;
已声明,调用:
engine.ordinal<PhysicsComponent>();
会 return 1 而使用 InputComponent 而不是 PhysicsComponent 的类似调用会 return 2.
有可能吗?如果有的话,应该怎么办?
在 Boost.Mp11 中,这是一个简短的单行代码(一如既往):
template <typename... Components>
struct Engine {
template <typename Component>
static constexpr int ordinal() {
return mp_find<Engine, Component>::value;
}
};
请注意,如果 Component
不存在,这将是 return sizeof...(Components)
。如果需要,您可以添加静态断言来验证这一点。
我原来的答案在首屏...
所以你想在 Components...
中找到 Component
的索引?
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>
{ };
用法:
template <typename Component>
size_t ordinal() { return index<Component, Components...>::value; }
按照构造,尝试获取不在 Components...
中的 Component
的 ordinal
将是一个编译错误。这似乎很合适。
未测试:
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...>();
}
我可以使用结构,但我发现它更简洁(没有所有 ::type
丑陋之处)。
如果您希望在找不到类型时出现编译时错误,请将 ordinal
更改为:
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...>();
}
我下面的目标是尽可能将事物保留在编译时领域。
这是删除一些样板文件的别名。 std::integral_constant
是一种很棒的 std
类型,它存储编译时确定的整数类型:
template<std::size_t I>
using size=std::integral_constant<std::size_t, I>;
接下来是一个index_of
类型,还有一个稍微好用一点的index_of_t
:
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
>{};
这个别名 returns 是一个纯粹的 std::integral_constant
,而不是从它继承的类型:
template<class T, class...Ts>
using index_of_t = size< index_of<T, types<Ts...>>::value >;
最后是我们的函数:
template <class Component>
static constexpr index_of_t<Component, Components...>
ordinal() const {return{};}
它不仅是 constexpr
,它 returns 是一个将其值编码为类型的值。 size<?>
有一个 constexpr operator size_t()
和一个 operator()
,因此您可以在大多数需要无缝整数类型的地方使用它。
您还可以使用:
template<class Component>
using ordinal = index_of_t<Component, Components...>;
现在ordinal<Component>
是表示组件索引的类型,而不是函数。
为了完整起见,我添加了这个,它利用了 C++11 的 constexpr 功能和一些 stl 函数。我觉得它比其他解决方案更干净。
//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...>();
}
我有一个可变引擎模板class:
template <typename ... Components> class Engine;
我想在编译时为每个组件分配一个编号,这相当于它们的顺序。这将在进行以下调用时 returned:
template <typename Component> int ordinal();
例如如果:
Engine<PositionComponent, PhysicsComponent, InputComponent> engine;
已声明,调用:
engine.ordinal<PhysicsComponent>();
会 return 1 而使用 InputComponent 而不是 PhysicsComponent 的类似调用会 return 2.
有可能吗?如果有的话,应该怎么办?
在 Boost.Mp11 中,这是一个简短的单行代码(一如既往):
template <typename... Components>
struct Engine {
template <typename Component>
static constexpr int ordinal() {
return mp_find<Engine, Component>::value;
}
};
请注意,如果 Component
不存在,这将是 return sizeof...(Components)
。如果需要,您可以添加静态断言来验证这一点。
我原来的答案在首屏...
所以你想在 Components...
中找到 Component
的索引?
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>
{ };
用法:
template <typename Component>
size_t ordinal() { return index<Component, Components...>::value; }
按照构造,尝试获取不在 Components...
中的 Component
的 ordinal
将是一个编译错误。这似乎很合适。
未测试:
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...>();
}
我可以使用结构,但我发现它更简洁(没有所有 ::type
丑陋之处)。
如果您希望在找不到类型时出现编译时错误,请将 ordinal
更改为:
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...>();
}
我下面的目标是尽可能将事物保留在编译时领域。
这是删除一些样板文件的别名。 std::integral_constant
是一种很棒的 std
类型,它存储编译时确定的整数类型:
template<std::size_t I>
using size=std::integral_constant<std::size_t, I>;
接下来是一个index_of
类型,还有一个稍微好用一点的index_of_t
:
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
>{};
这个别名 returns 是一个纯粹的 std::integral_constant
,而不是从它继承的类型:
template<class T, class...Ts>
using index_of_t = size< index_of<T, types<Ts...>>::value >;
最后是我们的函数:
template <class Component>
static constexpr index_of_t<Component, Components...>
ordinal() const {return{};}
它不仅是 constexpr
,它 returns 是一个将其值编码为类型的值。 size<?>
有一个 constexpr operator size_t()
和一个 operator()
,因此您可以在大多数需要无缝整数类型的地方使用它。
您还可以使用:
template<class Component>
using ordinal = index_of_t<Component, Components...>;
现在ordinal<Component>
是表示组件索引的类型,而不是函数。
为了完整起见,我添加了这个,它利用了 C++11 的 constexpr 功能和一些 stl 函数。我觉得它比其他解决方案更干净。
//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...>();
}