I'm toying with a general Bitmask class, based on Type-safe Bitmasks in C++, and here is a minimum example highligthning my issue (Compiler Explorer link here):
#include <type_traits>
#include <cstdint>
#include <iostream>
template<class EnumType,
// Ensure that Bitmask can only be used with enums
typename = std::enable_if_t<std::is_enum_v<EnumType>>>
class Bitmask
{
// Type to store bitmask. Should possibly be bigger
using underlying_type = std::underlying_type_t<EnumType>;
public:
constexpr Bitmask(EnumType option) : m_mask(bitmaskValue(option))
{std::cout << "Bitmask " << (int)m_mask << "\n";}
private:
// m_mask holds the underlying value, e.g. 2 to the power of enum value underlying_type
m_mask{0};
static constexpr underlying_type bitmaskValue(EnumType o)
{ return 1 << static_cast<underlying_type>(o); }
explicit constexpr Bitmask(underlying_type o) : m_mask(o) {}
};
enum class Option : uint8_t
{
a, b, c, d, e, f, g, h, i
};
enum class Option2 : int
{
a, b, c, d, e, f, g, h, i
};
int main()
{
Bitmask<Option> b1{Option::a};
Bitmask<Option> b2{Option::h};
Bitmask<Option> b3{Option::i};
Bitmask<Option2> b4{Option2::i};
}
// Output
Bitmask 1
Bitmask 128
Bitmask 0
Bitmask 256
In short, the Bitmask type works fine as long as the underlying type has more bits than the highest enum value used. If not, the bitmaskValue function will overflow and not give desired result, as shown in output, where b3 gets a value of 0, not 256.
I of course understand that an uint8_t cannot store more than 8 different bits, so what I want is some way to make this a compiler error, either when declaring Bitmask<Option>, or when instatiating a Bitmask with a too high value.
I tried changing the bitmaskValue method to:
static constexpr underlying_type bitmaskValue(EnumType o) {
if constexpr (std::numeric_limits<underlying_type>::digits >= static_cast<underlying_type>(o)) {
return 1 << static_cast<underlying_type>(o);
} else {
// some error
}
}
...but then I get the error that 'o' is not a constant expression.
Can anyone helpt me in the correct direction here? For the record, our project is currently using gcc 9.2/c++17, though I hope we can soon upgrade to gcc 11.1/c++20.
Thanks.