c++templatessfinaestatic-castenum-class

In a template function, How do I use std::underlying_type just if type of the input is enum class?


I have a piece of code that returns the value of some bits of a given number (I counted enum classes as a number too, using a static_cast).

template<typename Type>
bool get_bits(Type input, uint8_t offset, uint8_t n, Type* destination)
{
    if (offset + n> sizeof(Type) * 8)
        return false;

    Type bitmask = 0;

    for (int i = 0; i < n; ++i)
        bitmask |= (1 << i);

    *destination = static_cast<Type>(input >> offset & bitmask);
    return true;
}

This function tries to return the value of n bits of input beginning from the offset. It works fine for integral types but when I try to use it with enum classes, compilation fails. I tried to use std::underlying_type and then it worked for enum classes but not for integral types :|. How can I use it for both of them? I mean if the type is enum class, I want to cast the input to its underlying_type, do some bitwise operations, and then store the result (using static_cast) to the destination. But these casts should not be done for integral types that don't have underlying_type.


Solution

  • If you need it often, it's possible to write an adapter over std::underlying_type. Something like this

    namespace detail {
      template<typename T, bool = std::is_enum<T>::value>
      struct underlying_type { using type = T; };
    
      template<typename T>
      struct underlying_type<T, true> : ::std::underlying_type<T> {};
    }
    

    When you use detail::underlying_type<T> substitution occurs into the default argument too. When the supplied type is not an enumeration, the primary template matches the arguments (implicitly), and the exposed type is T.

    When the supplied type is an enumeration (and only then), the specialization gets used. And all it does is forward to the standard trait.