2014-09-04 115 views
5

我想'生成'一個函數指針的跳轉表。指向的功能有兩種類型的模板。對於兩個類型列表中的每個可能的對,應該有一個不同的函數實例化。理想情況下,我們可能會碰到這樣的:展開不同長度的參數包

#include <tuple> 

template <typename X, typename Y> 
void foo() 
{} 

template <typename... Xs, typename... Ys> 
void bar(const std::tuple<Xs...>&, const std::tuple<Ys...>&) 
{ 
    using fun_ptr_type = void (*) (void); 
    static constexpr fun_ptr_type jump_table[sizeof...(Xs) * sizeof...(Ys)] 
    = {&foo<Xs, Ys>...}; 
} 

int main() 
{ 
    using tuple0 = std::tuple<int, char, double>; 
    using tuple1 = std::tuple<float, unsigned long>; 

    bar(tuple0{}, tuple1{}); 
} 

正如預期的那樣,它無法在元組有不同的長度:

foo.cc:15:20: error: pack expansion contains parameter packs 'Xs' and 'Ys' that have different lengths (3 vs. 2) 
    = {&foo<Xs, Ys>...}; 
      ~~ ~~^
foo.cc:23:3: note: in instantiation of function template specialization 'bar<int, char, double, float, unsigned long>' requested here 
    bar(tuple0{}, tuple1{}); 
^
1 error generated. 

爲了實現這種功能,我已經嘗試過,並用indirection成功(一第一個跳轉表,其中包含指向另一個跳轉表的函數的指針),但我覺得它很笨拙。

所以,我的問題是:有沒有解決方法呢?

回答

4

即使在編譯時(即sizeof ...(Xs)== sizeof ...(Ys)),您的示例代碼也是錯誤的。假設你有N元組元組,那麼jump_table有N *

首先,你需要內部加入2所列出的類型:

template<class A, class B> 
struct P; 

template<class... Ts> 
struct L {}; 

template<class T, class... Ts> 
using mul = L<P<T, Ts>...>; 

template<class...> 
struct cat; 

template<class T> 
struct cat<T> 
{ 
    using type = T; 
}; 

template<class... As, class... Bs> 
struct cat<L<As...>, L<Bs...>> 
{ 
    using type = L<As..., Bs...>; 
}; 

template<class A, class B, class... Ts> 
struct cat<A, B, Ts...> 
{ 
    using type = typename cat<typename cat<A, B>::type, Ts...>::type; 
}; 

template<class A, class B> 
struct join; 

template<class... As, class... Bs> 
struct join<L<As...>, L<Bs...>> 
{ 
    using type = typename cat<mul<As, Bs...>...>::type; 
}; 

例如,

join<L<int[1], int[2]>, L<float[1], float[2], float[3]>>::type 

給你

L<P<int[1], float[1]>, P<int[1], float[2]>, P<int[1], float[3]>, P<int[2], float[1]>, P<int[2], float[2]>, P<int[2], float[3]> 

回到你的例子:

template <typename X, typename Y> 
void foo() 
{} 

template<class T, std::size_t N> 
struct jump_table 
{ 
    template<class... As, class... Bs> 
    constexpr jump_table(L<P<As, Bs>...>) 
     : table{&foo<As, Bs>...} 
    {} 

    T table[N]; 
}; 

template <typename... Xs, typename... Ys> 
void bar(const std::tuple<Xs...>&, const std::tuple<Ys...>&) 
{ 
    using fun_ptr_type = void (*) (void); 
    static constexpr jump_table<fun_ptr_type, sizeof...(Xs) * sizeof...(Ys)> table 
    = {typename join<L<Xs...>, L<Ys...>>::type()}; 
} 

int main() 
{ 
    using tuple0 = std::tuple<int, char, double>; 
    using tuple1 = std::tuple<float, unsigned long>; 

    bar(tuple0{}, tuple1{}); 
} 

這應該做你期望的。

+0

謝謝,它非常簡單,列表操作部分很容易重用。 – 2014-09-04 16:35:30

+0

使用'template struct L {using type = L;};'和''繼承,以及使用cat_t = typename cat :: type;'aliases'的一些'template 可以使列表內容變得更加流暢。 [見這裏](http://ideone.com/hI4Ohu)。 – Yakk 2014-09-04 19:13:09

1

你擁有的實際上更像是兩個列表(<X1,Y1>,<X2,Y2>,...)的「zip」,當列表長度不同時它不起作用。

要計算兩者的「產品」,我認爲你必須使用助手類來使它工作。看到像你這樣的其他問題:How to create the Cartesian product of a type list?

2

我對產品擴展context(f<Xs, Ys>...) /* not what we want */的正常解決方案是將其重寫爲context2(g<Xs, Ys...>...)。這意味着g負責擴大Ys相對於某些X,最終擴展執行g所有Xs。這種重寫的結果是我們引入了額外的嵌套,因此引入了不同的上下文。

在我們的情況下,而不是一個平面數組函數指針,我們將有一個數組函數指針數組。 不同的是,您嘗試的解決方案實際上是我們關心的&foo<X, Y>函數指針,而扁平化很簡單。

