関数を処理するためのマップまたはポインターのテーブルを使用した提案された回答は問題ありません。ただし、2 つの欠点があります。1) 手動で入れ子になったスイッチと比較して、パフォーマンスがわずかに低下します。2) ケース処理メソッドは完全に自己記述的ではありません。つまり、各ハンドル メソッドについて、その定義とマップを開始する場所で 2 回言及する必要があります。
2 つの選択肢があると思います。1) ソース コードの生成。ある種の表現からネストされたスイッチを自動的に生成します。ええと...このような小さなタスクにコード生成を追加することを気にしないのであれば、最適なコードを作成するのは非常に良いオプションです。2) プリプロセッサ ハックの使用。最もエレガントではありませんが、それを機能させるための非常に興味深い方法です。
まず、列挙型の X-Macroを宣言します。
#define ELEMENTS(processor) \
processor(firstElement) \
processor(secondElement) \
processor(thirdElement)
これを使用して、列挙型自体を宣言できます。
#define ENUM_PROCESSOR(arg) arg,
enum class
{
ELEMENTS(ENUM_PROCESSOR)
};
#undef ENUM_PROCESSOR
Now we can add method that uses macros to generate nested switches:
void processElements(const Elements element1,
const Elements element2)
{
// These macros are useful to trick the preprocessor to allow recursive macro calls
// https://github.com/pfultz2/Cloak/wiki/C-Preprocessor-tricks,-tips,-and-idioms
#define EMPTY(...)
#define DEFER(...) __VA_ARGS__ EMPTY()
#define EXPAND(...) __VA_ARGS__
#define ELEMENTS_INT() ELEMENTS
#define PROCESS_NESTED_ENUM_VALUE(value) \
case Elements::value: \
{ \
process<Value1, Elements::value>(); \
break; \
}
#define PROCESS_ENUM_VALUE(value) \
case Elements::value: \
{ \
constexpr Elements Value1 = Elements::value; \
switch (element2) \
{ \
DEFER(ELEMENTS_INT)()(PROCESS_NESTED_ENUM_VALUE) \
}; \
\
break; \
}
switch (element1)
{
EXPAND(ELEMENTS(PROCESS_ENUM_VALUE));
};
#undef EMPTY
#undef DEFER
#undef EXPAND
#undef ELEMENT_TYPES_INT
#undef PROCESS_ENUM_VALUE
#undef PROCESS_NESTED_ENUM_VALUE
}
ここでは、プリプロセッサを「だまして」ELEMENTS を再帰的に展開するために多くの努力が払われています。主なアイデアはここでよく説明されています。
次に、ハンドラーをテンプレート関数の特殊化として宣言します。
template <Elements Element1, Elements Element2>
void process();
template<>
void process<Elements::firstElement, Elements::firstElement>()
{
//some code 1;
}
...