Is it possible to handle non-primitive types in a variadic function?

non variadic
gcc variadic function
variadic function call
c variadic function
haskell variadic function
variadic function in python
passing va_list to another function
c passing variable arguments to another function

I have 2 versions of the same variadic function, however one works the other doesn't. I suspect the reason is because one is using a primitive type where the other uses std::string.

void test1(int f_num, ...)
{
    va_list file_names;
    va_start(file_names, f_num);

    for(int i=0; i<f_num; i++)
    {
        string name = string(va_arg(file_names, char*));
        cout << name << endl;
    }
    va_end(file_names);
}

void test2(int f_num, ...)
{
    va_list file_names;
    va_start(file_names, f_num);

    for(int i=0; i<f_num; i++)
    {
        string name = va_arg(file_names, string);
        cout << name << endl;
    }
    va_end(file_names);
}

int main()
{
    test1(3, "Hallo", "you", "people");
    test2(3, "Hallo", "you", "people");
}

The above results in the following output:

Hallo
you
people
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted (core dumped)

The first function thus works but the second doesn't. Am I correct to assume that it's because the variadic macro doesn't handle non-primitive types? Can you make it handle non-porimitive types?


va_arg decodes the va_list

You cannot use va_arg to convert the passed in variable argument parameter in a single go. The syntax of va_arg would be to decode the variable argument to match the type that was passed in. For example, if you pass in an int value, you must decode it as an int with va_arg. You cause undefined behavior if you try to decode it as anything else (like, as a double).

Since you passed in a string literal, the type should be const char *.

const char *arg = va_arg(file_names, const char *);
va_arg on non-trivial classes is implementation-defined

As MSalter's answer clearly explains, even if you really passed in a std::string to a function expecting variable arguments, it is not necessarily going to work because the semantics are implementation-defined.

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.10). … Passing a potentially-evaluated argument of class type (Clause 9) having a non- trivial copy constructor, a non-trivial move contructor, or a non-trivial destructor, with no corresponding parameter, is conditionally-supported with implementation-defined semantics. … C++.11 §[expr.call] ¶7

Note: (18.10) defines va_arg, and (Clause 9) is §[class].

Just use a variadic template

You can use C++11's variadic template argument feature to achieve the effect you want in a type safe way. Assume you actually want to do a little more than print each argument, the usual pattern is to recursively traverse the parameter pack, unpacking one parameter at a time.

void test3 (int f_num, std::string file_name) {
    std::cout << f_num << ':' << file_name << std::endl;
}

template <typename... T>
void test3 (int f_num, std::string file_name, T...rest) {
    test3(f_num, file_name);
    test3(f_num, rest...);
}

Thus, iteration is achieved by recursively calling the function, reducing the parameter pack by one parameter on the next recursive call.

Is it possible to handle non-primitive types in a variadic function?, first function thus works but the second doesn't. Am I correct to assume that it's because the variadic macro doesn't handle non-primitive types? Can you make  I like your idea of a variadic print function instead of it, which would be able to handle non-primitive types, which is another good point. And no, if you didn't have access to the header, you couldn't add additional log levels as it is now.


The answer to the first part is that you're mostly correct. There are restrictions on the use of class types, in particular if they have a non-trivial copy constructor (which std::string has). It might work on some implementations and not on others. Formally, that's conditionally-supported with implementation-defined semantics.

The second part is a bit harder, but also a bit simpler. These varargs are old C, and not typesafe. C++ does have typesafe variadics, via templates:

template<typename... T>
void test1(T... args)
{
    std::cout << ... << args << std::endl;
}

The problem is that you need to know how to unpack those args... - they're called parameter packs. There are a few different ways to unpack them; this particular form is called a fold expression.

Mastering Rust: Learn about memory safety, type system, , Learn about memory safety, type system, concurrency, and the new features of Rust which would not have been possible if it were implemented as a regular function. This is because Rust does not support variadic arguments for functions. We can also print non-primitive types in more of a JSON-like format with the "{:#​?}  Kirk McDonald (44/64) Nov 02 2006 In trying to work with the variadic templates, I've discovered a few ; Mike Parker (4/13) Nov 02 2006 Is it possible to restrict use type specialization to restrict a tuple ; Walter Bright (3/5) Nov 02 2006 Currently, no. I was reluctant to go down that path until I know where


If you call test2 as such, it should work:

test2(3, std::string("Hallo"), std::string("you"), std::string("people"));

What's happening here is that when you pass "Hallo" you're passing a char*, not an std::string, so when you try to retrieve the char* by giving it a type of std::string with va_arg, it fails since the object isn't an std::string.

Also, in some compilers, it seems that you can't pass types that aren't trivially destructable, when I tried this in cpp.sh it gave the error

In function 'void test2(int, ...)':
27:23: error: cannot receive objects of non-trivially-copyable type 'std::string {aka class std::basic_string<char>}' through '...'; 
 In function 'int main()':
36:51: error: cannot pass objects of non-trivially-copyable type 'std::string {aka class std::basic_string<char>}' through '...'

Although it seems this isn't part of the standard as I couldn't find anywhere that required this, so I assume it's just a limitation of the compiler they're using. If anyone knows the details about this, I'll edit the answer with the correct information.

Like Henri Menke said, it seems the problem is that cpp.sh is using an old version that isn't c++11 compliant, so if you use a compiler that is at least c++11 compliant, you can use std::string within variadic arguments.

It appears this is an implementation detail, as miradulo said, so it will depend on your compiler, and not the standard version you're using.

And also, like Henri Menke mentioned, if you use

using std::string_literals;

And then put an 's' at the end of the normal string as such:

test2(3, "Hallo"s, "you"s, "people"s);

It transforms the char* to a std::string so you can use them like this to call test2, this is due to string literals.

The Complete Rust Programming Reference Guide: Design, develop, , This is because Rust does not support variadic arguments for functions. We can also print non-primitive types in more of a JSON-like format with the "{:#?}  Just use a variadic template. You can use C++11's variadic template argument feature to achieve the effect you want in a type safe way. Assume you actually want to do a little more than print each argument, the usual pattern is to recursively traverse the parameter pack, unpacking one parameter at a time.


PostgreSQL 9.0 Official Documentation, A function that takes a single argument of a composite type should generally not have Another possible conflict is between variadic and non-variadic functions. There are a lot of workarounds to this. Instead of having GDscript handle the varying arguments, do so yourself within the function.


DCL11-C. Understand the type issues associated with variadic , The variable arguments to a variadic function are not checked for type by the compiler. string and dereferenced, which will likely cause the program to abnormally terminate. causing unknown data to be used instead of the pointer to the message. On a side note, most modern implementations handle this gracefully by  In computer science, an operator or function is variadic if it can take a varying number of arguments; that is, if its arity is not fixed.


AUUGN, It would be possible to wrap these primitive types up in classes such as the primitive types even to bit lengths (ints are 32-bit unsigned), and does not Varargs. C allows functions to have a variable number of arguments, such as print f ( ) . To your original question: Is it possible to 1) count 2) extract types in/from a typelist using variadic templates. The easiest thing is to put the list of template arguments into a tuple type and then extract the count and elements.