The proposed answers with map or table of pointers to handle functions are ok. But I see two downsides:
1) A small performance decrease comparing to the manual nested switches.
2) Case handling methods aren't fully self-descriptive. I mean you have to mention each handle methods twice - in its' definition and in the place where you init the map.
I see two alternative options:
1) Source code generation. Automatically generate nested switches from some kind of representation. Well... it's very good option to create optimal code, if don't mind adding code generation for such a small task.
2) Using preprocessor hacks. Not the most elegant but quite interest way to make it work.
First we declare X-Macro for our enum:
#define ELEMENTS(processor) \
processor(firstElement) \
processor(secondElement) \
processor(thirdElement)
We can use it to declare the enum itself:
#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
}
A lot of efforts here are made to "trick" preprocessor to expand ELEMENTS recursively. The main idea is well described here.
Now we declare our handlers as template function specialization:
template <Elements Element1, Elements Element2>
void process();
template<>
void process<Elements::firstElement, Elements::firstElement>()
{
//some code 1;
}
...