问题描述
我有一个模板函数(在我的情况下是cuda内核),在运行时可以在其中选择少量布尔模板参数.我很高兴在编译时实例化所有排列并动态调度,就像这样(对于布尔b0,b1,b2):
I have a template function (in my case a cuda kernel), where there are a small number of boolean template parameters that can chosen between at runtime. I am happy to instantiate all permutations at compile time and dispatch dynamically, like so (for boolean b0,b1,b2):
if (b0) {
if (b1) {
if (b2) {
myFunc<true,true,true,otherArgs>(args);
} else {
myFunc<true,true,false,otherArgs>(args);
}
} else {
if(b2) {
myFunc<true,false,true,otherArgs>(args);
} else {
myFunc<true,false,false,otherArgs>(args);
}
}
} else {
if(b1) {
if(b2) {
myFunc<false,true,true,otherArgs>(args);
} else {
myFunc<false,true,false,otherArgs>(args);
}
} else {
if(b2) {
myFunc<false,false,true,otherArgs>(args);
} else {
myFunc<false,false,false,otherArgs>(args);
}
}
}
这很烦人,如果我最终得到b3和b4,则会成倍恶化.
This is annoying to write, and gets exponentially worse if I end up with a b3 and b4.
是否有一种简单的方法可以在C ++ 11/14中以更简洁的方式重写此代码,而无需引入大型外部库(例如boost)?像这样:
Is there a simple way to rewrite this in a more concise way in C++11/14 without bringing in large external libraries (like boost)? Something like:
const auto dispatcher = construct_dispatcher<bool, 3>(myFunc);
...
dispatcher(b0,b1,b2,otherArgs,args);
推荐答案
没问题.
template<bool b>
using kbool = std::integral_constant<bool, b>;
template<std::size_t max>
struct dispatch_bools {
template<std::size_t N, class F, class...Bools>
void operator()( std::array<bool, N> const& input, F&& continuation, Bools... )
{
if (input[max-1])
dispatch_bools<max-1>{}( input, continuation, kbool<true>{}, Bools{}... );
else
dispatch_bools<max-1>{}( input, continuation, kbool<false>{}, Bools{}... );
}
};
template<>
struct dispatch_bools<0> {
template<std::size_t N, class F, class...Bools>
void operator()( std::array<bool, N> const& input, F&& continuation, Bools... )
{
continuation( Bools{}... );
}
};
在线示例.
所以 kbool
是一个变量,它表示一个编译时间常数布尔值. dispatch_bools
是一个具有 operator()
的帮助程序结构.
So kbool
is a variable with represents a compile time constant boolean. dispatch_bools
is a helper struct that has an operator()
.
此 operator()
接受运行时 bool
s的数组,并从 max-1
开始继续生成最大if/else分支,每次递归调用 dispatch_bools
时,都会计算出一个编译时布尔值.
This operator()
takes an array of runtime bool
s, and starting at max-1
proceeds to spawn max if/else branches, each recursing into call to dispatch_bools
with one more compile-time bool calculated.
这将生成2 ^ max代码;正是您不想编写的代码.
This generates 2^max code; exactly the code you don't want to write.
连续性一直向下传递到底部递归(其中 max = 0
).到那时,所有的编译时布尔都已经建立起来了,我们称之为 continuation :: operator()
,将这些编译时布尔作为函数参数传入.
The continuation is passed all the way down to the bottom recursion (where max=0
). At that point, all of the compile-time bools have been built up -- we call continuation::operator()
passing in those compile-time bools as function parameters.
希望 continuation :: operator()
是一个模板函数,可以接受编译时布尔.如果是,则有2 ^ max个实例化,每个实例都有2 ^ max个可能的真/假组合中的每一个.
Hopefully continuation::operator()
is a template function that can accept compile-time bools. If it is, there are 2^max instantiations of it, each with each of the 2^max possible true/false combinations.
要使用它来解决您在 c ++ 14 您只需执行以下操作:
To use this to solve your problem in c++14 you just do:
std::array<bool, 3> bargs={{b0, b1, b2}};
dispatch_bools<3>{}(bargs, [&](auto...Bargs){
myFunc<decltype(Bargs)::value...,otherArgs>(args);
});
这很容易,因为c++ 14 具有 auto
lambdas;它可以在lambda上具有模板 operator()
.将这些编译时布尔参数转换回模板非类型参数很容易.
This is easy because c++14 has auto
lambdas; it can have a template operator()
on a lambda. Turning those compile-time bool arguments back into template non-type arguments is easy.
请注意,许多名义上c++ 11 编译器支持自动lambda,因为它很容易.但是,如果缺少它,您仍然可以在 c ++ 11 以及一个辅助结构:
Note that many nominally c++11 compilers support auto-lambdas, because of how easy it was. However, if you lack it, you can still solve this in c++11 with a helper struct:
template<class OtherArgs>
struct callMyFunc {
Args args;
template<class...Bools>
void operator()(Bools...){
myFunc<Bools::value...,otherArgs>(args);
}
};
现在使用的是:
std::array<bool, 3> bargs={{b0, b1, b2}};
dispatch_bools<3>{}(bargs, callMyFunc<otherArgs>{args});
这基本上是手动编写 c ++ 14 lambda可以做到.
This is basically manually writing what the c++14 lambda would do.
在c ++ 14您可以将 void
替换为 auto
并返回而不仅仅是递归,这将为您合理地推断出返回类型.
In c++14 you can replace void
with auto
and return instead of just recursing and it will deduce a return type for you reasonably well.
如果您要在 c ++ 11 ,您可以编写很多 decltype
代码,也可以使用以下宏:
If you want that feature in c++11 you can either write a lot of decltype
code, or you can use this macro:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
并编写 dispatch_bools
的正文,例如:
template<class T, std::size_t N, class F, class...Bools>
auto operator()( std::array<T, N> const& input, F&& continuation, Bools... )
RETURNS(
(input[max-1])?
dispatch_bools<max-1>{}( input, continutation, kbool<true>{}, Bools{}... )
:
dispatch_bools<max-1>{}( input, continutation, kbool<false>{}, Bools{}... )
)
以及类似的< 0>
专长,并获得 c ++ 14 样式的返回值扣除=显示标记为'c ++ 11'的问题""rel =" tag> c ++ 11 .
and similar for the <0>
specialization, and get c++14 style return deduction in c++11.
RETURNS
使得推论单线函数的返回类型变得很简单.
RETURNS
makes deducing return types of one-liner functions trivial.
这篇关于动态分派到模板函数C ++的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!