Can I define a macro using a macro in C?
I have a set of #defines like this:
#define MODULE1_PINMASK 0x1
#define MODULE2_PINMASK 0x2
#define MODULE3_PINMASK 0x3
where the value of the pin mask depends on the second argument:
#define MODULE1_PORT_PIN A,1
#define MODULE2_PORT_PIN A,2
#define MODULE3_PORT_PIN A,3
If at any point in the future I make a change like:
#define MODULE1_PORT_PIN A,1 /* changes to #define MODULE1_PORT_PIN A,4 */
I need to change the mask as well:
#define MODULE1_PINMASK 0x1 /* then becomes #define MODULE1_PINMASK 0x4 */
I am trying to automate the process without manually changing the mask. So far I have these macros to extract the second argument of MODULEX_PORT_PIN (in this case, I don't need the first argument):
#define GET_SECOND(X, Y) Y
#define GET_PIN(PORT_PIN) GET_SECOND(PORT_PIN)
If I use them in functions, I get the correct result, for example:
uint8_t pinmask=0x0;
switch (GET_PIN(MODULE2_PORT_PIN))
{
case 1:
pinmask = 0x1;
break;
case 2:
pinmask = 0x2;
break;
case 3:
pinmask = 0x3;
break;
default:
break;
}
printf ("%#x", pinmask); /* prints "0x2" */
but i want to keep pinmasks as #defines. Is there a way to implement a macro #define GET_PINMASK
that uses the switch case to define the finger mask? I am targeting something like:
#define MODULE1_PINMASK ASSIGN_PINMASK(GET_PIN(MODULE1_PORT_PIN))
which in this case will define MODULE1_PINMASK as 0x1.
EDIT: The second argument in #define MODULE1_PORT_PIN A,1
is a uint8_t, not a hex value, so I can't pass it directly.
source to share
I think you can overdo the problem. If the second field of each MODULEn_PORT_PIN
defines an always integer constant expression , then this should work:
#define MODULE1_PORT_PIN A,1
#define MODULE2_PORT_PIN A,2
#define MODULE3_PORT_PIN A,3
#define GET_SECOND(X, Y) (Y)
#define PIN_TO_MASK(PIN) (1ul << GET_SECOND(PIN))
#define MODULE1_PINMASK PIN_TO_MASK(MODULE1_PORT_PIN)
#define MODULE2_PINMASK PIN_TO_MASK(MODULE2_PORT_PIN)
#define MODULE3_PINMASK PIN_TO_MASK(MODULE3_PORT_PIN)
It is not clear from your question if the second field could be anything other than an integer constant expression. If a constant is always present in the second field enum
, then macros MODULEn_PINMASK
can be used in any context other than expressions #if
. If it's ever associated with a variable, then they can only be used inside the function body. (Since this is C, not C ++, this is true even if a variable const
.)
There is no way to avoid having to write each one #define
separately. If this is a problem, you should be thinking about writing a program that generates the list #define
s. Generating source code from a DSL of your own invention at build time is an underrated technique.
source to share
Are you considering using x-macro ?
You start by creating an abstract #define
for the list of records:
#define CREATE_LIST() \
ENTRY(1, A, 0x1) \
ENTRY(2, A, 0x2) \
ENTRY(3, A, 0x3)
And then call the list for different definitions ENTRY
:
// Get the number of entries. Creates something like:
// const uint8_t PIN_COUNT = 0 + 1 + 1 + 1;
#define ENTRY(number, x, y) + 1
const uint8_t PIN_COUNT = \
CREATE_LIST()
;
#undef ENTRY
// Array of first parameters
#define ENTRY(number, x, y) #x ,
const char * Pin_names[PIN_COUNT] =
{
CREATE_LIST()
};
#undef ENTRY
// Array of second parameters
#define ENTRY(number, x, y) y,
const uint8_t Pin_masks[PIN_COUNT] =
{
CREATE_LIST()
};
#undef ENTRY
// Array of module names
#define ENTRY(number, x, y) STRINGIFY(MODULE ## number) ,
const char * Module_names[PIN_COUNT] =
{
CREATE_LIST()
};
#undef ENTRY
The preprocessor will expand it to the following value:
const uint8_t PIN_COUNT =
+ 1 + 1 + 1
;
const char * Pin_names[PIN_COUNT] =
{
"A" , "A" , "A" ,
};
const uint8_t Pin_masks[PIN_COUNT] =
{
0x1, 0x2, 0x3,
};
const char * Module_names[PIN_COUNT] =
{
"MODULE1", "MODULE2", "MODULE3"
};
The possibilities are endless. This is less readable, but perhaps a little more convenient.
source to share