可变参数函数模板
Nov. 30. 2015
2015年11月30日
A function with variable number of arguments is called a variadic function, and also known as variable argument function [1]. Due to its flexibility in size of the list of input arguments, it is very useful especially in system programming. Its usual use cases include summing of numbers, concatenating strings, and so on. Typical examples include the printf in C programming language, execl and execlp in Unix, _execl and _execlp in Windows, and many others. This post introduces how to declare/define and use such functions.
具有可变数量的参数的函数称为可变参数函数 ,也称为可变参数函数 [1]。 由于它在输入参数列表大小方面的灵活性,因此特别有用,特别是在系统编程中。 它的通常用例包括数字求和,连接字符串等。 典型示例包括C编程语言中的printf , Unix中的 execl和execlp , Windows中的 _execl和_execlp以及许多其他示例 。 这篇文章介绍了如何声明/定义和使用这样的函数。
如何声明可变参数 (How to declare a variadic function)
Declaration of a variadic function is almost same as an “ordinary” function, except that its formal parameter list ends with an ellipsis. Below is an example of the declaration of a variadic function.
可变参数函数的声明与“普通”函数几乎相同,不同之处在于其形式参数列表以省略号结尾。 下面是可变参数声明的示例。
int sum (int x, ...);
In C programming language, at least one named formal parameter must appear before the ellipsis parameter. Whereas in C++, variadic function without any named formal parameter is allowed, although no argument passed to such function is accessible in this case [2].
在C编程语言中,至少一个命名形式参数必须出现在省略号参数之前。 在C ++中 ,允许使用不带任何命名形式参数的可变参数函数,尽管在这种情况下,无法访问传递给该函数的参数[2]。
The comma between the last named parameter and the ellipsis is optional [2], i.e. “, …”, in the above declaration can be replaced by “…”.
姓氏参数和省略号之间的逗号是可选的[2],即上述声明中的“,...”可以替换为“ ...”。
如何使用可变参数函数 (How to use a variadic function)
Possibly the first example everybody would think of when discussing variadic function is the function printf. Next, we take a look at the source code of this function [3].
讨论可变参数函数时,每个人可能想到的第一个示例就是函数printf 。 接下来,我们看一下该函数的源代码[3]。
int __printf ( const char *format, ... )
{
va_list arg;
int done;
va_start (arg, format);
done = vfprintf (stdout, format, arg);
va_end (arg);
return done;
}
Clearly, the function printf calls vfprintf to implement all the “magics”. However, as the declaration of printf is enough for demonstrating how to define a variadic function, we are not going to dig deeper into the source code of vfprintf. Below the declaration of vfprintf is given.
显然,函数printf调用vfprintf来实现所有“ 魔术 ”。 但是,由于printf的声明足以说明如何定义可变参数函数,因此我们不会深入研究vfprintf的源代码。 在下面给出了vfprintf的声明。
int vfprintf ( FILE *s, const CHAR_T *format, va_list ap );
In both printf and vfprintf, three macros and one special data type are used to implement variadic functions. They are va_start, va_arg, va_end and va_list. They are defined in the header file stdarg.h (cstdarg for C++). To get a glimpse of its implementation, we take a look at an early version of the header file stdarg.h (see this discussion for details on how to check the source code of the latest stdarg.h).
在printf和vfprintf中 ,都使用三个宏和一种特殊的数据类型来实现可变参数功能。 它们是va_start , va_arg , va_end和va_list 。 它们在头文件stdarg.h (对于C ++为cstdarg )中定义。 为了了解其实现,我们看一下头文件stdarg.h的早期版本(有关如何检查最新stdarg.h的源代码的详细信息,请参见此讨论 )。
#define __va_argsiz(t) (((sizeof(t) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))// several lines are skipped #if defined __GNUC__ && __GNUC__ >= 3
typedef __builtin_va_list va_list;
#else
typedef char* va_list;
#endif // several lines are skipped #ifdef __GNUC__
#define va_start ( ap, pN ) ((ap) = ((va_list) __builtin_next_arg(pN)))
#else
#define va_start ( ap, pN ) ((ap) = ((va_list) (&pN) + __va_argsiz(pN)))
#endif // several lines are skipped #define va_arg(ap, t) (((ap) = (ap) + __va_argsiz(t)), *((t*) (void*) ((ap) - __va_argsiz(t)))) // several lines are skipped #define va_end(ap) ((void)0)
Now lets summarize these data type and macros.
现在让我们总结这些数据类型和宏。
typedef va_list
A list of variable arguments, a char* variable which will be initialized by the address of the first variable argument.
变量参数列表,一个char *变量,它将由第一个变量参数的地址初始化。
void va_start(va_list ap, pN);
A macro which takes a va_list variable, ap, and the last named parameter, pN, as its input, and initializes ap by adding the size of pN to its address. Therefore, after invoking va_start, ap points to the address of the first unnamed parameter. (This is why in C programming language, a variadic function must have at least one named parameter.)
一个宏,它使用va_list变量ap和最后一个命名参数pN作为其输入,并通过将pN的大小添加到其地址来初始化ap。 因此,在调用va_start之后,ap指向第一个未命名参数的地址。 ( 这就是为什么在 C 编程语言中,可变参数必须至少具有一个命名参数。 )
T va_arg(va_list ap, T);
A macro retrieving value of the next unnamed parameter. It takes the va_list variable ap and the type of the next unnamed parameter T as input, and return the value of the next unnamed parameter.
下一个未命名参数的宏检索值。 它以va_list变量ap和下一个未命名参数T的类型作为输入,并返回下一个未命名参数的值。
It always assumes that an actual argument is passed. So if va_arg() is called when there is no more arguments in ap, the behavior is undefined [4]. The following two ways can solve this problem.
它始终假定传递了实际参数。 因此,如果ap中没有更多参数时调用va_arg(),则该行为是不确定的[4]。 以下两种方法可以解决此问题。
when call a variadic function, put a special value, say
0
orNULL
, to the end of the list of the input arguments;当调用可变参数函数时,在输入参数列表的末尾放置一个特殊值,例如
0
或NULL
;- pass a variable as a named argument which gives the number of arguments being passed. 将变量作为命名参数传递,该变量给出要传递的参数数量。
void va_end(va_list ap);
A macro to cleanup the va_list
object initialized by a call to va_start
or va_copy
[2] by simply setting the pointer to NULL
.
只需将指针设置为NULL
即可清理通过调用va_start
或va_copy
[2]初始化的va_list
对象的宏。
例子 (Examples)
Well, with all the knowledge covered above, we are now ready to use variadic functions. Next, I close the discussion in this note by providing an implementation of the variadic function declared at the beginning of this note.
好了,有了以上所有知识,我们现在可以使用可变参数函数了 。 接下来,我通过提供本注释开头声明的可变参数函数的实现来结束本注释的讨论。
template<typename T>
T sum (T x, ...)
{
va_list summands;
va_start(summands, x);
T result = 0;
T summand = va_arg(summands, T);
while (summand != 0)
{
result += summand;
summand = va_arg(summands, T);
}
va_end(summands);
return result;
}
后记 (PostScript)
Actually, similar syntax is also extensively used in templates and macros. Here to demonstrate how to use them, I just give each an example, for more details you can very easily check the references online.
实际上,模板和宏中也广泛使用了类似的语法。 在这里演示如何使用它们,我仅举一个例子,有关更多详细信息,您可以轻松地在线查看参考。
/* variadic_template.cc *//* variadic_macro.cc */
#include <cstdio>
#include <syscall.h>
#include <sys/types.h>
#include <unistd.h>#define ERR_MSG(frmt, ...) \
fprintf(stderr, "**** Error ****\n"); \
fprintf(stderr, "File: %s, Line: %d\n", __FILE__, __LINE__); \
fprintf(stderr, "Function: %s, PID: %d, TID: %ld\n", \
__func__, getpid(), syscall(SYS_gettid)); \
fprintf(stderr, frmt, __VA_ARGS__); \
fprintf(stderr, "\n");int main()
{
printf("Testing variadic macro\n");
ERR_MSG("%s %s %s", "Don't worry!",
"This is just a test message!",
"^_^\n");
return 0;
}
翻译自: https://medium.com/swlh/variadic-functions-3419c287a0d2
可变参数函数模板