Variable number of parameters in function in C++
Solution 1
These are called Variadic functions. Wikipedia lists example code for C++.
To portably implement variadic functions in the C programming language, the standard stdarg.h header file should be used. The older varargs.h header has been deprecated in favor of stdarg.h. In C++, the header file
cstdarg
should be used.To create a variadic function, an ellipsis (
...
) must be placed at the end of a parameter list. Inside the body of the function, a variable of typeva_list
must be defined. Then the macrosva_start(va_list, last fixed param)
,va_arg(va_list, cast type)
,va_end(va_list)
can be used. For example:
#include <stdarg.h>
double average(int count, ...)
{
va_list ap;
int j;
double tot = 0;
va_start(ap, count); //Requires the last fixed parameter (to get the address)
for(j=0; j<count; j++)
tot+=va_arg(ap, double); //Requires the type to cast to. Increments ap to the next argument.
va_end(ap);
return tot/count;
}
Solution 2
The real C++ solution is variadic templates. You'll need a fairly recent compiler and enable C++11 support if needed.
Two ways to handle the "do the same thing with all function arguments" problem: recursively, and with an ugly (but very very Standards compliant) solution.
The recursive solution looks somewhat like this:
template<typename... ArgTypes>
void print(ArgTypes... args);
template<typename T, typename... ArgTypes>
void print(T t, ArgTypes... args)
{
std::cout << t;
print(args...);
}
template<> void print() {} // end recursion
It generates one symbol for each collection of arguments, and then one for each step into the recursion. This is suboptimal to say the least, so the awesome C++ people here at SO thought of a great trick abusing the side effect of a list initialization:
struct expand_type {
template<typename... T>
expand_type(T&&...) {}
};
template<typename... ArgTypes>
void print(ArgTypes... args)
{
expand_type{ 0, (std::cout << args, 0)... };
}
Code isn't generated for a million slightly different template instantiations, and as a bonus, you get preserved order of you function arguments. See the other answer for the nitty gritty details of this solution.
Solution 3
In C++11 and later you can also use initializer lists.
int sum(const initializer_list<int> &il)
{
int nSum = 0;
for (auto x: il)
nSum += x;
return nsum;
}
cout << sum( { 3, 4, 6, 9 } );
Solution 4
Aside from the other answers, if you're just trying to pass an array of integers, why not:
void func(const std::vector<int>& p)
{
// ...
}
std::vector<int> params;
params.push_back(1);
params.push_back(2);
params.push_back(3);
func(params);
You can't call it in parameter, form, though. You'd have to use any of the variadic function listed in your answers. C++0x will allow variadic templates, which will make it type-safe, but for now it's basically memory and casting.
You could emulate some sort of variadic parameter->vector thing:
// would also want to allow specifying the allocator, for completeness
template <typename T>
std::vector<T> gen_vec(void)
{
std::vector<T> result(0);
return result;
}
template <typename T>
std::vector<T> gen_vec(T a1)
{
std::vector<T> result(1);
result.push_back(a1);
return result;
}
template <typename T>
std::vector<T> gen_vec(T a1, T a2)
{
std::vector<T> result(1);
result.push_back(a1);
result.push_back(a2);
return result;
}
template <typename T>
std::vector<T> gen_vec(T a1, T a2, T a3)
{
std::vector<T> result(1);
result.push_back(a1);
result.push_back(a2);
result.push_back(a3);
return result;
}
// and so on, boost stops at nine by default for their variadic templates
Usage:
func(gen_vec(1,2,3));
Solution 5
See Variadic functions in C, Objective-C, C++, and D
You need to include stdarg.h and then use va_list
, va_start
, va_arg
and va_end
, as the example in the Wikipedia article shows. It's a bit more cumbersome than in Java or C#, because C and C++ have only limited built-in support for varargs.
AndreyAkinshin
Updated on April 16, 2020Comments
-
AndreyAkinshin about 4 years
How I can have variable number of parameters in my function in C++.
Analog in C#:
public void Foo(params int[] a) { for (int i = 0; i < a.Length; i++) Console.WriteLine(a[i]); } public void UseFoo() { Foo(); Foo(1); Foo(1, 2); }
Analog in Java:
public void Foo(int... a) { for (int i = 0; i < a.length; i++) System.out.println(a[i]); } public void UseFoo() { Foo(); Foo(1); Foo(2); }
-
Licky Lindsay over 14 yearsI think that example is C.. but close enough.
-
Carl Norum over 14 yearsC usually compiles fine in C++ compilers.
-
Licky Lindsay over 14 yearsI assume it compiles but the use of <stdarg.h> header instead of <cstdarg> flags it as clearly "C".
-
Stephan202 over 14 years@Licky: as stated, I copied the code from Wikipedia. And I highlighted the remark that for C++ the
cstdarg
header should be used. Seems clear to me. -
David Thornley over 14 yearsIf you use cstdarg, won't that leave va_list etc. in the std namespace? Or are those macros in cstdarg?
-
Licky Lindsay over 14 years@Stephan202 my first reply must have been before you copied the example and added the highlighting.
-
Michael Burr over 14 yearsFWIW - I always use the old C-style header names even in C++. The new names still feel foreign and they provide no value. I'm not the only one: blogs.msdn.com/vcblog/archive/2008/08/28/…
-
Michael Burr over 14 years@David Thornley -
va_list
is a type. Most everything else in cstdarg/stdarg.h are macros. -
Michael Burr over 14 yearsOh - and the cstdarg might put
va_list
in the global namespace. It has to put it in thestd
namespace, but is allowed to also put it in the global namespace (a big reason why the C++ specific headers really provide no value). -
Michael Burr over 14 yearsOn one very important thing that should be added to the answer here is that the types of things that may be passed in the varargs part of the parameter list is severely limited - basically only built in types, enumerations, pointers, and POD types.
-
Stephan202 over 14 years@Michael: feel free to edit the answer! It sounds like you know more about this subject than me.
-
UncleBens over 14 yearsRe: C headers in C++. Don't C++ versions clean up some namespace pollution (undefining macros) and declare additional overloads. E.g
<cmath>
should be very different from<math.h>
? -
kay about 6 yearsYou're only talking about C here, not C++ (at least not current standards).
-
Morse about 6 yearsPlease check how to answer and also use formatting tools to indent code
-
DanyAlejandro about 5 yearsvariadic templates is definitely the C++ way, but after writing the code for making a simple printf() wrapper, I'll stick with cstdarg just because it works, it's standards compliant, and it is readable enough for me not to have to google what that code does in 6 months. Gosh, take pity of the poor soul that will maintain your code in the future...