399

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

nunos
  • 20,479
  • 50
  • 119
  • 154
  • 58
    At this time with _C++11_ the answers for this question would greatly differ – K-ballo Jan 08 '13 at 20:07
  • 2
    @K-ballo I added C++11 examples as well since a recent question asked this same thing recently and I felt this needed one in order to justify closing it http://stackoverflow.com/questions/16337459/undefined-number-of-arguments-in-c#16337614 – Shafik Yaghmour May 03 '13 at 13:46
  • 1
    Added pre *C++11* options to my answer as well, so it now should cover most of the choices available. – Shafik Yaghmour Dec 02 '13 at 18:32
  • @K-ballo there is afaik no way to do it in C++ in case you need forced argument type.. no construction like foo(int ... values) :/ If you don't care about types, then yes, variadic templates in C++11 works great – graywolf Aug 07 '16 at 21:03
  • https://stackoverflow.com/a/29326784/4361073 – parasrish Nov 24 '17 at 13:08
  • @nunos: I've just now seen [another question](https://stackoverflow.com/questions/58986072) closed as duplicate of this one... please consider shifting the checkmark from wilhelmtell's "this is how it's done in C but it is not good" answer to Shafik's actually-C++ answer. – DevSolar Nov 22 '19 at 01:30
  • @nunos: Is there any particular *reason* why you don't want to change the accepted answer? – DevSolar Nov 22 '19 at 20:48
  • @nunos Can you please change the accepted answer to [the one that covers both the antique solution and the newer C++11 version](https://stackoverflow.com/a/16338804)? – S.S. Anne Jan 09 '20 at 20:52
  • In my case, these five lines were enough: https://stackoverflow.com/a/1056423/1396334 (being this one the most important: `vsnprintf (buffer, 255, format, args);`) – Avio Jan 20 '22 at 15:29

17 Answers17

481

In C++11 you have two new options, as the Variadic arguments reference page in the Alternatives section states:

  • Variadic templates can also be used to create functions that take variable number of arguments. They are often the better choice because they do not impose restrictions on the types of the arguments, do not perform integral and floating-point promotions, and are type safe. (since C++11)
  • If all variable arguments share a common type, a std::initializer_list provides a convenient mechanism (albeit with a different syntax) for accessing variable arguments.

Below is an example showing both alternatives (see it live):

#include <iostream>
#include <string>
#include <initializer_list>

template <typename T>
void func(T t) 
{
    std::cout << t << std::endl ;
}

template<typename T, typename... Args>
void func(T t, Args... args) // recursive variadic function
{
    std::cout << t <<std::endl ;

    func(args...) ;
}

template <class T>
void func2( std::initializer_list<T> list )
{
    for( auto elem : list )
    {
        std::cout << elem << std::endl ;
    }
}

int main()
{
    std::string
        str1( "Hello" ),
        str2( "world" );

    func(1,2.5,'a',str1);

    func2( {10, 20, 30, 40 }) ;
    func2( {str1, str2 } ) ;
} 

If you are using gcc or clang we can use the PRETTY_FUNCTION magic variable to display the type signature of the function which can be helpful in understanding what is going on. For example using:

std::cout << __PRETTY_FUNCTION__ << ": " << t <<std::endl ;

would results int following for variadic functions in the example (see it live):

void func(T, Args...) [T = int, Args = <double, char, std::basic_string<char>>]: 1
void func(T, Args...) [T = double, Args = <char, std::basic_string<char>>]: 2.5
void func(T, Args...) [T = char, Args = <std::basic_string<char>>]: a
void func(T) [T = std::basic_string<char>]: Hello

In Visual Studio you can use FUNCSIG.

Update Pre C++11

Pre C++11 the alternative for std::initializer_list would be std::vector or one of the other standard containers:

#include <iostream>
#include <string>
#include <vector>

template <class T>
void func1( std::vector<T> vec )
{
    for( typename std::vector<T>::iterator iter = vec.begin();  iter != vec.end(); ++iter )
    {
        std::cout << *iter << std::endl ;
    }
}

int main()
{
    int arr1[] = {10, 20, 30, 40} ;
    std::string arr2[] = { "hello", "world" } ; 
    std::vector<int> v1( arr1, arr1+4 ) ;
    std::vector<std::string> v2( arr2, arr2+2 ) ;

    func1( v1 ) ;
    func1( v2 ) ;
}

and the alternative for variadic templates would be variadic functions although they are not type-safe and in general error prone and can be unsafe to use but the only other potential alternative would be to use default arguments, although that has limited use. The example below is a modified version of the sample code in the linked reference:

#include <iostream>
#include <string>
#include <cstdarg>
 
void simple_printf(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
 
    while (*fmt != '\0') {
        if (*fmt == 'd') {
            int i = va_arg(args, int);
            std::cout << i << '\n';
        } else if (*fmt == 's') {
            char * s = va_arg(args, char*);
            std::cout << s << '\n';
        }
        ++fmt;
    }
 
    va_end(args);
}
 

int main()
{
    std::string
        str1( "Hello" ),
        str2( "world" );

    simple_printf("dddd", 10, 20, 30, 40 );
    simple_printf("ss", str1.c_str(), str2.c_str() ); 

    return 0 ;
} 

Using variadic functions also comes with restrictions in the arguments you can pass which is detailed in the draft C++ standard in section 5.2.2 Function call paragraph 7:

When there is no parameter for a given argument, the argument is passed in such a way that the receiving function can obtain the value of the argument by invoking va_arg (18.7). The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the argument expression. After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed. If the argument has a non-POD class type (clause 9), the behavior is undefined. [...]

Thomas Weller
  • 55,411
  • 20
  • 125
  • 222
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
191

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
  • 57,473
  • 20
  • 96
  • 131
  • 10
    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 Nov 01 '09 at 19:24
  • 1
    @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 Nov 02 '09 at 10:28
  • 3
    is it necessary to provide atleast one argument before the `...` syntax? – Lazer Jun 23 '10 at 09:54
  • 4
    @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 Jun 23 '10 at 21:33
  • 12
    you should probably use `` in C++ instead of `` – newacct Jan 08 '13 at 21:05
  • 2
    There does need to be at least one normal parameter before `...`, whose name you pass to `va_start`. This is to allow for implementations where the macro uses the address of that parameter to figure out the addresses of the rest of them (other implementations might for example use some compiler builtin to access the stack or frame pointer). – Steve Jessop Nov 19 '13 at 15:03
  • I was thinking of using varargs to implement a max(int,int,int..) function. What would be an alternate (and neat) way to do it? – Rushil Paul May 26 '14 at 06:35
  • 16
    Variable number of arguments is great for debug or for functions/methods that fill some array. Also it's great for many mathematical operations, such as max, min, sum, average... It's not mess when you don't mess with it. – Tomáš Zato Dec 05 '14 at 20:49
  • @Rushil: Why not implement a max that takes an array of int? – MyUsername112358 Feb 04 '16 at 00:57
  • @MyUsername112358 array would not be a good choice for me as I won't be able to create and pass the array dynamically. – Rushil Paul Feb 15 '16 at 14:38
  • @Rushil For a max function you should definitely use a vararg template. – Omnifarious Jul 20 '18 at 01:23
  • Lack of verbosity is more important than unsafety, imo, so why couch the correct answer in the opinion? just bounced off the initializer list implementation because it introduced too much semantic complexity and verbosity in what should be the same convenient, albeit unsafe, interface that the entry point to main is anyway! – Chris Jan 12 '20 at 17:06
  • Anyways, technical details are why we get paid. There are plenty of technical details to computing an effective numerical derivative, integration, or setting up the PDEs for a nuclear fusion simulation in a tokamak. The noise at the function interface is the bottleneck for advanced users, not the noise inside the function capsule. This answer is far and away superior to the ones below. – Chris Jan 12 '20 at 17:17
78

A C++17 solution: full type safety + nice calling syntax

Since the introduction of variadic templates in C++11 and fold expressions in C++17, it is possible to define a template-function which, at the caller site, is callable as if it was a varidic function but with the advantages to:

  • be strongly type safe;
  • work without the run-time information of the number of arguments, or without the usage of a "stop" argument.

Here is an example for mixed argument types

template<class... Args>
void print(Args... args)
{
    (std::cout << ... << args) << "\n";
}
print(1, ':', " Hello", ',', " ", "World!");

And another with enforced type match for all arguments:

#include <type_traits> // enable_if, conjuction

template<class Head, class... Tail>
using are_same = std::conjunction<std::is_same<Head, Tail>...>;

template<class Head, class... Tail, class = std::enable_if_t<are_same<Head, Tail...>::value, void>>
void print_same_type(Head head, Tail... tail)
{
    std::cout << head;
    (std::cout << ... << tail) << "\n";
}
print_same_type("2: ", "Hello, ", "World!");   // OK
print_same_type(3, ": ", "Hello, ", "World!"); // no matching function for call to 'print_same_type(int, const char [3], const char [8], const char [7])'
                                               // print_same_type(3, ": ", "Hello, ", "World!");
                                                                                              ^

More information:

  1. Variadic templates, also known as parameter pack Parameter pack(since C++11) - cppreference.com.
  2. Fold expressions fold expression(since C++17) - cppreference.com.
  3. See a full program demonstration on coliru.
YSC
  • 38,212
  • 9
  • 96
  • 149
27

in c++11 you can do:

void foo(const std::list<std::string> & myArguments) {
   //do whatever you want, with all the convenience of lists
}

foo({"arg1","arg2"});

list initializer FTW!

Markus Zancolò
  • 342
  • 3
  • 5
23

In C++11 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++11FAQ.

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++11 variable argument templates.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • @donlan - If you're using C++17, you can use fold expressions to make things a lot simpler in some cases (think creatively here, you can use the `,` operator with fold expressions). Otherwise, I don't think so. – Omnifarious Jan 12 '20 at 17:30
  • 1
    Nice article referenced.. When I started reading the class template tuple in the section "We can build a variadic type" I suddenly thought: "OMG! This is Lisp programming, at compile time, in C++.." ..LOL.. – Razzle Jan 18 '21 at 20:30
15

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
  • 73,905
  • 40
  • 169
  • 246
  • Also, this only seems to work in the case of a function like printing, where you actually have an iteration of a single argument function across each arg. Otherwise, you are just initializing a list and passing the list for the end around on `std::initializer_lists`... And this is already introducing massive complexity on a simple task. – Chris Jan 12 '20 at 17:11
14

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
  • 3,200
  • 1
  • 34
  • 46
  • 6
    Cleaner would be to not enforce vectors. Instead use a template argument specifying the STL-styled collection then iterate through it using the argument's begin and end methods. This way you can use std::vector, c++11's std::array, std::initializer_list or even make your own collection. – Jens Åkerblom Mar 29 '13 at 11:26
  • 4
    @JensÅkerblom I agree but this is the kind of choice that should be analyzed for the issue at hand, to avoid over engineering. Since this is a matter of API signature, it is important to understand the tradeoff between maximum flexibility and clarity of intent/usability/maintanability etc. – Francesco Mar 30 '13 at 14:18
8

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
  • 17,020
  • 7
  • 59
  • 90
  • By error prone I assume you mean potentially very very dangerous? Especially when working with untrusted input. – Kevin Loney Nov 01 '09 at 18:32
  • 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 Nov 01 '09 at 18:34
7

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.

Zoli
  • 1,137
  • 7
  • 12
7

Using variadic templates, example to reproduce console.log as seen in JavaScript:

Console console;
console.log("bunch", "of", "arguments");
console.warn("or some numbers:", 1, 2, 3);
console.error("just a prank", "bro");

Filename e.g. js_console.h:

#include <iostream>
#include <utility>

class Console {
protected:
    template <typename T>
    void log_argument(T t) {
        std::cout << t << " ";
    }
public:
    template <typename... Args>
    void log(Args&&... args) {
        int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
        cout << endl;
    }

    template <typename... Args>
    void warn(Args&&... args) {
        cout << "WARNING: ";
        int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
        cout << endl;
    }

    template <typename... Args>
    void error(Args&&... args) {
        cout << "ERROR: ";
        int dummy[] = { 0, ((void) log_argument(std::forward<Args>(args)),0)... };
        cout << endl;
    }
};
kungfooman
  • 4,473
  • 1
  • 44
  • 33
4

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
  • 10,944
  • 6
  • 56
  • 81
2

C++ 11 with color code support

  • Is generic and works for all dataypes
  • Works like JavaScript console.log(1,"23")
  • Supports color codes for info, warning, error.
  • Example:
    enter image description here
#pragma once
#include <iostream>
#include <string>

const std::string RED = "\e[0;91m";
const std::string BLUE = "\e[0;96m";
const std::string YELLOW = "\e[0;93m";

class Logger {
private:
  enum class Severity { INFO, WARN, ERROR };

  static void print_colored(const char *log, Severity severity) {
    const char *color_code = nullptr;

    switch (severity) {
    case Severity::INFO:
      color_code = BLUE.c_str();
      break;
    case Severity::WARN:
      color_code = YELLOW.c_str();
      break;
    case Severity::ERROR:
      color_code = RED.c_str();
      break;
    }

    std::cout << "\033" << color_code << log << "\033[0m -- ";
  }

  template <class Args> static void print_args(Args args) {
    std::cout << args << " ";
  }

public:
  template <class... Args> static void info(Args &&...args) {
    print_colored("[INFO] ", Severity::INFO);
    int dummy[] = {0, ((void)print_args(std::forward<Args>(args)), 0)...};
    std::cout << std::endl;
  }

  template <class... Args> static void warn(Args &&...args) {
    print_colored("[WARN] ", Severity::WARN);
    int dummy[] = {0, ((void)print_args(std::forward<Args>(args)), 0)...};
    std::cout << std::endl;
  }

  template <class... Args> static void error(Args &&...args) {
    print_colored("[ERROR]", Severity::ERROR);
    int dummy[] = {0, ((void)print_args(std::forward<Args>(args)), 0)...};
    std::cout << std::endl;
  }
};

Orif Milod
  • 465
  • 1
  • 7
  • 17
1

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

Thomas Padron-McCarthy
  • 27,232
  • 8
  • 51
  • 75
1
// spawn: allocate and initialize (a simple function)
template<typename T>
T * spawn(size_t n, ...){
  T * arr = new T[n];
  va_list ap;
  va_start(ap, n);
  for (size_t i = 0; i < n; i++)
    T[i] = va_arg(ap,T);
  return arr;
}

User writes:

auto arr = spawn<float> (3, 0.1,0.2,0.3);

Semantically, this looks and feels exactly like an n-argument function. Under the hood, you might unpack it one way or the other.

Chris
  • 28,822
  • 27
  • 83
  • 158
0

It is possible now...using boost any and templates In this case, arguments type can be mixed

#include <boost/any.hpp>
#include <iostream>

#include <vector>
using boost::any_cast;

template <typename T, typename... Types> 
void Alert(T var1,Types... var2) 
{ 

    std::vector<boost::any> a(  {var1,var2...});

    for (int i = 0; i < a.size();i++)
    {

    if (a[i].type() == typeid(int))
    {
        std::cout << "int "  << boost::any_cast<int> (a[i]) << std::endl;
    }
    if (a[i].type() == typeid(double))
    {
        std::cout << "double "  << boost::any_cast<double> (a[i]) << std::endl;
    }
    if (a[i].type() == typeid(const char*))
    {
        std::cout << "char* " << boost::any_cast<const char*> (a[i]) <<std::endl;
    }
    // etc
    }

} 


void main()
{
    Alert("something",0,0,0.3);
}
Aftershock
  • 5,205
  • 4
  • 51
  • 64
-1

We could also use an initializer_list if all arguments are const and of the same type

pkumar0
  • 2,069
  • 3
  • 14
  • 21
-2
int fun(int n_args, ...) {
   int *p = &n_args; 
   int s = sizeof(int);
   p += s + s - 1;
   for(int i = 0; i < n_args; i++) {
     printf("A1 %d!\n", *p);
     p += 2;
   }
}

Plain version