有一个人所共知 问题,为C99中的各种宏提供空的args。
示例:
#define FOO(...) printf(__VA_ARGS__)
#define BAR(fmt, ...) printf(fmt, __VA_ARGS__)
FOO("this works fine");
BAR("this breaks!");根据BAR()标准,上述C99的使用确实是不正确的,因为它将扩展到:
printf("this breaks!",);注意后面的逗号--不可行。
一些编译器(例如: Visual 2010)会悄悄地为您去掉这个后缀逗号。其他编译器(例如: GCC)支持将##放在__VA_ARGS__前面,如下所示:
#define BAR(fmt, ...) printf(fmt, ##__VA_ARGS__)但是是否有一种符合标准的方法来获得这种行为呢?也许使用多个宏?
现在,##版本看起来相当受支持(至少在我的平台上是这样),但我更愿意使用符合标准的解决方案。
先发制人:我知道我可以写一个小函数。我试着用宏来做这件事。
编辑:下面是一个示例(虽然很简单),说明我为什么要使用BAR():
#define BAR(fmt, ...) printf(fmt "\n", ##__VA_ARGS__)
BAR("here is a log message");
BAR("here is a log message with a param: %d", 42);这会自动为BAR()日志语句添加一个换行符,前提是fmt总是一个双引号的C-字符串。它不将换行符打印为单独的printf(),如果日志记录是行缓冲的,并且异步地来自多个源,这是有利的。
发布于 2011-04-08 01:08:09
可以避免使用GCC的,##__VA_ARGS__扩展,如果您愿意接受一些硬编码的上限,您可以传递给您的变量宏的数量,如理查德·汉森对这个问题的回答所描述的。但是,据我所知,如果您不希望有任何这样的限制,就不可能只使用C99指定的预处理器特性;您必须使用该语言的一些扩展。clang和icc已经采用了GCC的这一扩展,但MSVC没有。
早在2001年,我就在文档N976中写下了GCC的标准化扩展(以及相关的扩展,允许您使用__VA_ARGS__以外的名称作为rest参数),但这没有得到委员会的任何响应;我甚至不知道是否有人读过它。2016年,N2023再次提出了这一建议,我鼓励任何知道该提案将如何在评论中告知我们的人。
发布于 2012-06-23 20:19:19
有一个你可以使用的论点计数技巧。
以下是实现jwd问题中的第二个BAR()示例的一种标准兼容的方法:
#include <stdio.h>
#define BAR(...) printf(FIRST(__VA_ARGS__) "\n" REST(__VA_ARGS__))
/* expands to the first argument */
#define FIRST(...) FIRST_HELPER(__VA_ARGS__, throwaway)
#define FIRST_HELPER(first, ...) first
/*
* if there's only one argument, expands to nothing. if there is more
* than one argument, expands to a comma followed by everything but
* the first argument. only supports up to 9 arguments but can be
* trivially expanded.
*/
#define REST(...) REST_HELPER(NUM(__VA_ARGS__), __VA_ARGS__)
#define REST_HELPER(qty, ...) REST_HELPER2(qty, __VA_ARGS__)
#define REST_HELPER2(qty, ...) REST_HELPER_##qty(__VA_ARGS__)
#define REST_HELPER_ONE(first)
#define REST_HELPER_TWOORMORE(first, ...) , __VA_ARGS__
#define NUM(...) \
SELECT_10TH(__VA_ARGS__, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE,\
TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway)
#define SELECT_10TH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, ...) a10
int
main(int argc, char *argv[])
{
BAR("first test");
BAR("second test: %s", "a string");
return 0;
}同样的技巧也适用于:
解释
策略是将__VA_ARGS__分为第一个参数和其余参数(如果有的话)。这样就可以在第一个参数之后插入内容,而在第二个参数之前插入内容(如果存在的话)。
FIRST()
这个宏只是扩展到第一个参数,放弃了其余的参数。
实现是简单明了的。throwaway参数确保FIRST_HELPER()获得两个参数,这是必需的,因为...至少需要一个参数。有一个论点,它扩展如下:
FIRST(firstarg)FIRST_HELPER(firstarg, throwaway)firstarg在两个或两个以上的情况下,它扩展如下:
FIRST(firstarg, secondarg, thirdarg)FIRST_HELPER(firstarg, secondarg, thirdarg, throwaway)firstargREST()
这个宏扩展到除第一个参数之外的所有内容(如果有多个参数,则包括第一个参数之后的逗号)。
这个宏的实现要复杂得多。一般策略是计算参数的数量(一个或多个),然后扩展到REST_HELPER_ONE() (如果只给出一个参数)或REST_HELPER_TWOORMORE() (如果给定两个或多个参数)。REST_HELPER_ONE()只是扩展到零--在第一个参数之后没有参数,所以剩下的参数是空集合。REST_HELPER_TWOORMORE()也很简单--它扩展为逗号,后面跟着除第一个参数之外的所有内容。
参数使用NUM()宏进行计数。如果只给出一个参数,则这个宏扩展到ONE;如果给出两个到九个参数,则扩展为TWOORMORE;如果给出10个或多个参数,则中断(因为它扩展到第10个参数)。
NUM()宏使用SELECT_10TH()宏来确定参数的数量。顾名思义,SELECT_10TH()只是扩展到它的第10个参数。由于省略号,SELECT_10TH()至少需要传递11个参数(标准规定必须至少有一个参数用于省略)。这就是为什么NUM()将throwaway作为最后一个参数传递(如果没有它,将一个参数传递给NUM()只会导致10个参数被传递给SELECT_10TH(),这将违反标准)。
REST_HELPER_ONE()或REST_HELPER_TWOORMORE()的选择是通过将REST_HELPER_与NUM(__VA_ARGS__)在REST_HELPER2()中的扩展连接起来完成的。请注意,REST_HELPER()的目的是确保在与REST_HELPER_连接之前完全展开NUM(__VA_ARGS__)。
有一个论点的扩展如下:
REST(firstarg)REST_HELPER(NUM(firstarg), firstarg)REST_HELPER2(SELECT_10TH(firstarg, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway), firstarg)REST_HELPER2(ONE, firstarg)REST_HELPER_ONE(firstarg)有两个或两个以上参数的扩展如下:
REST(firstarg, secondarg, thirdarg)REST_HELPER(NUM(firstarg, secondarg, thirdarg), firstarg, secondarg, thirdarg)REST_HELPER2(SELECT_10TH(firstarg, secondarg, thirdarg, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, TWOORMORE, ONE, throwaway), firstarg, secondarg, thirdarg)REST_HELPER2(TWOORMORE, firstarg, secondarg, thirdarg)REST_HELPER_TWOORMORE(firstarg, secondarg, thirdarg), secondarg, thirdarg发布于 2011-12-29 21:48:19
不是一般的解决方案,但在printf的情况下,您可以追加如下新行:
#define BAR_HELPER(fmt, ...) printf(fmt "\n%s", __VA_ARGS__)
#define BAR(...) BAR_HELPER(__VA_ARGS__, "")我认为它忽略了没有在格式字符串中引用的任何额外的arg。所以你甚至可以逃脱:
#define BAR_HELPER(fmt, ...) printf(fmt "\n", __VA_ARGS__)
#define BAR(...) BAR_HELPER(__VA_ARGS__, 0)我不敢相信C99在没有标准方法的情况下被批准了。C++11也存在这一问题。
https://stackoverflow.com/questions/5588855
复制相似问题