带有模板参数的C ++函数调度

时间:2013-05-14 20:22:10

标签: c++ templates dispatch

我正在重构一个大类 - 我们称之为Big - 它有大量的复制粘贴代码。这些复制粘贴代码大部分存在于switch case中,其中只涉及的类型最终不同。代码基于类的enum成员变量进行切换,其值仅在运行时已知。

我尝试解决此问题涉及使用Dispatcher类,通过名为static的{​​{1}}函数查找适当类型的函数。执行实际工作的函数始终称为lookup(),并且必须在包装器类模板中定义(其唯一参数是当前正在打开的运行时go()值)。 enum函数本身可能是也可能不是模板函数。

这是代码的简化版本。我为这个长度道歉,但这是我能够做到的那么短,而不会失去重要的背景。

go()

除了:

之外,这大部分都有效
  1. 它在Visual C ++ 9(2008)下构建并正常工作,但在GCC 4.8下,它导致#include <cassert> class Big { public: enum RuntimeValue { a, b }; Big(RuntimeValue rv) : _rv(rv) { } bool equals(int i1, int i2) { return Dispatcher<Equals, bool(int, int)>::lookup(_rv)(i1, i2); } template<typename T> bool isConvertibleTo(int i) { return Dispatcher<IsConvertibleTo, bool(int)>::lookup<T>(_rv)(i); } private: template<RuntimeValue RV> struct Equals { static bool go(int i1, int i2) { // Pretend that this is some complicated code that relies on RV // being a compile-time constant. return i1 == i2; } }; template<RuntimeValue RV> struct IsConvertibleTo { template<typename T> static bool go(int i) { // Pretend that this is some complicated code that relies on RV // being a compile-time constant. return static_cast<T>(i) == i; } }; template<template<RuntimeValue> class FunctionWrapper, typename Function> struct Dispatcher { static Function * lookup(RuntimeValue rv) { switch (rv) { case a: return &FunctionWrapper<a>::go; case b: return &FunctionWrapper<b>::go; default: assert(false); return 0; } } template<typename T> static Function * lookup(RuntimeValue rv) { switch (rv) { case a: return &FunctionWrapper<a>::go<T>; case b: return &FunctionWrapper<b>::go<T>; default: assert(false); return 0; } } // And so on as needed... template<typename T1, typename T2> static Function * lookup(RuntimeValue rv); }; RuntimeValue _rv; }; int main() { Big big(Big::a); assert(big.equals(3, 3)); assert(big.isConvertibleTo<char>(123)); } 的函数模板重载中的编译错误。
  2. 要求为lookup()中要支持的每个新功能模板参数编写lookup()的新函数模板重载。
  3. 使用起来既麻烦又令人困惑。
  4. 以下是GCC下发生的错误:

    go()

    我的问题有两个:

    1. 为什么在GCC下无法构建,我该如何解决?
    2. 有没有更好的(即不那么麻烦和混乱)的方式呢?
    3. 代码必须在Visual C ++ 9(2008)下编译,因此我不能使用C ++ 11特定的任何内容。

2 个答案:

答案 0 :(得分:6)

由于go是模板的从属名称,因此您需要使用template消歧器:

case a: return &FunctionWrapper<a>::template go<T>;
//                                  ^^^^^^^^
case b: return &FunctionWrapper<b>::template go<T>;
//                                  ^^^^^^^^

这告诉编译器解析范围解析运算符(::)后面的内容作为模板的名称,后续的尖括号作为模板参数的分隔符。

  

为什么在GCC下无法构建,我该如何解决?

因为GCC符合标准,并且执行two-phase name lookup,而MSVC将名称查找延迟到实例化时间,因此知道go是模板的名称。

在实例化之前,此信息不可用,因为无法知道T是什么,主模板可以专门用于给定T,因此go不是template成员函数模板的名称,而不是数据成员。

这就是说,我希望MSVC无论如何都支持{{1}}消歧器,所以添加它应该使你的程序在GCC / Clang / any-conforms-to-the Standard和MSVC上编译。

答案 1 :(得分:0)

我最近写了一个命令调度程序:

File Name = 
Procedure = sander_class::sander_class
Description = This test is to verify that constructor is called. Req Tested:  67060-SWINTR-73

使用它的样子:

#include <map>

// because std::invoke is not in this compiler version.
#define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember))

template <class MyType, class cmd_type, class ret_type, typename... Args>
class CommandDispatcher {
    typedef ret_type (MyType::*CommandFunction)(Args... args);
public:
    // create using static/existing map
    CommandDispatcher(std::map<cmd_type, CommandFunction>& cmd_map) : _command_table(cmd_map) {}

    ret_type operator()(MyType& my_obj, cmd_type cmd, Args... args) 
    {
        int retval = 0;

        if (_command_table.find(cmd) == _command_table.end()) {
            std::cerr << "No command implementation found: " << cmd << endl;
            return -EINVAL;
        }
        return CALL_MEMBER_FN(my_obj, _command_table[cmd])(args...);
    }
private:
    std::map<cmd_type, CommandFunction>& _command_table;
};

在cpp中:

class MyClass {
public:
    MyClass() : _dispatcher(_command_map) {}
private:
    static std::map<int,  CommandFunction> _command_map;
    CommandDispatcher<MyClass, int, int, const char*, int> _dispatcher;
};