If I have a normal (weak) enumeration, I can use its enumerated values as non-type template parameters, like so:
enum { Cat, Dog, Horse };
template <int Val, typename T> bool magic(T &t)
{
return magical_traits<Val>::invoke(t);
}
and call it as: magic<Cat>(t)
as far as I can see, if I have a strongly-typed enumeration and don't want to hard-code the enumeration type, I end up with:
enum class Animal { Cat, Dog, Horse };
template <typename EnumClass, EnumClass EnumVal, typename T> bool magic(T &t)
{
return magical_traits<EnumVal>::invoke(t);
}
and now I have to write: magic<Animal, Animal::Cat>(t)
, which seems redundant.
Is there any way to avoid typing out both the enum class and the value, short of
#define MAGIC(E, T) (magic<decltype(E), E>(T));
You can do it like this, if you can use C++17
#include <type_traits>
enum class Animal { Cat, Dog, Horse };
template <typename EnumClass, EnumClass EnumVal>
void magic_impl()
{
static_assert(std::is_same_v<EnumClass, Animal>);
static_assert(EnumVal == Animal::Cat);
}
template <auto EnumVal>
void magic()
{
magic_impl<decltype(EnumVal), EnumVal>();
}
int main()
{
magic<Animal::Cat>();
}
demo:
http://coliru.stacked-crooked.com/a/9ac5095e8434c9da
I'm sorry, I have to tell you that
It is not possible
Take the macro, put it into a scary named header and protect it from your colleague's cleanup script. Hope for the best.
If you're only interested in the enum
's value, and not its type, you should be able to use a constexpr
function to convert the value to an integer, avoiding repeating the type name.
enum class Animal { Cat, Dog, Horse };
template <typename T> constexpr int val(T t)
{
return static_cast<int>(t);
}
template <int Val, typename T> bool magic(T &t)
{
return magical_traits<Val>::invoke(t);
}
magic<val(Animal::Cat)>(t);
However, as pointed out already by others, if you want to make this depend on the type as well, it will not work.
This question has an accepted answer (upvoted).
While refactoring my own code, I figured out a more complete solution:
Step 1: using code I was writing:
template<typename V, typename EnumClass, EnumClass Discriminator>
class strong_type final // type-safe wrapper for input parameters
{
V value;
public:
constexpr explicit strong_type(V x): value{x} {}
constexpr auto get() const { return value; }
};
Step 2: client code:
enum class color { red, green, blue, alpha };
// the part OP was asking about:
template<color C>
using color_channel = strong_type<std::uint8_t, color, C>;
using red = color_channel<color::red>; // single argument here
using green = color_channel<color::green>;
using blue = color_channel<color::blue>;
using alpha = color_channel<color::alpha>;