Erik Van Hamme
Erik Van Hamme

Reputation: 33

Enum class bitmasks used in template constexpr method

I have the following C++11 code in my microcontroller project:

template<std::uint32_t... I>
struct mask_or;

template<>
struct mask_or<> {
    static constexpr std::uint32_t value = 0;
};

template<std::uint32_t first, std::uint32_t... rest>
struct mask_or<first, rest...> {
    static constexpr std::uint32_t value = first | mask_or<rest...>::value;
};

This works fine, and allows me to pass a variable amount of uint32_t's as template arguments. The compiler will then OR all of them and substitute each call to a constant value. For the microcontroller, this is ideal because it does not have to do OR operations before assigning to the register.

For some cases, I want to use an enum class like below as values:

enum class gpiopin : std::uint32_t {
    p0 = GPIO_IDR_IDR_0, // 0x00000001
    ...
    p14 = GPIO_IDR_IDR_14, // 0x00004000
    p15 = GPIO_IDR_IDR_15 // 0x00008000
};

As I have multiple of these enum classes, I am looking for a generic way of using the enum class values in the above mask_or code. In summary, I want to be able to do this:

SFR = mask_or<gpiopin::p1, gpiopin::p14>::value;

Ideally, I would like the mask_or<...>::value to be constexpr to keep the code size low and speed high.

I feel like this should be possible, but I cannot seem to make it work. Can anyone help me?

Upvotes: 1

Views: 207

Answers (1)

Daniel Frey
Daniel Frey

Reputation: 56863

You could use something like this:

template<typename E, E... I>
struct mask_or;

template<typename E>
struct mask_or<E> {
    static constexpr E value = E(0);
};

template<typename E, E first, E... rest>
struct mask_or<E, first, rest...> {
    using UT = typename std::underlying_type<E>::type;
    static constexpr E value = E(UT(first) | UT(mask_or<E, rest...>::value));
};

but this is still not optimal, since you now need to add the type as the first parameter:

mask_or<gpiopin, gpiopin::p0, gpiopin::p14>::value

Live example


It would be much easier to simply overload operator|:

template<typename> struct is_bitmask : std::false_type {};

template<typename E>
constexpr
typename std::enable_if<is_bitmask<E>::value, E>::type
operator|( const E lhs, const E rhs )
{
    using UT = typename std::underlying_type<E>::type;
    return E(UT(lhs) | UT(rhs));
}

and register your type for the operator with

template<> struct is_bitmask<gpiopin> : std::true_type {};

with this, you can use

gpiopin::p0 | gpiopin::p14

Live example

Upvotes: 2

Related Questions