将 ## 预处理器运算符与另一个宏输出一起使用

using ## preprocessor operator with another macros output

我正在尝试使用 ## 预处理器运算符来确定要使用的 GPIO。 这是我的代码:

#define GPIO_Pin_1 0x0001
#define GPIO_Pin_2 0x00CA
#define GPIO_Pin_3 0x00DE
#define GPIO_Pin_4 0x00AC

#define DIVIDE(a , b) ( (a) / (b) )
#define NUMBER_TO_GPIO(a) GPIO_Pin_##DIVIDE(a , 2)

int gpioNumber = 8;
int gpioAddress = NUMBER_TO_GPIO(gpioNumber);

gpioAddress 的期望值是 0x00AC (GPIO_Pin_4),但编译器给出了这个错误:"GPIO_Pin_DIVIDE declared implicitly"。我想连接 GPIO_Pin_ 和 DIVIDE(a,b) 宏

的结果

## 连接符号。因此,你形成了一个未知的符号GPIO_Pin_DIVIDE

您可以做的是:

#define GPIO_Pin_1 0x0001
#define GPIO_Pin_2 0x00CA
#define GPIO_Pin_3 0x00DE
#define GPIO_Pin_4 0x00AC

#define DIVIDE(a , b) ( (a) / (b) )
#define NUMBER_TO_GPIO(a) (DIVIDE( GPIO_Pin_##a , 2))

int gpioAddress = NUMBER_TO_GPIO(4);

但我不确定这是否是你的意思。

如评论中所述,您的宏将不起作用。您可以使用数组:

const int GPIO_Pins[] = {
    /*GPIO_Pin_1*/ 0x0001,
    /*GPIO_Pin_2*/ 0x00CA,
    /*GPIO_Pin_3*/ 0x00DE,
    /*GPIO_Pin_4*/ 0x00AC
};
#define NUMBER_TO_GPIO(a) GPIO_Pins[(a) / 2 - 1]

当然是没有边界检查的问题

对于 C++,在编译时使用 constexpr 计算值。

static constexpr std::array<int, 4> Pins = { 0x01, 0x0A, 0xDE, 0xAC };

static constexpr int pin2(int a)
{
    return Pins[a / 2];
}

没有什么好的方法可以仅使用 CPP 来完成您想做的事情。特别是因为您在运行时计算 GPIO 编号。

确保它是编译时直接替换的唯一方法是直接使用索引(你没有这样做)

将此视为 C:

中的解决方案
const long GPIO_PINS[] = { 0xFFFF, 0x0001, 0x00CA, 0x00DE, 0x00AC };
static inline long getGPIOAddr(int gpioAddress) {
    return GPIO_PINS[gpioAddress >> 1];
}

int main() {
    const int gpioNumber = 8;
    return getGPIOAddr(gpioNumber);
}

这里是 gcc 的汇编输出(clang 给出了类似的东西):

gcc -S -O foo.c

    .file   "foo.c"
    .text
    .globl  main
    .type   main, @function
main:
.LFB1:
    .cfi_startproc
    movl    2, %eax ; NOTICE THE OPTIMIZATION HERE!
    ret
    .cfi_endproc
.LFE1:
    .size   main, .-main
    .globl  GPIO_PINS
    .section    .rodata
    .align 32
    .type   GPIO_PINS, @object
    .size   GPIO_PINS, 40
GPIO_PINS:
    .quad   65535
    .quad   1
    .quad   202
    .quad   222
    .quad   172
    .ident  "GCC: (GNU) 7.2.0"
    .section    .note.GNU-stack,"",@progbits

为了完整起见,这里是 clang:

    .file   "foo.c"
    .text
    .globl  main
    .align  16, 0x90
    .type   main,@function
main:                                   # @main
    .cfi_startproc
# BB#0:
    movl    2, %eax
    ret
.Ltmp0:
    .size   main, .Ltmp0-main
    .cfi_endproc

    .type   GPIO_PINS,@object       # @GPIO_PINS
    .section    .rodata,"a",@progbits
    .globl  GPIO_PINS
    .align  16
GPIO_PINS:
    .quad   65535                   # 0xffff
    .quad   1                       # 0x1
    .quad   202                     # 0xca
    .quad   222                     # 0xde
    .quad   172                     # 0xac
    .size   GPIO_PINS, 40


    .ident  "clang version 3.4.2 (tags/RELEASE_34/dot2-final)"
    .section    ".note.GNU-stack","",@progbits

任何优秀的现代编译器都可以确定 gpioNumber 在特定 AST 期间不会 更改,并将正确地替换常量。

所以不要试图超越编译器,它在优化方面比你好得多。

如果您对编译器在这方面做得有多好感兴趣,我已经在 godbolt 上加载了这段代码:

https://godbolt.org/g/EWjsHB

您可以随心所欲地查看代码的反汇编。 所有 那里的编译器使用 -O 作为选项优化了这些东西。