枚举的编译时查找表

dev*_*vtk 4 c++ c++11

我有一个枚举列表,定义如下:

enum PinEnum {
    kPinInvalid,
    kPinA0,
    kPinA1,
    kPinB0,
    kPinB1,
    kPinC0,
    kPinC1,
}
Run Code Online (Sandbox Code Playgroud)

这些枚举中的每一个都需要与另外两个值相关联,即端口和引脚号.目前,我通过运行时函数访问这些:

GPIO_TypeDef * PinGetPort(const PinEnum pin) {
    switch (pin) {
        case kPinA0:
        case kPinA1:
            return GPIOA;
        case kPinB0:
        case kPinB1:
            return GPIOB;
        case kPinC0:
        case kPinC1:
            return GPIOC;
        default:
            return NULL;
    }
}

uint16_t PinGetPin(const PinEnum pin) {
    switch (pin) {
        case kPinA0:
        case kPinB0:
        case kPinC0:
            return GPIO_Pin_0;
        case kPinA1:
        case kPinB1:
        case kPinC1:
            return GPIO_Pin_1;
        default:
            return 0;
    }
}
Run Code Online (Sandbox Code Playgroud)

特别是,我这样做是因为我不希望大型查找表在运行时占用RAM(代码大小不是问题).

有没有办法使用编译时查找表,constexpr函数或模板构造来执行此操作,以便语句PinGetPin(kPinA0)PinGetPort(kPinA0)每个语句优化为单个值,而不必经历冗长的函数调用和case语句?这些函数的参数将始终为类型const PinEnum,其值在编译时已知.

例如,典型的使用场景如下:

const PinEnum kPinStatus = kPinB0;

int main(int argc, char ** argv) {
    ...
    PinGetPort(kPinStatus)->BSRRH = PinGetPin(kPinStatus);
    // GPIOB->BSRRH = GPIO_Pin_0; <-- should optimize to this during compilation
    ...
}
Run Code Online (Sandbox Code Playgroud)

C++ 11答案很好.

虽然编译时查找表还有其他答案,但我没有看到直接应用于此案例的答案.它们要么需要字符串递归,要么实际计算并存储一个查找表(如果没有别的办法,这可能最终会出现).

Vit*_*meo 7

使用模板结构将枚举作为参数,模板特化std::integral_constant.

#include <type_traits>

enum class pin { pin0, pin1 };

template<pin> struct lookup_port;    
template<pin> struct lookup_num;

template<> struct lookup_port<pin::pin0> 
  : std::integral_constant<int, 0> { };

template<> struct lookup_num<pin::pin0> 
  : std::integral_constant<int, 520> { };

template<> struct lookup_port<pin::pin1> 
  : std::integral_constant<int, 22> { };

template<> struct lookup_num<pin::pin1> 
  : std::integral_constant<int, 5440> { };

int main()
{
    static_assert(lookup_port<pin::pin0>::value == 0, "");
    static_assert(lookup_port<pin::pin1>::value == 22, "");

    static_assert(lookup_num<pin::pin0>::value == 520, "");
    static_assert(lookup_num<pin::pin1>::value == 5440, "");
}
Run Code Online (Sandbox Code Playgroud)

在C++ 14中,switch由于轻松的constexpr限制,您的功能可能是constexpr .


Yak*_*ont 5

做一张桌子:

template<class...>struct types {};
template<class lhs, class rhs>struct e{};
template<class types, class lhs>
struct find{};
template<class types, class lhs>
using find_t=typename find<types,lhs>::type;

template<class T0, class R0, class...Ts>
struct find< types<e<T0,R0>,Ts...>, T0>{
  using type=R0;
};
template<class T0, class R0, class...Ts, class lhs>
struct find< types<e<T0,R0>,Ts...>, lhs>:
  find< types<Ts...>, lhs >
{};
Run Code Online (Sandbox Code Playgroud)

使用:

template<PinEnum e>
using PinEnum_t = std::integral_constant<PinEnum, e>;
template<uint16_t i>
using uint16 = std::integral_constant<uint16_t, i>;

using PinGetPin_t = types<
  e<PinEnum_t<kPinA0>, uint16<GPIOA>>,
  e<PinEnum_t<kPinA1>, uint16<GPIOA>>,
  // ...
  e<PinEnum_t<kPinC1>, uint16<GPIOC>>
>;

static_assert( find_t<PinGetPin_t, PinEnum_t<kPinA0>>{}==GPIOA, "oops");
Run Code Online (Sandbox Code Playgroud)

尝试访问上述系统中的无效引脚会导致编译时错误.

实例.

我把所有东西都放在了类型的土地上,并且一对一的地图.多对一地图,或没有PinEnum_t包装等,也是可能的.