I have a need to use a macro to write #defines and other macros in C for an embedded MCU.
With the following definitions:
// Define the pin state as wired to the board, CA (PIN controls -ve lead) or CC (PIN controls +ve lead)
#define CA_ON LOW // Common Anode LED ON when PIN is LOW
#define CA_OFF HIGH // Common Anode LED OFF when PIN is HIGH
#define CC_ON HIGH // Common Cathode LED ON when PIN is HIGH
#define CC_OFF LOW // Common Cathode LED OFF when PIN is LOW
// LED ON/OFF MACROs for Common Anode (CA) and Common Cathode (CC) LEDs
#define CA_LED_ON(PIN) digitalWrite(PIN, CA_ON) // Set the LED ON for Common Anode
#define CA_LED_OFF(PIN) digitalWrite(PIN, CA_OFF) // Set the LED OFF for Common Anode
#define CC_LED_ON(PIN) digitalWrite(PIN, CC_ON) // Set the LED ON for Common Cathode
#define CC_LED_OFF(PIN) digitalWrite(PIN, CC_OFF) // Set the LED OFF for Common Cathode
I currently have something such as the following lines that need to be repeated:
#define PIN_LED_RED 32
#define RED_LED_ON CA_LED_ON(PIN_LED_RED)
#define RED_LED_OFF CA_LED_OFF(PIN_LED_RED)
//
#define PIN_LED_BLUE 19
#define BLUE_LED_ON CC_LED_ON(PIN_LED_BLUE)
#define BLUE_LED_OFF CC_LED_OFF(PIN_LED_BLUE)
//
#define SET_BLUE_LED pinMode(PIN_LED_BLUE, OUTPUT);
I would like to write something similar to
#define LED_CONFIGURE(ID, PIN, CA_CC) \
#define PIN_LED_##ID PIN \
#define ID##_LED_ON CA_CC##_LED_ON(PIN) \
#define ID##_LED_OFF CA_CC##_LED_OFF(PIN) \
#define ID##_CONFIGURE_LED(PIN) \
pinMode(PIN, OUTPUT); \
Thus I can just write one line for each LED:
LED_CONFIGURE(RED, 32, CA)
LED_CONFIGURE(BLUE, 19, CC)
The pre-processor balks at the '#' symbol within a macro.
Any C MACRO Gurus know how to get around this?
I have a need to use a macro to write #defines and other macros in C
That doesn't work.
The replacement list in a macro definition cannot, in the definition itself, have the form of a macro definition because preprocessor directives must not be preceded by other tokens on their logical line. And a result of macro expansion is never interpreted as a macro definition or any other kind of preprocessor directive because the spec explicitly says not (C23 6.10.5.5/3).
I'm inclined to think that your best option is probably to not define macros RED_PIN_ON
etc at all. It's not clear to me that writing, say,
RED_LED_ON
, is significantly better than writing
CA_LED_ON(PIN_LED_RED)
, which your existing macro stack already supports. If you want to have those slightly shorter macros then you will need to define them all individually. You could probably write a code generator to produce the definitions for you, but that seems like a lot more trouble than it's worth.
Addendum
From a comment:
I'm trying to write the code to require the least amount of changes when compiled for different MCUs and boards. From my experience, seeing RED_LED_ON in the code is clear, how and where it's connected is important, that's only needed to be defined once. I can put board/MCU specific definitions in different header files and/or use conditional compilation without impacting the logic part of the code.
You do not need macros to define other macros in order to apply macros to those objectives. Instead, you can let macros expand differently according to the definitions of other macros. For example, you might like something along these lines:
//////
// Hardware specific definitions:
//
// Pin type and number for each LED
#define PIN_TYPE_RED CA
#define PIN_NUMBER_RED 32
#define PIN_TYPE_BLUE CC
#define PIN_NUMBER_BLUE 19
// Per-type mappings from (pin type, led state) to voltage level
// Possibly these could be shared across different hardware.
// Define the pin state as wired to the board, CA (PIN controls -ve lead) or CC (PIN controls +ve lead)
#define CA_ON LOW // Common Anode LED ON when PIN is LOW
#define CA_OFF HIGH // Common Anode LED OFF when PIN is HIGH
#define CC_ON HIGH // Common Cathode LED ON when PIN is HIGH
#define CC_OFF LOW // Common Cathode LED OFF when PIN is LOW
//////
// Common macros
//
// Helper and utility macros
#define CONCAT(x, y) x ## _ ## y
#define LED_TYPE(color) CONCAT(PIN_TYPE, color)
#define LED_PIN(color) CONCAT(PIN_NUMBER, color)
#define PIN_LEVEL(type, state) CONCAT(type, state)
#define LED_HELPER(pin, type, state) digitalWrite(pin, PIN_LEVEL(type, state))
// Note: CA_LED_ON, etc, not used
// Primary macros for use in other code
#define LED_ON(color) LED_HELPER(LED_PIN(color), LED_TYPE(color), ON)
#define LED_OFF(color) LED_HELPER(LED_PIN(color), LED_TYPE(color), OFF)
/////
// Demo
//
LED_ON(RED)
LED_OFF(RED)
LED_ON(BLUE)
LED_OFF(BLUE)
That gives you LED_ON
and LED_OFF
macros taking an LED code (here expressed as color names). Although these definitions use the name color
for their parameter, there's nothing particularly color-specific there. Codes such as FLUX_CAPACITOR
and DEATH_RAY
would work fine too, provided only that corresponding pin type and pin number definitions are provided for them.
Nor is this inherently limited to two pin types. As long as the machine-specific definitions map a pin-type code to ON and OFF levels, those same definitions can map LED names to that pin type code, in which case LED_ON()
and LED_OFF()
will do their thing.