Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 904
s4eed Avatar asked Sep 12 '25 11:09

s4eed


2 Answers

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.

like image 168
StoryTeller - Unslander Monica Avatar answered Sep 15 '25 00:09

StoryTeller - Unslander Monica


You can use SFINAE to split the enum class implementation:

template <typename Type,
          typename = std::enable_if_t<std::is_enum_v<Type>>>
bool get_bits(Type input, uint8_t offset, uint8_t n, Type* destination) {
    // enum-class implementation
}

and integral implementation:

template <typename Type,
          typename = std::enable_if_t<std::is_integral_v<Type>>>
bool get_bits(Type input, uint8_t offset, uint8_t n, Type* destination) {
    // integral implementation
}

The above is a C++17 version.

For C++11, you would have something like:

template <typename Type,
          typename std::enable_if<std::is_enum<Type>::value>::type* = nullptr>
bool get_bits(Type input, uint8_t offset, uint8_t n, Type* destination) {
    // enum-class implementation
}

template <typename Type,
          typename std::enable_if<std::is_integral<Type>::value>::type* = nullptr>
bool get_bits(Type input, uint8_t offset, uint8_t n, Type* destination) {
    // integral implementation
}
like image 33
NutCracker Avatar answered Sep 15 '25 01:09

NutCracker