tags:

views:

703

answers:

9

How can I write a function that accepts a variable number of arguments? Is this possible? How?

+7  A: 

The only way is through the use of C style variable arguments, as described here. Note that this is not a recommended practice, as it's not typesafe and error-prone.

Dave Van den Eynde
By error prone I assume you mean potentially very very dangerous? Especially when working with untrusted input.
Kevin Loney
Yes, but because of the type safety issues. Think all of the possible issues that regular printf has: format strings not matching passed arguments, and such. printf uses the same technique, BTW.
Dave Van den Eynde
+8  A: 

C-style variadic functions are supported in C++.

However, most C++ libraries use an alternative idiom e.g. whereas the 'c' printf function takes variable arguments the c++ cout object uses << overloading which addresses type safety and ADTs (perhaps at the cost of implementation simplicity).

Will
+2  A: 

As others have said, C-style varargs. But you can also do something similar with default arguments.

Thomas Padron-McCarthy
+6  A: 

You probably shouldn't, and you can probably do what you want to do in a safer and simpler way. Technically to use variable number of arguments in C you include stdarg.h. From that you'll get the va_list type as well as three functions that operate on it called va_start(), va_arg() and va_end().

#include<stdarg.h>

int maxof(int n_args, ...)
{
    va_list ap;
    va_start(ap, n_args);
    int max = va_arg(ap, int);
    for(int i = 2; i <= n_args; i++) {
        int a = va_arg(ap, int);
        if(a > max) max = a;
    }
    va_end(ap);
    return max;
}

If you ask me, this is a mess. It looks bad, it's unsafe, and it's full of technical details that have nothing to do with what you're conceptually trying to achieve. Instead, consider using overloading or inheritance/polymorphism, builder pattern (as in operator<<() in streams) or default arguments etc. These are all safer: the compiler gets to know more about what you're trying to do so there are more occasions it can stop you before you blow your leg off.

wilhelmtell
Presumably, you cannot pass references to a varargs function because the compiler wouldn't know when to pass by value and when by reference, and because the underlying C macros would not necessarily know what to do with references -- there are already restrictions on what you can pass into a C function with variable arguments because of things like promotion rules.
Jonathan Leffler
@Jonathan: That doesn't really matter. The problem is quite solvable in theory, so ISO WG21 can just say: "This is the spec, now you make it work.". There's no good reason why the C++ compiler makers would have to restrict themselves to C macros. For instance, a trivial solution would be to push RTTI information on the stack before each vararg argument. But as wilhemtell correctly explains, even if the compiler could, you'd still have that nasty interface.
MSalters
@wilhelmtell: is it necessary to provide atleast one argument before the `...` syntax?
Lazer
@Lazer it is not a language or library requirement, but the standard library doesn't give you means to tell the length of the list. You need the caller to give you this information or else somehow figure it out yourself. In the case of `printf()`, for example, the function parses the string argument for special tokens to figure out how many extra arguments it should expect in the variable argument list.
wilhelmtell
+5  A: 

There is no standard C++ way to do this without resorting to C-style varargs (...).

There are of course default arguments that sort of "look" like variable number of arguments depending on the context:

void myfunc( int i = 0, int j = 1, int k = 2 );

// other code...

myfunc();
myfunc( 2 );
myfunc( 2, 1 );
myfunc( 2, 1, 0 );

All four function calls call myfunc with varying number of arguments. If none are given, the default arguments are used. Note however, that you can only omit trailing arguments. There is no way, for example to omit i and give only j.

zdawg
+1  A: 

If you know the range of number of arguments that will be provided, you can always use some function overloading, like

f(int a)
    {int res=a; return res;}
f(int a, int b)
    {int res=a+b; return res;}

and so on...

Dunya Degirmenci
+4  A: 

Apart from varargs or overloading, you could consider to aggregate your arguments in a std::vector or other containers (std::map for example). Something like this:

template <typename T> void f(std::vector<T> const&);
std::vector<int> my_args;
my_args.push_back(1);
my_args.push_back(2);
f(my_args);

In this way you would gain type safety and the logical meaning of these variadic arguments would be apparent.

Surely this approach can have performance issues but you should not worry about them unless you are sure that you cannot pay the price. It is a sort of a a "Pythonic" approach to c++ ...

Francesco
+4  A: 

In C++[01]x there is a way to do variable argument templates which lead to a really elegant and type safe way to have variable argument functions. Bjarne himself gives a nice example of printf using variable argument templates in the C++0xFAQ.

Personally, I consider this so elegant that I wouldn't even bother with a variable argument function in C++ until that compiler has support for C++[01]x variable argument templates.

Omnifarious
+1  A: 

It's possible you want overloading or default parameters - define the same function with defaulted parameters:

void doStuff( int a, double termstator = 1.0, bool useFlag = true )
{
   // stuff
}

void doStuff( double std_termstator )
{
   // assume the user always wants '1' for the a param
   return doStuff( 1, std_termstator );
}

This will allow you to call the method with one of four different calls:

doStuff( 1 );
doStuff( 2, 2.5 );
doStuff( 1, 1.0, false );
doStuff( 6.72 );

... or you could be looking for the v_args calling conventions from C.

Kieveli