Plesos
Plesos

Reputation: 75

Can I define a macro using a macro in C?

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

Answers (2)

zwol
zwol

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 #defines. Generating source code from a DSL of your own invention, at build time, is an under-valued technique.

Upvotes: 4

vgru
vgru

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

Related Questions