【发布时间】:2013-05-14 20:22:10
【问题描述】:
我正在重构一个包含大量复制粘贴代码的大型类——我们称之为Big。大部分复制粘贴代码存在于switchcases 中,其中只有涉及的类型最终不同。代码基于类的 enum 成员变量进行切换,其值仅在运行时才知道。
我试图解决这个问题的方法是创建一个Dispatcher 类,该类通过一个名为lookup() 的static 函数查找适当类型的函数。执行实际工作的函数始终称为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));
}
这主要是有效的,除了:
- 它在 Visual C++ 9 (2008) 下可以正常构建和工作,但在 GCC 4.8 下会导致
lookup()的函数模板重载时出现编译错误。 - 它要求为
go()中我们希望支持的每个新的函数模板参数数量编写一个新的函数模板重载lookup()。 - 使用起来既麻烦又混乱。
以下是在 GCC 下发生的错误:
Big.cpp: In static member function 'static Function* Big::Dispatcher<FunctionWrapper, Function>::lookup(Big::RuntimeValue)':
Big.cpp(66,65) : error: expected primary-expression before '>' token
case a: return &FunctionWrapper<a>::go<T>;
^
Big.cpp(66,66) : error: expected primary-expression before ';' token
case a: return &FunctionWrapper<a>::go<T>;
^
Big.cpp(67,65) : error: expected primary-expression before '>' token
case b: return &FunctionWrapper<b>::go<T>;
^
Big.cpp(67,66) : error: expected primary-expression before ';' token
case b: return &FunctionWrapper<b>::go<T>;
^
我的问题有两个:
- 为什么在 GCC 下构建失败,我该如何解决?
- 是否有更好的(即不那么繁琐和混乱的)方法来做到这一点?
代码必须在 Visual C++ 9 (2008) 下可编译,所以我不能使用任何 C++11 特定的代码。
【问题讨论】: