使用未知数量的参数调用可变参数函数
Calling a variadic function with an unknown number of parameters
假设我有一个函数,它接受可变数量的参数:我想从其他地方调用这个函数,构建参数列表,但事先不知道我需要多少参数。
抱歉,没有很好地解释,希望这段代码能让我的问题更清楚:
void foo(int n, ...) {
va_list vl;
va_start(vl,n);
for (int i = 0; i<n; i++) {
// Do something to each passed variable
}
}
正在从这个调用该函数:
void bar(int howManyParams) {
// Here I want to call foo() with howManyParams parameters
// (values are irrelevant for the question)
//
// I.e. for howManyParams = 1, we should call foo(0)
// for howManyParams = 2, we should call foo(0,0)
// for howManyParams = 3, we should call foo(0,0,0)
// etc.
//
}
您将需要一个终止参数,它可能是 NULL 或其他不应出现在您的实际参数中的参数。在您的函数内部,您可以遍历参数,直到到达终止 NULL 或您选择的任何其他值来表示结束。
如果不能保留一个特殊值来指示列表的结尾,则为每个参数传递 2 个参数。有点浪费,但确实允许按顺序自动生成代码而不受值限制。
foo(0);
foo(1, Params1, 0);
foo(1, Params1, 1, Params2, 0);
foo(1, Params1, 1, Params2, 1, Params3, 0);
实际上,在 运行 时间构建一个可变长度的参数列表——我很确定你正在尝试这样做——是非常棘手的。在标准 C 中根本无法做到这一点,但您可以尝试各种技巧。
也许最好的是 http://sourceware.org/libffi/ 的 "Foreign Function Interface Library"。
另见 C FAQ 列表中的问题 15.13:http://c-faq.com/varargs/invvarargs.html
另请参阅以前的 Whosebug 问题:
How to call functions by their pointers passing multiple arguments in C?
在运行时执行 OP 要求的更简单的方法可能是依赖标准容器,如 std::vector
s 和其他容器。
无论如何,为了完整起见,这里有一个示例,说明如何在编译时创建可变参数包并稍后用于调用函数:
#include<utility>
#include<tuple>
#include<iostream>
auto params(std::index_sequence<0>) {
return std::tuple<std::size_t>{};
}
template<std::size_t I, std::size_t... O>
auto params(std::index_sequence<I, O...>) {
auto tup = std::tuple<std::size_t>{ sizeof...(O) };
auto seq = std::make_index_sequence<sizeof...(O)>{};
return std::tuple_cat(tup, params(seq));
}
void foo() {
std::cout << "done." << std::endl;
}
template<typename Arg, typename... Args>
void foo(Arg &&arg, Args&&... args) {
std::cout << "arg: " << arg << ", still to be elaborated: " << sizeof...(Args) << std::endl;
foo(std::forward<Args>(args)...);
}
template<typename... Args, std::size_t... Indexes>
void invoke(std::tuple<Args...> &tup, std::index_sequence<Indexes...>) {
foo(std::get<Indexes>(tup)...);
}
template<std::size_t N>
void bar(std::integral_constant<std::size_t, N> size) {
auto seq = std::make_index_sequence<N>{};
auto tup = params(seq);
invoke(tup, seq);
}
int main() {
bar(std::integral_constant<std::size_t, 3>{});
bar(std::integral_constant<std::size_t, 5>{});
}
不幸的是,因为它必须在编译时完全解决,所以 bar
函数的参数不能是它自己的 std::size_t
。
相反,可以使用 std::integral_constant
来做到这一点。
当我需要做这样的事情时,我会使用 "switch-fan"。
switch( n ){
case 1: foo(0); break;
case 2: foo(0,0); break;
case 3: foo(0,0,0); break;
}
假设我有一个函数,它接受可变数量的参数:我想从其他地方调用这个函数,构建参数列表,但事先不知道我需要多少参数。
抱歉,没有很好地解释,希望这段代码能让我的问题更清楚:
void foo(int n, ...) {
va_list vl;
va_start(vl,n);
for (int i = 0; i<n; i++) {
// Do something to each passed variable
}
}
正在从这个调用该函数:
void bar(int howManyParams) {
// Here I want to call foo() with howManyParams parameters
// (values are irrelevant for the question)
//
// I.e. for howManyParams = 1, we should call foo(0)
// for howManyParams = 2, we should call foo(0,0)
// for howManyParams = 3, we should call foo(0,0,0)
// etc.
//
}
您将需要一个终止参数,它可能是 NULL 或其他不应出现在您的实际参数中的参数。在您的函数内部,您可以遍历参数,直到到达终止 NULL 或您选择的任何其他值来表示结束。
如果不能保留一个特殊值来指示列表的结尾,则为每个参数传递 2 个参数。有点浪费,但确实允许按顺序自动生成代码而不受值限制。
foo(0);
foo(1, Params1, 0);
foo(1, Params1, 1, Params2, 0);
foo(1, Params1, 1, Params2, 1, Params3, 0);
实际上,在 运行 时间构建一个可变长度的参数列表——我很确定你正在尝试这样做——是非常棘手的。在标准 C 中根本无法做到这一点,但您可以尝试各种技巧。
也许最好的是 http://sourceware.org/libffi/ 的 "Foreign Function Interface Library"。
另见 C FAQ 列表中的问题 15.13:http://c-faq.com/varargs/invvarargs.html
另请参阅以前的 Whosebug 问题:
How to call functions by their pointers passing multiple arguments in C?
在运行时执行 OP 要求的更简单的方法可能是依赖标准容器,如 std::vector
s 和其他容器。
无论如何,为了完整起见,这里有一个示例,说明如何在编译时创建可变参数包并稍后用于调用函数:
#include<utility>
#include<tuple>
#include<iostream>
auto params(std::index_sequence<0>) {
return std::tuple<std::size_t>{};
}
template<std::size_t I, std::size_t... O>
auto params(std::index_sequence<I, O...>) {
auto tup = std::tuple<std::size_t>{ sizeof...(O) };
auto seq = std::make_index_sequence<sizeof...(O)>{};
return std::tuple_cat(tup, params(seq));
}
void foo() {
std::cout << "done." << std::endl;
}
template<typename Arg, typename... Args>
void foo(Arg &&arg, Args&&... args) {
std::cout << "arg: " << arg << ", still to be elaborated: " << sizeof...(Args) << std::endl;
foo(std::forward<Args>(args)...);
}
template<typename... Args, std::size_t... Indexes>
void invoke(std::tuple<Args...> &tup, std::index_sequence<Indexes...>) {
foo(std::get<Indexes>(tup)...);
}
template<std::size_t N>
void bar(std::integral_constant<std::size_t, N> size) {
auto seq = std::make_index_sequence<N>{};
auto tup = params(seq);
invoke(tup, seq);
}
int main() {
bar(std::integral_constant<std::size_t, 3>{});
bar(std::integral_constant<std::size_t, 5>{});
}
不幸的是,因为它必须在编译时完全解决,所以 bar
函数的参数不能是它自己的 std::size_t
。
相反,可以使用 std::integral_constant
来做到这一点。
当我需要做这样的事情时,我会使用 "switch-fan"。
switch( n ){
case 1: foo(0); break;
case 2: foo(0,0); break;
case 3: foo(0,0,0); break;
}