简体   繁体   中英

c preprocessor macro concatenate arguments after expansion

I am writing a driver for avr gpio, I have a function that takes an input which is an enum. I made a macro that calls this function after concatenating the port name with "__" so I can always use initPort(PORTA,1,...).

#define initPort(port,mask,dir,pullup) GPIO_Init(port ## __,mask,dir,pullup)

typedef enum {
PORTA__,
PORTB__,
PORTC__,
PORTD__
} PORT;

void GPIO_Init(PORT p, uint8_t pins, Direction dir,uint8_t pullup) {
switch (p) {
    case PORTA__:

now when I want to use that function I use: initPort(PORTA,1,...) and this works fine. The problem is when I want to use something like:

#define LED_PORT PORTA
initPort(LED_PORT,1,...) 

what happens now is that the argument of GPIO_Init is now LED_PORT__ and not PORTA__

is it possible to fix this or I have to use another way?

The usual technique for this is to add a level of indirection, like this:

#define PORTNAME(port)     port ## __
#define initPort(port, mask, dir, pullup) GPIO_Init(PORTNAME(port), mask, dir, pullup)

typedef enum {
    PORTA__,
    PORTB__,
    PORTC__,
    PORTD__
} PORT;

void GPIO_Init(PORT p, uint8_t pins, Direction dir, uint8_t pullup)
{
    switch (p)
    {
    case PORTA__:
        break;
    }
}

#define LED_PORT PORTA
initPort(LED_PORT, 1, 9, 43)

This can be preprocessed (I ran cpp port31.c to get this output):

# 1 "port31.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "port31.c"




typedef enum {
    PORTA__,
    PORTB__,
    PORTC__,
    PORTD__
} PORT;

void GPIO_Init(PORT p, uint8_t pins, Direction dir, uint8_t pullup)
{
    switch (p)
    {
    case PORTA__:
        break;
    }
}


GPIO_Init(PORTA__, 1, 9, 43)

This looks like the result you are after.

There are other questions on SO which describe the general technique.

You actually can do it by forcing the preprocessor to perform an extra pass before:

#define initPortS(port,mask,dir,pullup) GPIO_Init(port ## __,mask,dir,pullup)
#define initPort(...) initPortS(__VA_ARGS__)
#define LED_PORT PORTA

initPort(LED_PORT,1,2,3);

This will do:

1st pass:

initPort(LED_PORT,1,2,3); -> initPortS(PORTA,1,2,3);

2nd pass:

initPortS(PORTA,1,2,3); -> GPIO_Init(PORTA__,1,2,3);

Here is a demo

Possible pitfall:

If PORTA is a defined symbol, it will get expanded too on the second pass. So if you have a line such as

#define PORTA XXX

somewhere in the code, it will expand into

GPIO_Init(XXX__,1,2,3);

What you are asking is impossible. However, you can create a function called LED_PORTinitPort() and call that by concatentating the first argument of the macro to initPort(), which acheives the same goal.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM