2014-09-04 80 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