5
void foo(std::string arg, ...) {

   // do something with every argument

}

Lets say I want to be able to take every string argument and append an exclamation mark before printing it out on a new line.

3
  • 1
    The simple answer is that you can't. There is no standard functionality that allows you to enumerate or iterate over the arguments. That's why vararg functions need special arguments that contain information about the rest of the arguments, like the printf format string. You can solve it by passing an extra first integer argument whose value is the number of string arguments. Or since you program in C++ you can use templates and parameter packs. Commented Dec 20, 2016 at 7:35
  • 2
    Then there's the small issue with it being undefined behavior to pass a variadic function anything that isn't a builtin type... Commented Dec 20, 2016 at 7:36
  • You should use en.cppreference.com/w/cpp/language/parameter_pack Commented Dec 20, 2016 at 7:37

3 Answers 3

10

The best way is to use parameters pack. For example:

#include <iostream>

// Modify single string.
void foo(std::string& arg)
{
    arg.append("!");
}

// Modify multiple strings. Here we use parameters pack by `...T`
template<typename ...T>
void foo(std::string& arg, T&... args)
{
    foo(arg);
    foo(args...);
}

int main()
{
    // Lets make a test

    std::string s1 = "qwe";
    std::string s2 = "asd";

    foo(s1, s2);

    std::cout << s1 << std::endl << s2 << std::endl;

    return 0;
}

This will print out:

qwe!
asd!
Sign up to request clarification or add additional context in comments.

2 Comments

thanks yaranov, is there an iterative way to do this? I'm asking because I want to define a function/api that converts a list of strings into a single comma separated string. and it seems like a stringstream with a loop is the most efficient way.
If you have a list of strings then first if you need to modify strings then you can use foo accepting single argument for each string in a list (each item is going to be modified since argument to foo is passed by reference) and second is to create a new string by joining strings in the list by using something like join from boost (boost.org/doc/libs/1_60_0/doc/html/boost/algorithm/join.html).
3

C++17

Use a parameter pack with a fold expression:

#include <iostream>
#include <string>

// Modify multiple strings. Here we use parameters pack by `...T`
template<typename ...T>
void foo(T&... args)
{
    (args.append("!"),...);
}

int main()
{
    // Lets make a test

    std::string s1 = "qwe";
    std::string s2 = "asd";

    foo(s1, s2);

    std::cout << s1 << std::endl << s2 << std::endl;

    return 0;
}

Comments

2

Here is an iterative solution. There is a bit of noise in the function call, but no computation of the number of varargs is needed.

#include <iostream>
#include <string>
#include <initializer_list>
#include <functional> // reference_wrapper

void foo(std::initializer_list<std::reference_wrapper<std::string>> args) {
    for (auto arg : args) {
        arg.get().append("!");
    }
}

int main() {
    // Lets make a test

    std::string s1 = "qwe";
    std::string s2 = "asd";

    foo({s1, s2});

    std::cout << s1 << std::endl << s2 << std::endl;

    return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.