我可以在C中使用宏定义宏吗?

时间:2017-06-06 11:41:24

标签: c macros embedded c-preprocessor preprocessor

我有一组像这样的#defines:

#define MODULE1_PINMASK 0x1
#define MODULE2_PINMASK 0x2
#define MODULE3_PINMASK 0x3

其中pinmask的值取决于第二个参数:

#define MODULE1_PORT_PIN A,1
#define MODULE2_PORT_PIN A,2
#define MODULE3_PORT_PIN A,3

如果在将来的任何时候,我会做出改变,例如:

#define MODULE1_PORT_PIN A,1 /* changes to #define MODULE1_PORT_PIN A,4 */ 

我还需要更改pinmask:

#define MODULE1_PINMASK 0x1 /* then becomes #define MODULE1_PINMASK 0x4 */ 

我正在尝试通过不必手动更改pinmask来自动化该过程。到目前为止,我已经有了这些宏来提取MODULEX_PORT_PIN的第二个参数(在这种情况下我不关心第一个参数):

#define GET_SECOND(X, Y) Y
#define GET_PIN(PORT_PIN) GET_SECOND(PORT_PIN)

如果我在函数中使用它们,我会得到正确的结果,例如:

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" */

但我希望将pinmasks保持为#defines。有没有办法实现使用switch case定义pinmask的#define GET_PINMASK宏?我的目标是:

#define MODULE1_PINMASK ASSIGN_PINMASK(GET_PIN(MODULE1_PORT_PIN))

在这种情况下,它将MODULE1_PINMASK定义为0x1。

编辑:#define MODULE1_PORT_PIN A,1中的第二个参数是uint8_t而不是十六进制值,因此我无法直接传递它。

2 个答案:

答案 0 :(得分:4)

我认为你可能会过度思考这个问题。如果每个MODULEn_PORT_PIN定义的第二个字段始终为integer constant expression,那么这应该有效:

#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)

从您的问题中不清楚第二个字段是否可以是整数常量表达式之外的其他字段。如果第二个字段涉及enum常量,则MODULEn_PINMASK宏仍可用于除#if表达式之外的任何上下文中。如果它涉及变量,那么它们只能在函数体内使用。 (因为这是C而不是C ++,即使变量是const也是如此。)

无法避免必须单独编写每个#define。如果这是一个问题,你应该考虑编写一个生成#define列表的程序。在构建时,从您自己的发明的DSL生成源代码是一种价值不高的技术。

答案 1 :(得分:0)

您是否考虑过使用x-macros

首先为条目列表创建一个抽象#define

#define CREATE_LIST() \
    ENTRY(1, A, 0x1) \
    ENTRY(2, A, 0x2) \
    ENTRY(3, A, 0x3)

然后调用列表以获取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

预处理器会将其扩展为:

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"
};

可能性无穷无尽。它的可读性较差,但可能稍微更易于维护。