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
我想让 _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