C11 _Generic 评估另一个宏

C11 _Generic evaluating another macro

我想让 _Generic 计算另一个宏。在下面的示例中,db_put_u8 将插入一个 uint8_t 到缓冲区中,同时它还会留下一条日志消息。如果我错过了一个字段,日志可以帮助我了解。因此宏。我已经减少了功能来说明这里的问题,但实际上它应该适用于各种数据类型。

#include <stdio.h>
#include <stdint.h>

#define db_put_u8(con, name, val) \
    do { \
        fputs(name "<- " #val, con); \
    } while(0)

#define db_put(con, name, val) _Generic(val, uint8_t: db_put_u8)(con, name, val)

int main(void)
{
    uint8_t v = 10;
    db_put(stdout, "some-field", v);
    return 0;
}

当我尝试编译它时,出现错误:

test.c:15:5: error: use of undeclared identifier 'db_put_u8'
    db_put(stdout, "some-field", v);
    ^
test.c:9:55: note: expanded from macro 'db_put'
#define db_put(con, name, val) _Generic(val, uint8_t: db_put_u8)(con, name, val)
                                                      ^
1 error generated.

_Generic做得对吗? generic selection 语法没有明确限制其中的调用宏。有人可以解释我做错了什么以及如何解决吗?

宏 db_put_u8 将扩展为 do ... while(...) 语句,这不是表达式。 _Generic 需要表达式,因此观察到错误。您应该提供一个名为 db_put_u8 的函数。函数名称衰减为指向作为表达式的函数的指针。

另一种方法是在 _Generic 中用 'fputs(name "<- " #val, con)' 替换 db_put_u8 并删除最后的 '(name, val,con)'

    #define db_put(con, name, val) \
        _Generic(val, uint8_t: fputs(name "<- " #val, con))

如果无法更改 db_put_u8,则可以使用名为 statement expression 的 C 扩展。它得到 GCC 和 CLANG 的支持。基本上它允许放置任意语句信息 ({ ... })。最后计算的表达式作为结果返回。

    #define db_put(con, name, val) \
        _Generic(val, uint8_t: ({db_put_u8(con, name, val);}))

这一行:

#define db_put(con, name, val) _Generic(val, uint8_t: db_put_u8)(con, name, val)

首先得改成:

#define db_put(con, name, val) _Generic(val, uint8_t: db_put_u8(con, name, val))

因为 _Generic 在编译时工作,而不是预处理器时。并且经过预处理后,db_put_u8 不会展开,因为它不作为宏存在。 db_put_u8(arg1, arg2, arg3),但是,确实如此,因此您必须将括号放在泛型中。但是,还有另一个错误:_Generic 需要一个 eression,因此 do while 循环将不起作用。但是在这种情况下,您甚至不需要将宏包含在一个循环中,因为它只是一个语句,因此它没有包含许多语句的宏的潜在问题。您的最终程序应如下所示:

#include <stdio.h>
#include <stdint.h>

#define db_put_u8(con, name, val) \
        fputs(name "<- " #val, con)

#define db_put(con, name, val) _Generic(val, uint8_t: db_put_u8(con, name, val))

int main(void)
{
    uint8_t v = 10;
    db_put(stdout, "some-field", v);
    return 0;
}

编辑: 针对评论,有一个解决方案。只要 db_put_u8 中要做的第二件事也是一个语句,您就可以使用逗号运算符和更多的括号。这是一个用于测试的示例程序:

#include <stdio.h>
#include <stdint.h>

#define db_put_u8(con, name, val) \
   (fputs(name "<- " #val, con), \
    fputs("\nAnother test line\n", con))

#define db_put(con, name, val) \
    (_Generic(val, uint8_t: db_put_u8(con, name, val)))

int main(void)
{
    uint8_t v = 10;
    db_put(stdout, "some-field", v);
    return 0;
}

当我运行这个时,它输出:

some-field<- v
Another test line