2015-06-09 77 views
3

我有一個可變參數引擎模板類:如何獲取可變類模板中的類型的索引?

template <typename ... Components> class Engine; 

我想一個編號分配給這相當於他們的訂貨在編譯時每個組件。

template <typename Component> int ordinal(); 

因此,例如,如果:

Engine<PositionComponent, PhysicsComponent, InputComponent> engine; 

宣佈,該電話:

engine.ordinal<PhysicsComponent>(); 

將返回1,並用類似的呼籲,這將使得下面的電話時返回InputComponent而不是PhysicsComponent將返回2.

是否可能,如果你s,怎麼會這樣呢?

+1

我發現[這個解決方案(http://stackoverflow.com/questions/18063451/get-index-of-a-tuple-elements-type)元組,我敢肯定,你可以適應它爲你的使用情況。 – Borgleader

+0

看起來很有希望。 –

回答

7

所以你想在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; } 

由於構造,試圖讓ComponentordinalComponents...將是一個編譯錯誤。這似乎是適當的。

+0

通過將'index :: value'的結果添加到1非常巧妙的解決方案! – Alejandro

+0

謝謝。這很棒。你知道編譯器是否會優化模板迭代? –

+0

@NickCuthbert它將不得不編譯它,但是'index :: value'是一個編譯時常量 - 這裏沒有任何運行時間的工作。 – Barry

0

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...>(); 
} 

可以已經使用結構,但我覺得這是更清潔的(沒有所有的::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...>(); 
} 
2

我下面的目標是讓事情在編譯時的境界儘可能的。

這是刪除一些樣板的別名。 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 
>{}; 

此別名返回純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時,它返回其類型編碼它的值的值。size<?>有一個constexpr operator size_t()以及一個operator(),所以你可以在大多數期望整數類型無縫地使用它。

您還可以使用:

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

現在ordinal<Component>是表示組件的索引類型,而不是功能。

0

爲了完整起見,我使用了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...>(); 
} 
+0

你缺少'std :: enable_if'中的':: type'部分(即使你有'typename')。 –

+0

當類型查找不在列表中時,編譯錯誤是相當不友好的,我認爲(至少在GCC 6.3.0中)。而且它並沒有提到真正的問題。雖然我不確定這裏顯示的其他解決方案如何在這種情況下。 –