Add method specialization for all enumeration classes in C++

296 Views Asked by At

I have the following methods (broadly speaking) in my bitstream class:

class BitStream
{
    void StoreBits( unsigned int data, unsigned int numBits );
    
    public:
    template<typename T>
    void WriteType( T value ) { StoreBits( value, sizeof(T) * 8 ) );

    template<>
    void WriteType( float value ) { StoreBits( *(unsigned int*)&value, sizeof(float) * 8 ) );
};

Someone has thrown a strongly type enum class at WriteType, and obviously the enum does not automatically convert to an unsigned integer.

What can I do to write a specialization that handles all enumeration cases using the same function naming?

This is a trivialization of the problem - I have enum class instances being thrown at a number of other BitStream member methods that I have to handle, such as ReadType, StreamType (read or write), SmallestRangeWrite<Min,Max>() etc.

I have C++17 support available. I know there is std::is_enum, but how can I specialize on that here, especially at compile time?

Could I somehow write:

template<typename T>inline void BitStream::WriteType( T value ) 
{
  if (type_is_enum())
    StoreEnum<T>(value)
  else
    StoreBits( value, sizeof(T) * 8 ) );
}

I need to ensure that the StoreBits method is not compiled when T is an enumeration. Simply avoiding it at runtime with a branch isn't enough.

2

There are 2 best solutions below

6
On BEST ANSWER

Test to see if the type is an enum and if it's not convertible to u32. This allows you to distinguish between normal enum and enum class. If it's an enum but is not convertible to u32, then it's an enum class:

template<typename T>
inline void BitStream::WriteType(T value) 
{
    if constexpr (std::is_enum_v<T> && !std::is_convertible_v<T, u32>)
        // enum class
        StoreEnum<T>(value)
    else
       // normal enum or other type that is convertible to u32
        StoreBits(value, sizeof(T) * 8 ));
}

Since your StoreEnum and StoreBits function templates can't take both, you need to use if constexpr here to avoid the type check when compiling.

3
On

The best feature of C++17 (that is my personal opinion) is constexpr if. That means that you don't need to define explicit specializations. That would work:

template<typename T>inline void BitStream::WriteType(T value) 
{
    if constexpr (std::is_enum_v<T>)
        StoreEnum<T>(value);
    else
        StoreBits(value, sizeof(T) * 8);
}

Using constexpr if is important as you run into the risk of the compilation failure if either StoreEnum or StoreBits are undefined for the type T.