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)
Community
  • 1
  • 1
Luis E.
  • 841
  • 11
  • 15
  • what exact you need? a macro or a string? – Jason Hu Mar 13 '15 at 14:16
  • 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 constructs – doynax Mar 13 '15 at 14:31
  • Look at [C preprocessor and token concatenation](http://stackoverflow.com/questions/1489932/c-preprocessor-and-token-concatenation) — it should deal with your problem. – Jonathan Leffler Mar 13 '15 at 14:43
  • 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. – Jonathan Leffler Mar 13 '15 at 15:49

3 Answers3

2

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).
Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
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);
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • 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. – Luis E. Mar 13 '15 at 15:10
  • @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...) – Lundin Mar 13 '15 at 15:18
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
Matt
  • 13,674
  • 1
  • 18
  • 27