Search code examples
cgccc-preprocessortoken-pasting-operator

C Pre-processing: Chaining defines together


I'm trying to concatenate multiple symbols together based on an if statement but have looked on SO and was unable to find something that worked without needing arguments passed into my last macro.

Essentially I'm trying to do this

#define COMMS_WD_PORT 1
#define COMMS_WD_TXRX CAN_TX
#if COMMS_WD_TXRX == CAN_TX
    #define COMMS_WD_REG (uint32_t*)canREG#COMMS_WD_PORT##->TIOC
#else
    #define COMMS_WD_REG (uint32_t*)canREG#COMMS_WD_PORT##->RIOC
#endif

Output I'd like

If COMMS_WD_TXRX == CAN_TX
expand COMMS_WD_REG to (uint32_t*)canREG1->TIOC

If COMMS_WD_TXRX == CAN_RX
expand COMMS_WD_REG to (uint32_t*)canREG1->RIOC

What I actually get

If COMMS_WD_TXRX == CAN_TX
COMMS_WD_REG expands to (uint32_t*)canREG#1->TIOC

If COMMS_WD_TXRX == CAN_RX
COMMS_WD_REG expands to (uint32_t*)canREG#1->RIOC

I'm quite stuck on the use of ## vs # and how to chain all of these together properly. I've looked at the gcc pre-processor guide but it didn't have examples that were close to what I'm trying to do.

Edit: My goal is to write to a specific bit in the COMMS_WD_REG register so if there's a better way to achieve the effect I want please let me know

Edit 2: I've found that

#define COMMS_WD_REG() (uint32_t*)canREG#COMMS_WD_PORT##->TIOC
COMMS_WD_REG() expands to canREG1->TIOC

Which is the output I want, but then I get an error "expected a macro parameter name" at COMMS_WD_PORT


Solution

  • The key piece here is that you want to paste together the token canREG with the token 1 that came from the expansion of COMMS_WD_PORT. Since the tokens pasted with ## are not normally subject to macro expansion, you need double indirection as explained in C: Expand Macro With Token Pasting.

    Stealing ouah's example, we can do the following, with an auxiliary CANREG macro to simplify things a little bit:

    #define COMMS_WD_PORT 1
    #define COMMS_WD_TXRX CAN_TX
    
    #define CAT(x, y) CAT_(x, y)
    #define CAT_(x, y) x ## y
    
    #define CANREG CAT(canREG, COMMS_WD_PORT)
    
    #if COMMS_WD_TXRX == CAN_TX
        #define COMMS_WD_REG (uint32_t*)CANREG->TIOC
    #else
        #define COMMS_WD_REG (uint32_t*)CANREG->RIOC
    #endif
    

    Now COMMS_WD_REG expands to (uint32_t*)canREG1->TIOC as desired. Try it on godbolt.