Enders Enders - 3 months ago 19
C Question

Conditional statements based on #define's name

I just started doing some C (coming from Java). I'm trying to figure out what the language's approach to a conditional based on a define's name is.

e.g. I have a huge header file that I can't(shouldn't) edit with a lot of defines.

#define GPIO_OTYPER_OT_0 ((uint32_t)0x00000001)
#define GPIO_OTYPER_OT_1 ((uint32_t)0x00000002)
#define GPIO_OTYPER_OT_2 ((uint32_t)0x00000004)
#define GPIO_OTYPER_OT_3 ((uint32_t)0x00000008)
#define GPIO_OTYPER_OT_4 ((uint32_t)0x00000010)
#define GPIO_OTYPER_OT_5 ((uint32_t)0x00000020)


And so on;

I want to make a function/declaration (or whatever to the solution is) to act on the _# part of the define.

(pseudocode)

void initialize(int X) {
GPIOA->MODER |= GPIO_MODER_MODER%X_5;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_%X;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR%X;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR5;
GPIOA->ODR |= GPIO_ODR_ODR_%X;
}


Where %X is int X

All I can think of is a switch statement for each X but X has a large range so the source file would be huge.

edit:
https://github.com/espruino/Espruino/blob/master/targetlibs/stm32f4/lib/stm32f411xe.h
is the header file.

Answer

There is no way to insert an arbitrary integer into a macro name. However, your integers are going to be small (smaller than 32 for sure, because all your constants are of a 32-bit type). So you can convert a switch-statement into a one-line expression, like so:

x == 0 ? GPIO_OTYPER_OT_0 : \
x == 1 ? GPIO_OTYPER_OT_1 : \
x == 2 ? GPIO_OTYPER_OT_2 : \
...
x == 31 ? GPIO_OTYPER_OT_31 : 0

Here, you can even make a "default" expression that will generate a runtime error - something like (abort(), (uint32_t)0).

To make this more generic, separate the GPIO_OTYPER_OT_ part into a macro argument, and use the "paste operator" ##:

#define MY_MACRO(name, x) \
x == 0 ? name ## 0 : \
x == 1 ? name ## 1 : \
x == 2 ? name ## 2 : \
...
x == 31 ? name ## _31 : \
(abort(), name ## 0)

Usage example:

GPIOA->ODR |= MY_MACRO(GPIO_ODR_ODR_, x);

You have to make a separate macro for those names that have x in the middle:

#define MY_MACRO2(prefix, x, suffix) ( \
(x) == 0 ? prefix ## 0 ## suffix : \
(x) == 1 ? prefix ## 1 ## suffix : \
...
(x) == 31 ? prefix ## 31 ## suffix : \
(abort(), prefix ## 0 ## suffix))

Here I also added the necessary parentheses (around x and around the whole macro), like it is customary with C macros.


P.S. If your large header file doesn't define macros with numbers up to 31, but has a smaller limit, you cannot use the macro that mentions all these names, because you would get a compilation error. In that case, insert the maximum into the name of the macro. Then you can define them in a "recursive" way:

#define MY_MACRO_MAX1(prefix, x) \
x == 0 ? prefix ## 0 ## suffix : prefix ## 1 ## suffix

#define MY_MACRO_MAX2(prefix, x) \
x == 2 ? prefix ## 2 ## suffix : MY_MACRO_MAX1(prefix, x)

#define MY_MACRO_MAX3(prefix, x) \
x == 3 ? prefix ## 3 ## suffix : MY_MACRO_MAX2(prefix, x)

#define MY_MACRO_MAX4(prefix, x) \
x == 4 ? prefix ## 4 ## suffix : MY_MACRO_MAX3(prefix, x)

#define MY_MACRO_MAX5(prefix, x) \
x == 5 ? prefix ## 5 ## suffix : MY_MACRO_MAX4(prefix, x)

...
Comments