A Basic Solution For my_enum
A template template is normally the way to go for this kind of thing. Unfortunately, those only exist for types, and not for non-type parameters like function pointers.
But, turn those functions into functors:
template<typename T>
struct func1;
template<>
struct func1<A> {
void operator ()() {
std::cout << "func 1 A";
}
};
/* etc */
And you're good to go:
template <template <typename T> class F, typename... Args>
void dispatch(my_enum me, Args&&... args) {
switch (me) {
case my_enum::A: F<A>{}(std::forward<Args>(args)...); break;
case my_enum::B: F<B>{}(std::forward<Args>(args)...); break;
case my_enum::C: F<C>{}(std::forward<Args>(args)...);
}
}
usage:
my_enum me = my_enum::A;
dispatch<func1>(me);
std::cout << '\n';
dispatch<func2>(me);
demo: https://godbolt.org/z/c6jPxa
Generalizing A Bit Further
If you're willing to get "clever", you can generalize dispatch too. Define some helper stubs:
template <typename TEnum, TEnum... Es>
struct Tags {};
template <typename... Ts>
struct Types {};
template <typename TTags, typename TTypes>
struct Dispatch;
Tags and Types hold the enums, and types you want to test:
template <template <typename T> class F>
using dispatch = typename Dispatch<
Tags<my_enum, my_enum::A, my_enum::B, my_enum::C>, // the tags
Types<A, B, C> // their respective types
>::type<F>;
Then, you can generate that switch block by combining an initializer list, the comma operator, and the ternary operator.
template <typename TEnum, TEnum... ETags, typename... TTypes>
struct Dispatch<Tags<TEnum, ETags...>, Types<TTypes...>> {
template <template <typename T> class F>
struct type {
template <typename... Args>
void operator()(TEnum e, Args&&... args)
{
(void)std::initializer_list<bool> {
e == ETags ? (F<TTypes>{}(std::forward<Args>(args)...), false) : false...
};
}
};
};
Usage:
template <template <typename T> class F>
auto dispatch = typename Dispatch<
Tags<my_enum::A, my_enum::B, my_enum::C>,
Types<A, B, C>
>::template type<F>{};
void wrapper() {
my_enum me = my_enum::A;
dispatch<func1>(me);
std::cout << '\n';
dispatch<func2>(me);
}
Demo: https://godbolt.org/z/Edhbrr
For Functions With Return Types
If you want your functors to return a value, it's a little trickier because you need a default return value if none of the tags match. This solution calls F<void>{}(...) in that case by recursively testing one argument and calling a dispatcher with the remaining arguments.
template <typename TEnum, TEnum ETag, TEnum... ETags, typename TType, typename... TTypes>
struct Dispatch<Tags<TEnum, ETag, ETags...>, Types<TType, TTypes...>> {
template <template <typename T> class F>
struct type {
template <typename... Args>
auto operator()(TEnum e, Args&&... args) -> decltype(F<TType>{}(std::forward<Args>(args)...))
{
return e == ETag
? F<TType>{}(std::forward<Args>(args)...)
: typename Dispatch<Tags<TEnum, ETags...>, Types<TTypes...>>::template type<F>{}(e, std::forward<Args>(args)...);
}
};
};
template <typename TEnum>
struct Dispatch<Tags<TEnum>, Types<>> {
template <template <typename T> class F>
struct type {
template <typename... Args>
auto operator()(TEnum e, Args&&... args) -> decltype(F<void>{}(std::forward<Args>(args)...))
{
return F<void>{}(std::forward<Args>(args)...);
}
};
};
Demo: https://godbolt.org/z/qz6r4T