Joe Blake
Joe Blake

Reputation: 27

Trouble with va_list c++

I'm just trying to write a very simple function with a variable number of arguments so I can write a function similar to printf for an assignment. After looking at the documentation for va_list I'm not sure why this code keeps giving me run-time errors:

enter image description here

Here is my code:

void print(string sOne , ...);
void main()
{
    print("first string", "second string", "third String");
    system("pause");
}

void print(string sOne , ...)
{
    va_list arguments;
    va_start(arguments, sOne);
     while ((va_arg(arguments, int)) != 0)
    {
        string printString = va_arg(arguments, string);
        cout << printString;
    }
    va_end(arguments);
}

Upvotes: 0

Views: 6034

Answers (3)

Slava
Slava

Reputation: 44268

SergeyA explained why your code does not work, here is one of the possible solutions:

void print(const char *sOne , ...);
int main()
{
    print("first string", "second string", "third String", nullptr);
    system("pause");
}

void print(const char *sOne , ...)
{
    va_list arguments;
    va_start(arguments, sOne);
    while (sOne)
    {
        cout << sOne;
        sOne = va_arg(arguments, const char *);
    }
    va_end(arguments);
}

Again this example is in case you have to use C-style variadic function, you should consider using C++ variadic template instead.

Upvotes: 2

SergeyA
SergeyA

Reputation: 62613

Your implementation of variadic function is very incorrect.

First, you need a some way to tell the function how many arguments there are or when they end. Standard printf does this by using format specifiers (their number represents the number of args), another option is to provide the number explicitly. You seem to expect the last argument to be integer 0 (strange choice btw.), but you never pass 0 as a last argument to your variadic function.

Second, you can't portably extract std::string from variadic function arguments. Only trivial types are fully supported, and for strings you have to use char*. std::string is not trivial, because it has non-trivial constructor and destructor. Some compilers do support non-trivial types as arguments for such functions, but others do not, so you shouldn't try this.

The last, but not the least: variadic functions have no place in C++ world, even for assignments.

Upvotes: 7

Jonathan Mee
Jonathan Mee

Reputation: 38959

So you have several problems with your implementation.

  1. You don't pass in a limit to the number of calls to va_arg and:

If va_arg is called when there are no more arguments in ap, or if the type of the next argument in ap (after promotions) is not compatible with T, the behavior is undefined

  1. You appear to want to print "first string" so you need to pass that as part of the va_list, you can clean up both 1 and 2 by using a count as your first argument: void print(int sOne, ...)
  2. You are passing in const char*s as arguments to your va_list and expecting it to promote those to strings. va_list will not promote for you. It casts whatever you passed in to the type specified in va_arg And if you try to treat a const char* as a string you will end up with a run-time error, as you have seen. This can be corrected by using strings as your va_list arguments: "first string"s "second string"s, "third String"s this is only conditionally supported in C++ You'll need to use va_arg(arguments, const char*)
  3. You are calling va_arg to advance your for loop. You'll need to use a counter for that. Calls to va_arg extract the next argument, they do not test if an argument is available. Instead use your count that you passed in as part of 2 for (auto i = 0; i < sOne; ++i)

Making those changes should get your code looking something like this:

void print(int sOne, ...) {
    va_list arguments;
    va_start(arguments, sOne);

    for (auto i = 0; i < sOne; ++i) {
        cout << va_arg(arguments, const char*) << endl;
    }
    va_end(arguments);
}

Live Example

Upvotes: 0

Related Questions