#include <cassert> 
#include <utility> 
#include <array> 

template<typename X, typename Y> 
void foo() {} 

using foo_type = void(*)(); 

template<typename... T> 
struct list { 
    static constexpr auto size = sizeof...(T); 
}; 

template<typename X, typename Y, typename Indices = std::make_index_sequence<X::size * Y::size>> 
struct dispatch; 

template< 
    template<typename...> class XList, typename... Xs 
    , template<typename...> class YList, typename... Ys 
    , std::size_t... Indices 
> 
struct dispatch<XList<Xs...>, YList<Ys...>, std::index_sequence<Indices...>> { 
private: 
    static constexpr auto stride = sizeof...(Ys); 
    using inner_type = std::array<foo_type, stride>; 
    using multi_type = inner_type[sizeof...(Xs)]; 

    template<typename X, typename... Yss> 
    static constexpr inner_type inner() 
    { return {{ &foo<X, Yss>... }}; } 

    static constexpr multi_type multi_value = { 
     inner<Xs, Ys...>()... 
    }; 

public: 
    static constexpr auto size = sizeof...(Xs) * sizeof...(Ys); 
    static constexpr foo_type value[size] = { 
     multi_value[Indices/stride][Indices % stride]... 
    }; 
}; 

template< 
    template<typename...> class XList, typename... Xs 
    , template<typename...> class YList, typename... Ys 
    , std::size_t... Indices 
> 
constexpr foo_type dispatch<XList<Xs...>, YList<Ys...>, std::index_sequence<Indices...>>::value[size]; 

int main() 
{ 
    using dispatch_t = dispatch< 
      list<int, char, double>, 
      list<float, unsigned long> 
     >; 

    constexpr auto&& table = dispatch_t::value; 

    static_assert(dispatch_t::size == 6, ""); 
    static_assert(table[0] == &foo<int, float>, ""); 
    static_assert(table[1] == &foo<int, unsigned long>, ""); 
    static_assert(table[2] == &foo<char, float>, ""); 
    static_assert(table[3] == &foo<char, unsigned long>, ""); 
    static_assert(table[4] == &foo<double, float>, ""); 
    static_assert(table[5] == &foo<double, unsigned long>, ""); 
} 

Coliru demo

+0

謝謝你的回答。但是,我很難理解'main()'之前的聲明。它看起來像一個明確的實例,因爲如果我刪除它,編譯器會抱怨相應的缺失符號。另外,我不明白編譯器如何在'value [size]'中看到'dispatch'的大小? – 2014-09-04 09:43:50

+1

@AlexandreHamez它不是一個實例,而是一個定義。 [這是它看起來像](http://coliru.stacked-crooked.com/a/c7b51e3b7be6a62e)非模板類。由於'dispatch'是一個類模板,我們必須在我們的例子中添加一些樣板。這樣的定義從不需要(對於'constexpr'靜態數據成員),但編譯器仍然可以要求它。我不會要求它。 – 2014-09-04 13:40:36

3

這裏的其他答案對於手頭的問題似乎太複雜了。下面是我該怎麼做:

#include <array> 
#include <tuple> 

template <typename X, typename Y> void foo() {} 

using fun_ptr_type = void (*) (void); 

// Build one level of the table. 
template <typename X, typename ...Ys> 
constexpr std::array<fun_ptr_type, sizeof...(Ys)> 
    jump_table_inner = {{&foo<X, Ys>...}}; 

// Type doesn't matter, we're just declaring a primary template that we're 
// about to partially specialize. 
template <typename X, typename Y> void *jump_table; 

// Build the complete table. 
template <typename ...Xs, typename ...Ys> 
constexpr std::array<std::array<fun_ptr_type, sizeof...(Ys)>, sizeof...(Xs)> 
    jump_table<std::tuple<Xs...>, std::tuple<Ys...>> = {jump_table_inner<Xs, Ys...>...}; 

int main() { 
    using tuple0 = std::tuple<int, char, double>; 
    using tuple1 = std::tuple<float, unsigned long>; 

    // Call function for (int, float). 
    jump_table<tuple0, tuple1>[0][0](); 
} 

這是Clang 3.5在其C++ 14模式接受。

+0

在提出這個問題之前,我嘗試了類似的方法,但它不能用clang 3.4或g ++ 4.9進行編譯......但是,我同意這是一個更簡單的解決方案。 – 2014-09-06 08:25:12

+0

我反對你描述我的答案爲「太複雜」,看到我們正在做同樣的事情。我通過類模板專業化來做到這一點,這是爲了教學目的 - 我努力使其他人可以嘗試Coliru上的代碼,以便即使在我們的編譯器不同時,他們也可以理解我的答案。顯然,由代碼組成的簡短答案也有其優點,但我認爲副手的說法不合適。 – 2014-09-07 05:27:02

+1

我並不反對你的解決方案具有更多的教育價值;它暴露更多的技術,並在更多情況下工作。但是,這正是對於手頭問題而言過於複雜的原因。 – 2014-09-15 19:56:12