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.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
James
  • 2,742
  • 1
  • 20
  • 43
  • 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](http://en.cppreference.com/w/cpp/language/parameter_pack). – Some programmer dude Dec 20 '16 at 07: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... – StoryTeller - Unslander Monica Dec 20 '16 at 07:36
  • You should use http://en.cppreference.com/w/cpp/language/parameter_pack – Stargateur Dec 20 '16 at 07:37

3 Answers3

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!
stas.yaranov
  • 1,797
  • 10
  • 17
  • 1
    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. – James Dec 20 '16 at 07:46
  • 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 (http://www.boost.org/doc/libs/1_60_0/doc/html/boost/algorithm/join.html). – stas.yaranov Dec 20 '16 at 07:50
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;
}
Fake Code Monkey Rashid
  • 13,731
  • 6
  • 37
  • 41
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;
}
llf
  • 610
  • 10
  • 11