Битовые маски класса Enum, используемые в шаблоне метода constexpr

У меня есть следующий код C++11 в моем проекте микроконтроллера:

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;
};

Это прекрасно работает и позволяет мне передавать переменное количество uint32_t в качестве аргументов шаблона. Затем компилятор ИЛИ все из них и подставляет каждый вызов в постоянное значение. Для микроконтроллера это идеально, потому что он не должен выполнять операции ИЛИ перед назначением в регистр.

В некоторых случаях я хочу использовать перечислимый класс, как показано ниже:

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

Поскольку у меня есть несколько из этих классов enum, я ищу общий способ использования значений класса enum в приведенном выше коде mask_or. Таким образом, я хочу быть в состоянии сделать это:

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

В идеале я хотел бы, чтобы mask_or<...>:: value было constexpr, чтобы поддерживать низкий размер кода и высокую скорость.

Я чувствую, что это должно быть возможно, но я не могу заставить его работать. Может кто-нибудь мне помочь?

1 ответ

Решение

Вы можете использовать что-то вроде этого:

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));
};

но это все еще не оптимально, так как теперь вам нужно добавить тип в качестве первого параметра:

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

Живой пример


Было бы намного проще просто перегрузить 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));
}

и зарегистрируйте свой тип для оператора с

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

с этим вы можете использовать

gpiopin::p0 | gpiopin::p14

Живой пример

Другие вопросы по тегам