I have a list of enums which are defined as follows:
enum PinEnum {
kPinInvalid,
kPinA0,
kPinA1,
kPinB0,
kPinB1,
kPinC0,
kPinC1,
}
Each of these enums needs to be associated with two other values, the port and the pin number. Currently, I'm accessing these through run-time functions:
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;
}
}
In particular, I'm doing this because I do not want a large lookup table to be taking up RAM at runtime (code size is much less of an issue).
Is there a way to do this using a compile-time lookup table, constexpr
function, or a template construct so that the statements PinGetPin(kPinA0)
and PinGetPort(kPinA0)
each get optimized to a single value instead of having to go through a lengthy function call and case statement? The arguments to these functions will always be of type const PinEnum
with values known at compile time.
For example, a typical usage scenario is the following:
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 answers are fine.
While there are other answers out there for compile-time lookup tables, I do not see one which would directly apply to this case. They either require string recursion, or actually calculate and store a lookup table (which this may end up coming to if there's no other way).
Enums allow us to define or declare a collection of related values that can be numbers or strings as a set of named constants. Unlike some of the types available in TypeScript, enums are preprocessed and are not tested at compile time or runtime.
Enum values or Enum instances are public, static, and final in Java. They are a compile-time constant, which means you can not changes values of Enum instances and further assignment will result in a compile-time error.
Well a lookup table is simply an initialized array that contains precalculated information. They are typically used to avoid performing complex (and hence time consuming) calculations. For example, it is well known that the speed of CRC calculations may be significantly increased by use of a lookup table.
Use template structs taking enum as a parameter, template specialization, and 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, "");
}
In C++14, your switch
function could be constexpr, thanks to relaxed constexpr restrictions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With