Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile-time lookup table for enum

Tags:

c++

c++11

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).

like image 570
devtk Avatar asked Oct 27 '15 17:10

devtk


People also ask

Are enums evaluated at compile time?

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.

Is enum compile time?

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.

What is a lookup table in C?

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.


1 Answers

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.

like image 114
Vittorio Romeo Avatar answered Oct 05 '22 19:10

Vittorio Romeo