前言
使用示例:
#ifndef COLOR_H
#define COLOR_H
#define NONE "\033[0m"
#define BLACK "\033[0;30m"
#define L_BLACK "\033[1;30m"
#define RED "\033[0;31m"
#define L_RED "\033[1;31m"
#define GREEN "\033[0;32m"
#define L_GREEN "\033[1;32m"
#define YELLOW "\033[0;33m"
#define L_YELLOW "\033[1;33m"
#define BLUE "\033[0;34m"
#define L_BLUE "\033[1;34m"
#define PURPLE "\033[0;35m"
#define L_PURPLE "\033[1;35m"
#define CYAN "\033[0;36m"
#define L_CYAN "\033[1;36m"
#define WHITE "\033[0;37m"
#define L_WHITE "\033[1;37m"
//#define BOLD "\033[1m"
//#define UNDERLINE "\033[4m"
#define printr(format, ...) do{printf(RED format NONE, ##__VA_ARGS__);}while(0)
#define printg(format, ...) do{printf(GREEN format NONE, ##__VA_ARGS__);}while(0)
#define printy(format, ...) do{printf(YELLOW format NONE, ##__VA_ARGS__);}while(0)
#define printb(format, ...) do{printf(BLUE format NONE, ##__VA_ARGS__);}while(0)
#define printk(format, ...) do{printf(BLACK format NONE, ##__VA_ARGS__);}while(0)
#define printw(format, ...) do{printf(WHITE format NONE, ##__VA_ARGS__);}while(0)
#define printc(format, ...) do{printf(CYAN format NONE, ##__VA_ARGS__);}while(0)
#define printp(format, ...) do{printf(PURPLE format NONE, ##__VA_ARGS__);}while(0)
#define printlr(format, ...) do{printf(L_RED format NONE, ##__VA_ARGS__);}while(0)
#define printlg(format, ...) do{printf(L_GREEN format NONE, ##__VA_ARGS__);}while(0)
#define printly(format, ...) do{printf(L_YELLOW format NONE, ##__VA_ARGS__);}while(0)
#define printlb(format, ...) do{printf(L_BLUE format NONE, ##__VA_ARGS__);}while(0)
#define printlk(format, ...) do{printf(L_BLACK format NONE, ##__VA_ARGS__);}while(0)
#define printlw(format, ...) do{printf(L_WHITE format NONE, ##__VA_ARGS__);}while(0)
#define printlc(format, ...) do{printf(L_CYAN format NONE, ##__VA_ARGS__);}while(0)
#define printlp(format, ...) do{printf(L_PURPLE format NONE, ##__VA_ARGS__);}while(0)
#define fprintr(stream, format, ...) do{fprintf(stream, RED format NONE, ##__VA_ARGS__);}while(0)
#define fprintg(stream, format, ...) do{fprintf(stream, GREEN format NONE, ##__VA_ARGS__);}while(0)
#define fprinty(stream, format, ...) do{fprintf(stream, YELLOW format NONE, ##__VA_ARGS__);}while(0)
#define fprintb(stream, format, ...) do{fprintf(stream, BLUE format NONE, ##__VA_ARGS__);}while(0)
#define fprintk(stream, format, ...) do{fprintf(stream, BLACK format NONE, ##__VA_ARGS__);}while(0)
#define fprintw(stream, format, ...) do{fprintf(stream, WHITE format NONE, ##__VA_ARGS__);}while(0)
#define fprintc(stream, format, ...) do{fprintf(stream, CYAN format NONE, ##__VA_ARGS__);}while(0)
#define fprintp(stream, format, ...) do{fprintf(stream, PURPLE format NONE, ##__VA_ARGS__);}while(0)
#define fprintlr(stream, format, ...) do{fprintf(stream, L_RED format NONE, ##__VA_ARGS__);}while(0)
#define fprintlg(stream, format, ...) do{fprintf(stream, L_GREEN format NONE, ##__VA_ARGS__);}while(0)
#define fprintly(stream, format, ...) do{fprintf(stream, L_YELLOW format NONE, ##__VA_ARGS__);}while(0)
#define fprintlb(stream, format, ...) do{fprintf(stream, L_BLUE format NONE, ##__VA_ARGS__);}while(0)
#define fprintlk(stream, format, ...) do{fprintf(stream, L_BLACK format NONE, ##__VA_ARGS__);}while(0)
#define fprintlw(stream, format, ...) do{fprintf(stream, L_WHITE format NONE, ##__VA_ARGS__);}while(0)
#define fprintlc(stream, format, ...) do{fprintf(stream, L_CYAN format NONE, ##__VA_ARGS__);}while(0)
#define fprintlp(stream, format, ...) do{fprintf(stream, L_PURPLE format NONE, ##__VA_ARGS__);}while(0)
#endif
GNU原文链接
翻译
宏定义可以接受可变参数(和变参函数一样多)。
语法和变参函数很像,例如:
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
这种宏叫做可变参数宏(variadic)。
- 当这样的宏被使用时,最后一个被命名的参数(上面的例子没有命名的参数)后所有的序列,包括逗号,都会成为可变参数。
- 这个参数序列会替换宏当中的标识符
__VA_ARGS__
宏展开的例子:
eprintf("%s:%d: ", input_file, lineno)
-> fprintf(stderr, "%s:%d:", input_file, lineno);
可变参数会完全展开后再插入到该宏中,看起来就和普通参数一样。
你可以使用#
或者##
来字符串化(stringize)参数、连接它的开头或尾随序列。(但是下面会讲##
的一种重要的特殊用法。)
C++扩展
如果你的宏很复杂,你可能想要有更多的名字(而不是仅仅有__VA_ARGS__
) C++ 允许这样的扩展。你可以在...
前紧跟一个参数名,这个名字会被用来作为可变参数。所以上面的例子可以修改成这样:
#define eprintf(args...) fprintf(stderr, args)
如果使用这个扩展,你就不能在同一个宏内再使用__VA_ARGS__
。
不使用扩展
不过,就算不用扩展,你也可以在一个可变参数宏(varidic)中
使用命名参数,例如:
#define eprintf(format, ...) fprintf(stderr, format, __VA_ARGS__)
- 这样的写法看起来更具描述性,但是由于历史原因它不太灵活:你必须在格式化字符串之后提供至少一个参数。(译者注:也就是说,比如你在调用
printf()
时,除了格式化字符串,还要有别的参数) - 在标准C中,你不能省略分隔参数和省略号
...
的逗号。(不过,此限制已经在C++20中取消,并且GNU C从不存在这个限制。)(译者注:总之都加上逗号肯定不错)
另外,如果你的可变参数为空,会有语法错误,因为格式化字符串后会多一个逗号:
eprintf("success!\n",);
-> fprintf(stderr, "success!\n", );
这在C++20中已经修复,GNU CPP也有处理该问题的扩展。
首先,在GNU CPP,以及C++20中,你可以完全省略可变参数:
eprintf("success!\n")
->fprintf(stderr, "success!\n", );
(下面这一段,我看不太懂,可能翻译有误,建议原文。。。)
其次,C++20引入了__VA_OPT__
函数宏。这个宏只能出现在可变参数宏的定义中。如果可变参数序列有任何参数(包括逗号),则__VA_OPT__
扩展为它的参数。但是如果可变参数序列没有任何参数,__VA_OPT__
就不扩展。
(截止。。。。。。。。。。。。。。。。)
#define eprintf(format, ...)\
fprintf(stderr, format __VA_OPT__(,) __VA_ARGS__)
__VA_OPT__
在GNU C和*GNU C++*可用。
GNU CPP扩展
由于历史原因,GNU CPP也提供另一种扩展来处理多余的尾随逗号:
##
会有特殊含义,当它放在逗号和可变参数之间时。尽管引入了__VA_OPT__
,但是为了向后兼容,GNU CPP仍然支持此扩展。
如果你这样写:
#define eprintf(format, ...) fprintf(stderr, format, ##__VA_ARGS__)
当##
前为逗号,或者传递的可变参数为空时,可变参数会被丢弃,然后##
前的逗号会被删除。
上面的解释对于宏参数唯一时的可变参数情况 具有二义性,因为试图区分是空参数还是缺少参数是没有意义的。当符合特定的C标准时,CPP保留逗号。否则逗号作为标准的扩展被删除。
补充
-
C标准要求
__VA_ARGS__
只能出现在可变参数宏的替换列表中。它不能用作宏名称、宏参数名或在不同类型的宏中使用。也可能在公开文本中被禁止,标准具有二义性。除非用于其所定义的目的,否则,我们建议您不要使用它。 -
同样,C++ 禁止在可变参数宏的替换列表之外使用
__VA_OPT__
。 -
可变参数宏随着C99成为C语言的标准部分。
-
GNU CPP以前使用命名可变参数(
args...
,不是...
和__VA_ARGS__
;)来支持它们,这仍然支持向后兼容。
原文
3.6 Variadic Macros
A macro can be declared to accept a variable number of arguments much as a function can. The syntax for defining the macro is similar to that of a function. Here is an example:
#define eprintf(...) fprintf (stderr, __VA_ARGS__)
This kind of macro is called variadic. When the macro is invoked, all the tokens in its argument list after the last named argument (this macro has none), including any commas, become the variable argument. This sequence of tokens replaces the identifier VA_ARGS in the macro body wherever it appears. Thus, we have this expansion:
eprintf ("%s:%d: ", input_file, lineno)
→ fprintf (stderr, "%s:%d: ", input_file, lineno)
The variable argument is completely macro-expanded before it is inserted into the macro expansion, just like an ordinary argument. You may use the ‘#’ and ‘##’ operators to stringize the variable argument or to paste its leading or trailing token with another token. (But see below for an important special case for ‘##’.)
If your macro is complicated, you may want a more descriptive name for the variable argument than VA_ARGS. CPP permits this, as an extension. You may write an argument name immediately before the ‘…’; that name is used for the variable argument. The eprintf macro above could be written
#define eprintf(args...) fprintf (stderr, args)
using this extension. You cannot use VA_ARGS and this extension in the same macro.
You can have named arguments as well as variable arguments in a variadic macro. We could define eprintf like this, instead:
#define eprintf(format, …) fprintf (stderr, format, VA_ARGS)
This formulation looks more descriptive, but historically it was less flexible: you had to supply at least one argument after the format string. In standard C, you could not omit the comma separating the named argument from the variable arguments. (Note that this restriction has been lifted in C++20, and never existed in GNU C; see below.)
Furthermore, if you left the variable argument empty, you would have gotten a syntax error, because there would have been an extra comma after the format string.
eprintf("success!\n", );
→ fprintf(stderr, "success!\n", );
This has been fixed in C++20, and GNU CPP also has a pair of extensions which deal with this problem.
First, in GNU CPP, and in C++ beginning in C++20, you are allowed to leave the variable argument out entirely:
eprintf ("success!\n")
→ fprintf(stderr, "success!\n", );
Second, C++20 introduces the VA_OPT function macro. This macro may only appear in the definition of a variadic macro. If the variable argument has any tokens, then a VA_OPT invocation expands to its argument; but if the variable argument does not have any tokens, the VA_OPT expands to nothing:
#define eprintf(format, ...) \
fprintf (stderr, format __VA_OPT__(,) __VA_ARGS__)
VA_OPT is also available in GNU C and GNU C++.
Historically, GNU CPP has also had another extension to handle the trailing comma: the ‘##’ token paste operator has a special meaning when placed between a comma and a variable argument. Despite the introduction of VA_OPT, this extension remains supported in GNU CPP, for backward compatibility. If you write
#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__)
and the variable argument is left out when the eprintf macro is used, then the comma before the ‘##’ will be deleted. This does not happen if you pass an empty argument, nor does it happen if the token preceding ‘##’ is anything other than a comma.
eprintf ("success!\n")
→ fprintf(stderr, "success!\n");
The above explanation is ambiguous about the case where the only macro parameter is a variable arguments parameter, as it is meaningless to try to distinguish whether no argument at all is an empty argument or a missing argument. CPP retains the comma when conforming to a specific C standard. Otherwise the comma is dropped as an extension to the standard.
The C standard mandates that the only place the identifier VA_ARGS can appear is in the replacement list of a variadic macro. It may not be used as a macro name, macro argument name, or within a different type of macro. It may also be forbidden in open text; the standard is ambiguous. We recommend you avoid using it except for its defined purpose.
Likewise, C++ forbids VA_OPT anywhere outside the replacement list of a variadic macro.
Variadic macros became a standard part of the C language with C99. GNU CPP previously supported them with a named variable argument (‘args…’, not ‘…’ and VA_ARGS), which is still supported for backward compatibility.