【问题标题】:Can I define a macro using a macro in C?我可以使用 C 中的宏定义宏吗?
【发布时间】:2017-11-07 09:58:26
【问题描述】:

我有一组像这样的#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" */

但我想将 pinmask 保留为 #defines。有没有办法实现一个#define GET_PINMASK 宏,它使用开关盒来定义引脚掩码?我的目标是:

#define MODULE1_PINMASK ASSIGN_PINMASK(GET_PIN(MODULE1_PORT_PIN))

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

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

【问题讨论】:

  • #define MODULE1_PORT_PIN A,1 中,1(或其他任何东西)是否被解释为十六进制?所以如果有#define MODULE23_PORT_PIN A,23,那么应该有#define MODULE23_PINMASK 0x23。对吗?
  • @internetAusie,该值实际上是一个uint8,所以我不能直接传递它。抱歉没有澄清,我已经编辑了我的帖子。
  • 我不确定您是否知道以下事实: 1. 宏(有 () 和没有)只是宏预处理器中文本替换的主题。 2. 0x1 = 1, 0x2 = 2, ..., 0x9 = 9。无论在哪里接受小数,十六进制也是如此(反之亦然)。
  • @Scheff,1. 我知道宏只是一个文本替换,但我想我应该问一下,以防有什么方法可以实现它。 2. 我给出了这些十六进制值来简化问题。实际上,int 值表示 X 位的二进制移位。因此,例如在#define MODULE2_PORT_PIN A,2 中,我追求的十六进制值是 0x4 (0b1
  • 另外,您的陈述“*_PIN 中的第二个参数是 uint8_t 而不是十六进制值”没有任何意义。您只显示了第二个参数,它们是小的、无后缀的十进制文字;这些在编译器中具有 int 类型(更准确地说是“翻译阶段 7”),并且在预处理器中根本没有类型(在预处理器进行算术的一个地方,它在 intmax_t 无论如何) .照原样,我看不出有什么问题,但如果第二个参数曾经是一个变量,那将立即意味着你想要的东西无法完成;会这样吗?

标签: c macros embedded c-preprocessor preprocessor


【解决方案1】:

我认为你可能想多了这个问题。如果每个 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。如果这是一个问题,您应该考虑编写一个生成#defines 列表的程序。在构建时从您自己发明的DSL 生成源代码是一种被低估的技术。

【讨论】:

  • 谢谢,@zwol 有效。我让我的生活变得困难,忽略了最简单的方法......
【解决方案2】:

您是否考虑过使用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"
};

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

【讨论】:

  • X 宏只能作为最后的手段。在这种情况下,OP 需要做一些非常基本的事情 - 引脚数字 I/O,这可以通过更简单的方式解决。他们并不是世界上第一个使用数字 I/O 编写微控制器程序的人。对于所有此类程序,每个端口一个定义和一个引脚掩码一个定义就足够了。
  • 这只是一个建议,仅此而已,最简单的方法已经用接受的答案回答了。如果您需要更多元数据(“命名枚举”是通常的示例),x-macros 变得有趣,因此上面示例中的所有字符串。
猜你喜欢
  • 1970-01-01
  • 2011-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-25
  • 1970-01-01
  • 1970-01-01
  • 2010-12-20
相关资源
最近更新 更多