我正在重构一个大类 - 我们称之为Big
- 它有大量的复制粘贴代码。这些复制粘贴代码大部分存在于switch
case
中,其中只涉及的类型最终不同。代码基于类的enum
成员变量进行切换,其值仅在运行时已知。
我尝试解决此问题涉及使用Dispatcher
类,通过名为static
的{{1}}函数查找适当类型的函数。执行实际工作的函数始终称为lookup()
,并且必须在包装器类模板中定义(其唯一参数是当前正在打开的运行时go()
值)。 enum
函数本身可能是也可能不是模板函数。
这是代码的简化版本。我为这个长度道歉,但这是我能够做到的那么短,而不会失去重要的背景。
go()
除了:
之外,这大部分都有效#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));
}
的函数模板重载中的编译错误。lookup()
中要支持的每个新功能模板参数编写lookup()
的新函数模板重载。以下是GCC下发生的错误:
go()
我的问题有两个:
代码必须在Visual C ++ 9(2008)下编译,因此我不能使用C ++ 11特定的任何内容。
答案 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;
};