我有一个带有几个布尔模板参数的函数:
template<bool par1, bool par2, bool par2>
void function(int arg1, int arg2, int arg3);
我想在编译时自动生成(使用任何模板魔法,如果需要,使用C ++ 11)一个表(或类似于C ++元编程的有趣结构中的某些东西)的函数指针到所有组合模板参数par*
的值,以便我可以构造一个函数,它将这些模板参数作为运行时参数并转发到正确的模板实例化:
void runtime_function(bool par1, bool par2, bool par3, int arg1, int arg2, int arg3);
我认为,如果不是模板函数,可以通过模板模板参数来实现这一点,这要归功于模板模板参数:
template<template<bool> class T> class CombinationsOfTemplateParameters;
template<template<bool, bool> class T> class CombinationsOfTemplateParameters;
template<template<bool, bool, bool> class T> class CombinationsOfTemplateParameters;
//and so on, up to some implementation defined hard limit.
但据我所知,没有办法指向通用模板函数,因此未指定其模板参数。因此,我首先不知道如何将其传递给模板参数列表中的某个帮助类。
有没有办法解决这个问题?
答案 0 :(得分:20)
第一步,只是为了理解这个问题,我将为每个实例化构建一个函数指针数组:
template<bool, bool, bool> void function(int, int, int);
typedef void (*func_type)(int, int, int);
func_type funcs[] = {
&function<false, false, false>,
&function<false, false, true>,
&function<false, true, false>,
&function<false, true, true >,
&function<true, false, false>,
&function<true, false, true >,
&function<true, true, false>,
&function<true, true, true >
};
注意这看起来像一个3位二进制数表:
0 0 0 == 0
0 0 1 == 1
0 1 0 == 2
0 1 1 == 3
// etc...
因此,您可以使用按位运算形成的整数索引数组:
void runtime_function(bool par1, bool par2, bool par3, int arg1, int arg2, int arg3)
{
func_type f = funcs[ int(par1)<<2 | int(par2)<<1 | int(par3) ];
f(arg1, arg2, arg3);
};
第二步,既然我已经了解了如何构造数组并使用它,我会使用可变参数模板自动生成数组,而不是手工编写。
首先使用创建整数参数包的类型(使用Johannes Schaub&#39; seq
模板):
template<int ...>
struct seq { };
template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> { };
template<int ...S>
struct gens<0, S...> {
typedef seq<S...> type;
};
然后在包扩展中使用它来生成每个可能的实例化:
template<bool, bool, bool> void function(int, int, int);
typedef void (*func_type)(int, int, int);
template<typename> struct make_table;
template<int... N>
struct make_table<seq<N...>>
{
static const func_type funcs[sizeof...(N)];
};
template<int... N>
const func_type make_table<seq<N...>>::funcs[sizeof...(N)] = {
&function< bool(N&4), bool(N&2), bool(N&1) >...
};
现在您可以这样使用:
void runtime_function(bool par1, bool par2, bool par3, int arg1, int arg2, int arg3)
{
typedef gens<8>::type seq8;
func_type f = make_table<seq8>::funcs[ (par1<<2) | (par2<<1) | par3 ];
f(arg1, arg2, arg3);
}
幻数8
是2的幂(bool参数的数量。)
第三步,测试一下。我相信,如果我的核心逻辑错误,它甚至不会编译,因为所有类型和包扩展都将由编译器检查,但我可能会使得按位操作错误。
#include <iostream>
template<bool b1, bool b2, bool b3>
void function(int i1, int i2, int i3){
std::cout << std::boolalpha << "f<"
<< b1 << ", " << b2 << ", " << b2
<< ">("
<< i1 << ", " << i2 << ", " << i3
<< ")\n";
}
int main()
{
runtime_function(false, true, true, 1, 2, 3);
runtime_function(true, false, false, 4, 5, 6);
}
打印:
f<false, true, true>(1, 2, 3)
f<true, false, false>(4, 5, 6)
要为具有四个bool模板参数的功能模板执行此操作,您需要使用gens<16>
并更改包扩展
template<int... N>
const func_type make_table<seq<N...>>::funcs[] = {
&function< bool(N&8), bool(N&4), bool(N&2), bool(N&1) >...
};
这不是很方便,因此应该可以通过引入另一个整数seq<3,2,1,0>
的参数包并使用它来概括它来处理任意数量的参数:
template<int... N, int... Bits>
const func_type make_table<seq<N...>, seq<Bits...>>::funcs[] = {
&function< /* some bitwise op using N & (1<<Bits) ... */ > ...
};
但这不会起作用,因为我们希望使用Bits
扩展包,但我们不希望它同时扩展N
(并且包装有所不同大小,所以它无论如何都无法工作,所以我们需要使用一个间接级别来允许包单独扩展。
下面的最终版本使用函数gen_func<N>
来获取索引N处的函数指针:
template<unsigned N, int... Mask>
static constexpr func_type gen_func(seq<Mask...>)
{ return &function<(N&(1<<Mask))...>; }
并添加genrevs
以创建一个反向的整数序列seq<2,1,0>
,该序列将传递给该函数以用作Mask
参数包:
gen_func<I>(typename genrevs<NParams>::type()) ...
通过该更改,make_table
类模板可以处理任何arity的函数,因此最后一步是通过函数类型对其进行参数化(并使其推导出参数的数量,并从中推导出可能的数量)函数特化)并向make_table
添加一个访问器以获得正确的函数:
void runtime_function(bool par1, bool par2, bool par3, int arg1, int arg2, int arg3)
{
auto f = make_table<void(int, int, int)>::get(par1, par2, par3);
f(arg1, arg2, arg3);
}
这是完整的最终版本。在昨晚写完这段代码后,我意识到它假定函数参数(int, int, int)
的数量与模板参数<bool, bool, bool>
的数量相同,如果那不正确那么你就是这样需要向make_table
添加额外的非类型模板参数,指定模板参数的数量(在下面的代码中NParams
并推断出来):
#include <type_traits>
template<int ...>
struct seq { };
template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> { };
template<int ...S>
struct gens<0, S...> {
typedef seq<S...> type;
};
template<int N, int ...S>
struct genrevs : genrevs<N-1, S..., N-1> { };
template<int ...S>
struct genrevs<0, S...> {
typedef seq<S...> type;
};
template<bool, bool, bool> void function(int, int, int);
template<unsigned N>
struct pow2
{
static constexpr unsigned value = 2*pow2<N-1>::value;
};
template<> struct pow2<0> { static constexpr unsigned value = 1; };
template<typename Signature> struct make_table_seq;
template<typename Res, typename... Params>
struct make_table_seq<Res(Params...)>
: gens<pow2<sizeof...(Params)>::value>
{ };
template<typename Signature, typename = typename make_table_seq<Signature>::type>
struct make_table;
template<typename Res, typename... Params, int... I>
class make_table<Res(Params...), seq<I...>>
{
static const unsigned NParams = sizeof...(Params);
public:
typedef Res (*func_type)(Params...);
template<typename... Bool>
static typename std::enable_if<sizeof...(Bool)==NParams, func_type>::type
get(Bool... b)
{ return funcs[ shift_or(0, b...) ]; }
private:
template<unsigned N, int... Mask>
static constexpr func_type gen_func(seq<Mask...>)
{ return &function<(bool(N&(1<<Mask)))...>; }
template<typename... Bool>
static int shift_or(int i, bool b0, Bool... b)
{
return shift_or((i<<1) | int(b0), b...);
}
static int shift_or(int i) { return i; }
static const func_type funcs[sizeof...(I)];
};
template<typename Res, typename... Params, int... I>
const typename make_table<Res(Params...), seq<I...>>::func_type
make_table<Res(Params...), seq<I...>>::funcs[] = {
gen_func<I>(typename genrevs<NParams>::type()) ...
};
// specialise for function pointer types as well as function types
template<typename Res, typename... Params>
struct make_table_seq<Res(*)(Params...)>
: make_table_seq<Res(Params...)>
{ };
template<typename Res, typename... Params, typename T>
class make_table<Res(*)(Params...), T>
: make_table<Res(Params...)>
{ };