views:

436

answers:

4

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);
}
+11  A: 

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 type va_list must be defined. Then the macros va_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;
}
Stephan202
I think that example is C.. but close enough.
Licky Lindsay
C usually compiles fine in C++ compilers.
Carl Norum
I assume it compiles but the use of <stdarg.h> header instead of <cstdarg> flags it as clearly "C".
Licky Lindsay
@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.
Stephan202
If you use cstdarg, won't that leave va_list etc. in the std namespace? Or are those macros in cstdarg?
David Thornley
@Stephan202 my first reply must have been before you copied the example and added the highlighting.
Licky Lindsay
FWIW - 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: http://blogs.msdn.com/vcblog/archive/2008/08/28/the-mallocator.aspx#8904359
Michael Burr
@David Thornley - `va_list` is a type. Most everything else in cstdarg/stdarg.h are macros.
Michael Burr
Oh - and the cstdarg might put `va_list` in the global namespace. It has to put it in the `std` 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
On 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.
Michael Burr
@Michael: feel free to edit the answer! It sounds like you know more about this subject than me.
Stephan202
Re: 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>`?
UncleBens
+2  A: 

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.

Jesper
+1  A: 

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));
GMan
A: 

If you don't care about portability, you could port this C99 code to C++ using gcc's statement expressions:

#include <cstdio>

int _sum(size_t count, int values[])
{
    int s = 0;
    while(count--) s += values[count];
    return s;
}

#define sum(...) ({ \
    int _sum_args[] = { __VA_ARGS__ }; \
    _sum(sizeof _sum_args / sizeof *_sum_args, _sum_args); \
})

int main(void)
{
    std::printf("%i", sum(1, 2, 3));
}

You could do something similar with C++0x' lambda expressions, but the gcc version I'm using (4.4.0) doesn't support them.

Christoph