C Preprocessor generate macros by concatenation and stringification [duplicate]
Asked Answered
C

3

1

I have a set of target macros for which I want to generate aliases based on a choosing macro, like so:

Choosing macro:

#define I2C_MODULE 1

Alias macros (conceptual form):

#define I2C_MODULE_BASE I2C<Value of I2C_MODULE>_BASE
#define I2C_MODULE_NVIC INT_I2C<Value of I2C_MODULE>

Target macros (from an external file out of my control):

#define INT_I2C0   24 
#define INT_I2C1   53
...
#define I2C0_BASE  0x40020000
#define I2C1_BASE  0x40021000
...   

I wanted to have the preprocessor generate the alias macros I2C_MODULE_BASE and I2C_MODULE_NVIC based on the choosing macro I2C_MODULE, but after much reading Q1, P1 and many other references I lost track of, I ended up hard-coding their values. Below I show my current working definitions, and then my last failed attempts at generating the macros:

What works:

#define I2C_MODULE 1
#define I2C_MODULE_BASE I2C1_BASE
#define I2C_MODULE_NVIC INT_I2C1

what did not work:

#define I2C_MODULE 1
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)

/* Attempt 1 */
#define I2C_MODULE_BASE "I2C" STR(I2C_MODULE) "_BASE"
#define I2C_MODULE_NVIC "INT_I2C" STR(I2C_MODULE)

/* Attempt 2 */
#define _I2C_MODULE_BASE "I2C" STR(I2C_MODULE) "_BASE"
#define _I2C_MODULE_NVIC "INT_I2C" STR(I2C_MODULE)
#define I2C_MODULE_BASE _I2C_MODULE_BASE
#define I2C_MODULE_NVIC _I2C_MODULE_NVIC

EDIT: I expanded upon the accepted answer to get to where I wanted, as follows:

#define PASTE2(a, b) a ## b
#define PASTE3(a, b, c) a ## b ## c

#define _I2C_MODULE_BASE(x) PASTE3(I2C, x, _BASE)
#define _I2C_MODULE_NVIC(x) PASTE2(INT_I2C, x)

#define I2C_MODULE_BASE _I2C_MODULE_BASE(I2C_MODULE)
#define I2C_MODULE_NVIC _I2C_MODULE_NVIC(I2C_MODULE)
Colwin answered 13/3, 2015 at 14:10 Comment(4)
what exact you need? a macro or a string?Guarantee
I don't get it, why are you dealing with strings? Plain old ## identifier concatenation should work just fine here, though admittedly you may run into trouble with the evaluation order if you start nesting these constructsFragmentation
Look at C preprocessor and token concatenation — it should deal with your problem.Cyathus
NB: Names beginning with an underscore are reserved for 'the implementation' (see ISO/IEC 9899:2011 §7.1.3 Reserved identifiers for the details). It is best to avoid defining such names yourself — though you'll often (even usually) get away with, until you move to a new operating system or something similar.Cyathus
C
3

This seems to work:

#define I2C_MODULE 1

//Alias macros (conceptual form):
//#define I2C_MODULE_BASE I2C<Value of I2C_MODULE>_BASE
//#define I2C_MODULE_NVIC INT_I2C<Value of I2C_MODULE>

//Target macros (from an external file out of my control):

#define INT_I2C0   24 
#define INT_I2C1   53

#define I2C0_BASE  0x40020000
#define I2C1_BASE  0x40021000

#define PASTE2(a, b) a ## b
#define PASTE3(a, b, c) a ## b ## c

#define I2C_MODULE_BASE(x) PASTE3(I2C, x, _BASE)
#define I2C_MODULE_NVIC(x) PASTE2(INT_I2C, x)

extern int i2c_module_base = I2C_MODULE_BASE(I2C_MODULE);
extern int i2c_module_nvic = I2C_MODULE_NVIC(I2C_MODULE);

extern int i2c_module_base_0 = I2C_MODULE_BASE(0);
extern int i2c_module_nvic_0 = I2C_MODULE_NVIC(0);

extern int i2c_module_base_1 = I2C_MODULE_BASE(1);
extern int i2c_module_nvic_1 = I2C_MODULE_NVIC(1);

Sample output (from cpp):

# 1 "xx.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "xx.c"
# 21 "xx.c"
extern int i2c_module_base = 0x40021000;
extern int i2c_module_nvic = 53;

extern int i2c_module_base_0 = 0x40020000;
extern int i2c_module_nvic_0 = 24;

extern int i2c_module_base_1 = 0x40021000;
extern int i2c_module_nvic_1 = 53;

It is closely based on my answer to C preprocessor and token concatenation.

There are undoubtedly other ways that the I2C_MODULE_BASE and I2C_MODULE_NVIC macros could be written, but the key points are:

  1. Using the ## token pasting operator (not the # stringifying operator).
  2. Using two levels of macro (for example, I2C_MODULE_BASE and PASTE3).
Cyathus answered 13/3, 2015 at 14:56 Comment(1)
Thank you Jonathan! It worked as expected. Please see my OP edit for what the final usage wasColwin
F
1

I suspect that you are writing a I2C driver which can generically handle multiple I2C hardware peripherals in the same micro-controller without rewriting all the same code multiple times.

In that case, what you are really looking for probably is something like this:

#define I2C1 ((volatile uint8_t*)0x12345678)  // address of first hw register for I2C1
#define I2C2 ((volatile uint8_t*)0x55555555)  // address of first hw register for I2C2

/* map all registers used for I2C, they will have same register layout for every 
   peripheral no matter which one:  */
#define I2C_CONTROL(base) (*(base + 0))
#define I2C_DATA(base)    (*(base + 1))
...


// create some dummy typedef to make your functions look nice:
typedef volatile uint8_t* I2C_t; 


// define whatever functions you need in the driver:
void i2c_init (IC2_t bus);
void i2c_send (I2C_t bus, const uint8_t* data, size_t n);
...

// implement functions in a bus-independent way:
void i2c_init (IC2_t bus)
{
  I2C_CONTROL(bus) = THIS | THAT; // setup registers
}


// caller code:

i2c_init(I2C1);
i2c_init(I2C2);
...
i2c_send(I2C1, "hello", 5);
i2c_send(I2C2, "world", 5);
Furnish answered 13/3, 2015 at 15:6 Comment(2)
You are almost spot on! But the issue is, the driver is already written (it is Texas Instruments' TivaWare for C), but its level of abstraction out of the box is not high enough that you can pick a peripheral by name and have it perform all the required configuration. Instead it requires you to go and look for several sets of related macros, which is what I'm trying to consolidate by using a choosing macro.Colwin
@LuisE. Alright, well the above are useful tips & tricks when writing your own drivers. (And since TI are, like most semiconductor companies, notorious for the very poor quality of their source code, you might yet end up writing one yourself in the end...)Furnish
C
0

Just use #if / #else / #endif

#if (I2C_MODULE == 0)
#define I2C_MODULE_BASE I2C0_BASE
#define I2C_MODULE_NVIC INT_I2C0
#elif (I2C_MODULE == 1)
#define I2C_MODULE_BASE I2C1_BASE
#define I2C_MODULE_NVIC INT_I2C1
#else
#error Unknown configuration
#endif
Captor answered 13/3, 2015 at 14:17 Comment(1)
This is a simple approach, but it gets cumbersome when I2C_MODULE can reach up to 8 :)Colwin

© 2022 - 2024 — McMap. All rights reserved.