【问题标题】:Execute integer templated function based on run-time parameter根据运行时参数执行整数模板函数
【发布时间】:2016-10-09 10:58:12
【问题描述】:

我经常有一些基于某种设计方法生成输出的原型行为。我模板化了设计方法,它可以实现我需要的许多功能。但是,有时设计方法是在运行时给出的,所以我通常需要编写一个巨大的 switch 语句。它通常看起来像这样:

enum class Operation
{
    A, B
};


template<Operation O>
    void execute();

template<>
    void execute<A>()
    {
        // ...
    }

template<>
    void execute<B>()
    {
        // ...
    }

void execute(Operation o)
{
    switch (o)
    {
    case Operation::A: return execute<Operation::A>();
    case Operation::B: return execute<Operation::B>();
    }
}

我很好奇是否有人为这个系统找到了一个很好的模式——这种方法的主要缺点是,如果实现了新的枚举,必须输入所有支持的枚举并在几个地方进行维护。

e:我应该补充一点,弄乱编译时模板的原因是允许编译器内联 HPC 中的方法以及继承 constexpr 属性。

e2:实际上,我想我要问的是让编译器使用隐式开关结构生成所有可能的代码路径。也许是一些递归模板魔法?

【问题讨论】:

  • 使用继承和多态怎么样?
  • 正如我刚刚添加的,编译器能够在编译时进行内联和优化是非常重要的(即整个代码结构是可见的和确定的)。否则是的,虚函数显然可以解决问题。

标签: c++ switch-statement metaprogramming


【解决方案1】:

如果你真的想为这个任务使用模板,你可以使用类似于this one的技术。

// Here second template argument default to the first enum value
template<Operation o, Operation currentOp = Operation::A>
// We use SFINAE here. If o is not equal to currentOp compiler will ignore this function.
auto execute() -> std::enable_if<o == currentOp, void>::type
{
    execute<currentOp>();
}

// Again, SFINAE technique. Compiler will stop search if the template above has been instantiated and will ignore this one. But in other case this template will be used and it will try to call next handler.
template<Operation o, Operation currentOp = Operation::A>
void execute()
{
    return execute<o, static_cast<Operation>(static_cast<int>(currentOp) + 1)(c);
}

【讨论】:

  • 有趣(顺便说一句,链接坏了),我去看看
  • 我已经修复了链接。
【解决方案2】:
template<class F, std::size_t...Is>
void magic_switch( std::size_t N, F&& f, std::index_sequence<Is...> ){
  auto* pf = std::addressof(f);
  using pF=decltype(pf);
  using table_ptr = void(*)(pF);
  static const table_ptr table[]={
    [](pF){ std::forward<F>(*pf)( std::integral_constant<std::size_t, Is>{} ); }...
  };
  return table[N]( pf );
}
template<std::size_t Count, class F>
void magic_switch( std::size_t N, F&& f ){
  return magic_switch( N, std::forward<F>(f), std::make_index_sequence<Count>{} );
}

这会生成一个跳转表,它在编译时常量上调用 lambda,根据运行时常量选择哪个条目。这与 switch case 语句的编译方式非常相似。

void execute(Operation o) {
  magic_switch<2>( std::size_t(o), [](auto I){
    execute<Operation(I)>();
  } );
}

修改它以返回非 void 是可能的,但所有分支必须返回相同的类型。

【讨论】:

    猜你喜欢
    • 2019-12-13
    • 2020-11-02
    • 1970-01-01
    • 2019-12-12
    • 1970-01-01
    • 2012-06-16
    • 2011-05-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多