动态调度到模板函数 C++

Mik*_*x7f 5 c++ templates c++11

我有一个模板函数(在我的例子中是一个 cuda 内核),其中有少量布尔模板参数可以在运行时选择。我很高兴在编译时实例化所有排列并动态调度,就像这样(对于布尔值 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这写起来很烦人,如果我最终得到 b3 和 b4,情况会变得更糟。

有没有一种简单的方法可以在 C++11/14 中以更简洁的方式重写它而不引入大型外部库(如 boost)?就像是:

const auto dispatcher = construct_dispatcher<bool, 3>(myFunc);
Run Code Online (Sandbox Code Playgroud)

...

dispatcher(b0,b1,b2,otherArgs,args);
Run Code Online (Sandbox Code Playgroud)

Yak*_*ont 5

没问题。

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{}... );
  }
};
Run Code Online (Sandbox Code Playgroud)

活生生的例子

kbool变量也是 如此,代表编译时常量布尔值。dispatch_bools是一个辅助结构,具有operator().

operator()需要一组运行bool时,并从开始max-1产生最大的 if/else 分支,每个分支递归到调用dispatch_bools,并计算出另一个编译时 bool 。

这会生成 2^max 代码;正是您不想编写的代码。

延续一直向下传递到底部递归(其中max=0)。此时,所有编译时布尔值都已构建完毕——我们将continuation::operator()这些编译时布尔值作为函数参数传递。

希望continuation::operator()是一个可以接受编译时布尔值的模板函数。如果是,则有 2^max 个实例,每个实例都有 2^max 种可能的真/假组合。


中的问题,您只需执行以下操作:

std::array<bool, 3> bargs={{b0, b1, b2}};
dispatch_bools<3>{}(bargs, [&](auto...Bargs){
  myFunc<decltype(Bargs)::value...,otherArgs>(args);
});
Run Code Online (Sandbox Code Playgroud)

这很容易,因为autolambda;它可以operator()在 lambda 上有一个模板。将这些编译时布尔参数转换回模板非类型参数很容易。

请注意,许多名义上的编译器都支持自动 lambda,因为它非常简单。但是,如果缺少它,您仍然可以在中使用辅助结构来解决此问题:

template<class OtherArgs>
struct callMyFunc {
  Args args;
  template<class...Bools>
  void operator()(Bools...){
    myFunc<Bools::value...,otherArgs>(args);
  }
};
Run Code Online (Sandbox Code Playgroud)

现在使用的是:

std::array<bool, 3> bargs={{b0, b1, b2}};
dispatch_bools<3>{}(bargs, callMyFunc<otherArgs>{args});
Run Code Online (Sandbox Code Playgroud)

这基本上是手动编写 lambda 的功能。


中,您可以替换voidauto并返回,而不仅仅是递归,它会很好地为您推导出返回类型。

如果您想要中的该功能,您可以编写大量decltype代码,也可以使用以下宏:

#define RETURNS(...) \
  noexcept(noexcept(__VA_ARGS__)) \
  -> decltype(__VA_ARGS__) \
  { return __VA_ARGS__; }
Run Code Online (Sandbox Code Playgroud)

并写出类似的正文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{}... )
)
Run Code Online (Sandbox Code Playgroud)

与专业化类似,并在<0>中获得样式的返回扣除。

RETURNS使得推断单行函数的返回类型变得微不足道。