Mot*_*tti 29
没有办法计算a的长度va_list
,这就是你需要printf
类似函数中的格式字符串的原因.
可用于处理a 的唯一函数宏va_list
是:
va_start
- 开始使用 va_list
va_arg
- 得到下一个论点 va_end
- 停止使用 va_list
va_copy
(自C++ 11和C99) - 复制 va_list
请注意,您需要调用va_start
并va_end
在相同的范围内,这意味着您不能将它包装在一个实用程序类中,该实用程序类va_start
在其构造函数和va_end
析构函数中调用(我被这一次咬过).
例如,这个班级毫无价值:
class arg_list {
va_list vl;
public:
arg_list(const int& n) { va_start(vl, n); }
~arg_list() { va_end(vl); }
int arg() {
return static_cast<int>(va_arg(vl, int);
}
};
Run Code Online (Sandbox Code Playgroud)
GCC输出以下错误
t.cpp:在构造函数中
arg_list::arg_list(const int&)
:
第7行:错误:va_start
在函数中使用,
由于-Wfatal-errors导致固定args 编译终止.
mbo*_*t35 12
尚未提及的一种方法是使用预处理器宏来使用va_list长度作为第一个参数来调用variadict函数,并且还沿着参数转发.这有点像"可爱"的解决方案,但不需要手动输入参数列表长度.
假设您具有以下功能:
int Min(int count, ...) {
va_list args;
va_start(args, count);
int min = va_arg(args, int);
for (int i = 0; i < count-1; ++i) {
int next = va_arg(args, int);
min = min < next ? min : next;
}
va_end(args);
return min;
}
Run Code Online (Sandbox Code Playgroud)
这个想法是你有一个预处理器宏,能够通过使用掩码来计算参数的数量__VA_ARGS__
.有一些很好的预处理器库可用于确定__VA_ARGS__
长度,包括P99和Boost预处理器,但我不会在这个答案中留下漏洞,这里是如何做到的:
#define IS_MSVC _MSC_VER && !__INTEL_COMPILER
/**
* Define the macros to determine variadic argument lengths up to 20 arguments. The MSVC
* preprocessor handles variadic arguments a bit differently than the GNU preprocessor,
* so we account for that here.
*/
#if IS_MSVC
#define MSVC_HACK(FUNC, ARGS) FUNC ARGS
#define APPLY(FUNC, ...) MSVC_HACK(FUNC, (__VA_ARGS__))
#define VA_LENGTH(...) APPLY(VA_LENGTH_, 0, ## __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#else
#define VA_LENGTH(...) VA_LENGTH_(0, ## __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#endif
/**
* Strip the processed arguments to a length variable.
*/
#define VA_LENGTH_(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, N, ...) N
Run Code Online (Sandbox Code Playgroud)
注意:上面的很多噪音都是对MSVC的解决方案.
通过上面定义,您可以创建一个宏来执行所有基于长度的操作:
/**
* Use the VA_LENGTH macro to determine the length of the variadict args to
* pass in as the first parameter, and forward along the arguments after that.
*/
#define ExecVF(Func, ...) Func(VA_LENGTH(__VA_ARGS__), __VA_ARGS__)
Run Code Online (Sandbox Code Playgroud)
只要它以int count
参数开头,该宏就能调用任何variadict函数.简而言之,而不是使用:
int result = Min(5, 1, 2, 3, 4, 5);
Run Code Online (Sandbox Code Playgroud)
您可以使用:
int result = ExecVF(Min, 1, 2, 3, 4, 5);
Run Code Online (Sandbox Code Playgroud)
这是Min的模板版本,使用相同的方法:https://gist.github.com/mbolt35/4e60da5aaec94dcd39ca
Kei*_*son 10
变量函数没有直接的方法来确定传递了多少个参数.(至少没有可移植的方式; <stdarg.h>
接口不提供该信息.)
有几种间接方式.
两个最常见的是:
*printf()
和*scanf()
功能的家庭使用了这种机制.exec*()
系列函数使用空指针标记参数的结尾.但还有其他可能性:
func(ARG_INT, 42, ARG_STRING, "foo", ARG_DOUBLE, 1.25, ARG_END);
func("-i", 42, "-s", "foo", "-d", 1.25, "");
您甚至可以为全局变量赋值以指定参数的数量:
func_arg_count = 3;
func(1, 2, 3);
Run Code Online (Sandbox Code Playgroud)
这将是丑陋但完全合法的.
在所有这些技术中,调用者完全有责任传递一致的参数; 被调用者只能假设其参数是正确的.
请注意,处理传递给它的所有参数不需要可变参数函数.例如,这个:
printf("%d\n", 10, 20);
Run Code Online (Sandbox Code Playgroud)
将打印10
并悄然忽略20
.几乎没有任何理由可以利用该功能.