template argument deduction with strongly-typed enumerations

20,200

Solution 1

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

Solution 2

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.

Solution 3

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>;
Share:
20,200
Useless
Author by

Useless

Updated on July 19, 2022

Comments

  • Useless
    Useless almost 2 years

    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));