我有一个枚举列表,定义如下:
enum PinEnum {
kPinInvalid,
kPinA0,
kPinA1,
kPinB0,
kPinB1,
kPinC0,
kPinC1,
}
这些枚举中的每一个都需要与另外两个值相关联,即端口和引脚号。目前,我通过运行时功能访问这些:
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;
}
}
特别是,我这样做是因为我不希望大型查找表在运行时占用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
...
}
C ++ 11答案很好。
虽然还有其他编译时查找表的答案,但我没有看到一个直接适用于这种情况的答案。它们要么需要字符串递归,要么实际计算并存储一个查找表(如果没有别的办法,最终可能会出现这种情况)。
答案 0 :(得分:7)
使用以enum作为参数的模板结构template specialization和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, "");
}
在C ++ 14中,由于relaxed constexpr restrictions,您的switch
函数可能是constexpr。
答案 1 :(得分: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 >
{};
使用:
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");
尝试访问上述系统中的无效引脚会导致编译时错误。
我把所有东西放在类型的土地上,并且一对一的地图。也可以使用多对一地图,或者没有PinEnum_t
包装等。
答案 2 :(得分:2)
我不确定PinGetPort
,因为我们没有GPIO_TypeDef
和GPIOA
等的定义,但PinGetPin
应该如果只是将constexpr
放在它前面,使用C ++ 14编译器并使用高级优化,那么工作正常。像:
constexpr uint16_t PinGetPin(const PinEnum pin) {
在C ++ 11编译器上,你可能会得到类似的东西:
constexpr uint16_t PinGetPin(const PinEnum pin) {
return ((pin == kPinA0) || (pin == kPinB0)) ? GPIO_PIN_0 :
(((pin == kPinA1) || (pin == kPinB1)) ? GPIO_PIN_1 : 0);
}
但是,正如你所看到的,它变得很难看......
答案 3 :(得分:-3)
您可以使用C数组来定义端口和引脚。然后使用枚举值在数组中访问它们。这使用了默认枚举值是连续的这一事实。当然,这不是编译时间。