【问题标题】:c preprocessor macro concatenate arguments after expansionc预处理器宏在扩展后连接参数
【发布时间】:2018-01-26 16:44:49
【问题描述】:

我正在为 avr gpio 编写一个驱动程序,我有一个函数接受一个枚举输入。我制作了一个宏,在将端口名称与“__”连接后调用此函数,因此我始终可以使用 initPort(PORTA,1,...)。

#define initPort(port,mask,dir,pullup) GPIO_Init(port ## __,mask,dir,pullup)

typedef enum {
PORTA__,
PORTB__,
PORTC__,
PORTD__
} PORT;

void GPIO_Init(PORT p, uint8_t pins, Direction dir,uint8_t pullup) {
switch (p) {
    case PORTA__:

现在,当我想使用该功能时,我使用:initPort(PORTA,1,...),这很好用。 问题是当我想使用类似的东西时:

#define LED_PORT PORTA
initPort(LED_PORT,1,...) 

现在发生的事情是 GPIO_Init 的参数现在是 LED_PORT__ 而不是 PORTA__

是否可以解决这个问题,或者我必须使用其他方式?

【问题讨论】:

    标签: c macros embedded avr atmega


    【解决方案1】:

    您实际上可以通过强制预处理器在之前执行额外的传递来做到这一点:

    #define initPortS(port,mask,dir,pullup) GPIO_Init(port ## __,mask,dir,pullup)
    #define initPort(...) initPortS(__VA_ARGS__)
    #define LED_PORT PORTA
    
    initPort(LED_PORT,1,2,3);
    

    这样就可以了:

    第一遍:

    initPort(LED_PORT,1,2,3); -> initPortS(PORTA,1,2,3);
    

    第二遍:

    initPortS(PORTA,1,2,3); -> GPIO_Init(PORTA__,1,2,3);
    

    Here is a demo

    可能的陷阱:

    如果PORTA 是一个已定义的符号,它也会在第二遍被扩展。因此,如果您有诸如

    之类的行
    #define PORTA XXX
    

    在代码中的某处,它将扩展为

    GPIO_Init(XXX__,1,2,3);
    

    【讨论】:

    • 我使用 atmel studio,这给了我与上一个答案相同的错误:粘贴“)”和“__”没有提供有效的预处理令牌和函数 GPIO_Init 的参数太少
    • 是的,就是这样,PORTA确实是在包含的文件中定义的。我想我没有希望让它发挥作用。 :(
    • 只是不要使用 PORTA__ 之类的东西作为你的名字,而是像 PortA 之类的东西,或其他不冲突的方案。
    【解决方案2】:

    通常的技术是添加一个间接级别,如下所示:

    #define PORTNAME(port)     port ## __
    #define initPort(port, mask, dir, pullup) GPIO_Init(PORTNAME(port), mask, dir, pullup)
    
    typedef enum {
        PORTA__,
        PORTB__,
        PORTC__,
        PORTD__
    } PORT;
    
    void GPIO_Init(PORT p, uint8_t pins, Direction dir, uint8_t pullup)
    {
        switch (p)
        {
        case PORTA__:
            break;
        }
    }
    
    #define LED_PORT PORTA
    initPort(LED_PORT, 1, 9, 43)
    

    这可以被预处理(我运行cpp port31.c 来得到这个输出):

    # 1 "port31.c"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 1 "port31.c"
    
    
    
    
    typedef enum {
        PORTA__,
        PORTB__,
        PORTC__,
        PORTD__
    } PORT;
    
    void GPIO_Init(PORT p, uint8_t pins, Direction dir, uint8_t pullup)
    {
        switch (p)
        {
        case PORTA__:
            break;
        }
    }
    
    
    GPIO_Init(PORTA__, 1, 9, 43)
    

    这看起来像你想要的结果。

    还有其他关于 SO 的问题描述了一般技术。

    【讨论】:

    • 我实际上在发布问题之前尝试过,但我得到一个错误:粘贴“)”和“__”没有给出有效的预处理令牌。以及更多错误和警告:GPIO_Init 函数的参数太少
    • 是的,我在尝试时遇到了一些类似的错误:#define EVALUATE(x) x#define initPort(port, mask, dir, pullup) GPIO_Init(EVALUATE(port) ## __, mask, dir, pullup)。它不起作用,所以我没有将其显示为“答案”。连接必须在我显示为 PORTNAME 宏的内容中。
    【解决方案3】:

    你问的是不可能的。但是,您可以创建一个名为 LED_PORTinitPort() 的函数,并通过将宏的第一个参数连接到 initPort() 来调用它,从而实现相同的目标。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-04
      • 2020-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-18
      • 2014-04-17
      相关资源
      最近更新 更多