Reputation: 75
I have a set of #defines like these:
#define MODULE1_PINMASK 0x1
#define MODULE2_PINMASK 0x2
#define MODULE3_PINMASK 0x3
where the value of the pinmask depends on the second argument of:
#define MODULE1_PORT_PIN A,1
#define MODULE2_PORT_PIN A,2
#define MODULE3_PORT_PIN A,3
If at any point in future, I make a change, e.g:
#define MODULE1_PORT_PIN A,1 /* changes to #define MODULE1_PORT_PIN A,4 */
I need to also change the pinmask:
#define MODULE1_PINMASK 0x1 /* then becomes #define MODULE1_PINMASK 0x4 */
I'm trying to automate the process by not having to manually change the pinmask. So far I've got these macros to extract the second argument of MODULEX_PORT_PIN (I don't care about the first argument in this case):
#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 instance:
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 the pinmasks as #defines. Is there a way to implement a #define GET_PINMASK
macro which uses the switch case to define the pinmask? I'm aiming for something like:
#define MODULE1_PINMASK ASSIGN_PINMASK(GET_PIN(MODULE1_PORT_PIN))
which in this case would define MODULE1_PINMASK as 0x1.
EDIT: The second argument in #define MODULE1_PORT_PIN A,1
is an uint8_t and not a hex value and so I can't pass it directly.
Upvotes: 2
Views: 457
Reputation: 140559
I think you may be overthinking the problem. If the second field of each MODULEn_PORT_PIN
define is always an 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 whether the second field can be something other than an integer constant expression. If the second field ever involves an enum
constant, then the MODULEn_PINMASK
macros can still be used in any context except for #if
expressions. If it ever involves a variable, then they can only be used inside the body of a function. (Since this is C and not C++, that's true even if the variable is const
.)
There is no way to avoid having to write each #define
individually. If that is a problem, you should be thinking about writing a program that generates the list of #define
s. Generating source code from a DSL of your own invention, at build time, is an under-valued technique.
Upvotes: 4
Reputation: 51214
Have you considered using x-macros?
You start by creating an abstract #define
for the list of entries:
#define CREATE_LIST() \
ENTRY(1, A, 0x1) \
ENTRY(2, A, 0x2) \
ENTRY(3, A, 0x3)
And then invoke the list for different definitions of 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 this to something like:
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. It's less readable, but perhaps slightly more maintainable.
Upvotes: 1