私は大規模なクラスをリファクタリングしている最中です-それを呼びましょうBig
-それには大量のコピーアンドペーストコードがあります。このコピー アンド ペースト コードの多くはswitch
case
s に存在し、関係する型のみが異なることになります。enum
コードは、実行時にのみ値がわかっているクラスのメンバー変数に基づいて切り替えています。
これを修正する私の試みは、と呼ばれるDispatcher
関数を介して適切に型指定された関数を検索するクラスを持つことです。実際の作業を行う関数は常に呼び出され、ラッパー クラス テンプレートで定義する必要があります (その唯一のパラメーターは、現在オンになっているランタイム値です)。関数は、テンプレート関数自体である場合とそうでない場合があります。static
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));
}
これは、次の点を除いて、ほとんど機能します。
- Visual C++ 9 (2008) では正常にビルドおよび動作しますが、GCC 4.8 では の関数テンプレート オーバーロードでコンパイル エラーが発生し
lookup()
ます。 lookup()
でサポートしたい新しい数の関数テンプレート パラメーターごとに、新しい関数テンプレート オーバーロードを記述する必要がありますgo()
。- 使い方が面倒でわかりにくいです。
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>;
^
私の質問は 2 つあります。
- これが GCC でのビルドに失敗するのはなぜですか? どうすれば修正できますか?
- これを行うためのより良い (つまり、面倒で混乱の少ない) 方法はありますか?
コードは Visual C++ 9 (2008) でコンパイルできる必要があるため、C++11 固有のものは使用できません。