Compile-time lookup table for listing

I have a list of listings that are defined as follows:

enum PinEnum {
    kPinInvalid,
    kPinA0,
    kPinA1,
    kPinB0,
    kPinB1,
    kPinC0,
    kPinC1,
}

Each of these enumerations must be associated with two other values: port and pin number. I am currently accessing these functions at runtime:

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 do this because I do not want the large lookup table to occupy RAM at run time (the code size is much smaller).

Is there a way to do this using the time search table compilation, function constexpror design template that operators PinGetPin(kPinA0)and PinGetPort(kPinA0)received optimization to a single value instead of the need to go a long function call and register business? Arguments for these functions will always be of type const PinEnumwith values ​​known at compile time.

, :

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

++ 11 .

, , . , ( , ).

+4
4

structs, enum , 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, "");
}

++ 14 switch constexpr, constexpr.

+6

:

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

+4

PinGetPort, GPIO_TypeDef GPIOA et al, PinGetPin , constexpr ++ 14 . :

constexpr uint16_t PinGetPin(const PinEnum pin) {

++ 11 - :

constexpr uint16_t PinGetPin(const PinEnum pin) {
    return ((pin == kPinA0) || (pin == kPinB0)) ? GPIO_PIN_0 : 
        (((pin == kPinA1) || (pin == kPinB1)) ? GPIO_PIN_1 : 0);
}

, , ...

+2

You can use C arrays to determine the port and output. Then use the enum value to access them in the array. This exploits the fact that the default enumeration values ​​are sequential. Of course, this is not compilation time.

-3
source

Source: https://habr.com/ru/post/1613383/


All Articles