4

I come from a Swift background and, though I know some C as well, this is my first time writing C++ code.

In Swift it is possible to write a function that takes any number of arguments:

func foo(bar: String...) {
    // ...
}

and bar can be of any type (String, Bool, Struct, Enum, etc).

I was wondering if the same can be done in C++. So, ideally I would write:

struct X {
    string s;
    X(int);
    // ...
}

void foo(string s, ...) {
    // ...
}

foo("mystr", X(1), X(2), X(3));

and inside foo I would somehow be able to access the list of arguments, somewhat akin to a printf function.

Right now I'm using a vector<X> as argument, since all the arguments have type X. However, that makes calling foo somewhat ugly, in my opinion:

foo("mystr", { X(1), X(2), X(3) });

Any solution I'm not seeing due to my strong lack of knowledge towards C++?


Edit: This is what I want done specifically inside foo:

string ssub(string s, vector<X> v) {
    int index, i = 0;

    while (1) {
        index = (int)s.find(SUB);
        if (index == string::npos) { break; }
        s.erase(index, string(SUB).size());
        s.insert(index, v[i].tostr());
        i++;
    }

    return s;
}

Basically, as long as I'm given a way to sequentially access the arguments, all is good.

Alex
  • 5,009
  • 3
  • 39
  • 73
  • 1
    Possible duplicate of [Variable number of arguments in C++?](http://stackoverflow.com/questions/1657883/variable-number-of-arguments-in-c) – jotik Apr 17 '16 at 16:50
  • @jotik That question does not specifically address having non POD types as arguments, and if any of the answers actually answer this question, it's all the same to me, because I can't understand them. – Alex Apr 17 '16 at 16:55

3 Answers3

1

Here's one of many ways.

You can copy/paste this entire program into your IDE/editor.

#include <utility>
#include <iostream>
#include <typeinfo>
#include <string>

//
// define a template function which applies the unary function object func
// to each element in the parameter pack elems.
// @pre func(std::forward<Elements>(elems)) must be well formed for each elems
// @returns void
//
template<class Function, class...Elements>
auto do_for_all(Function&& func, Elements&&...elems)
{
    using expand = int[];
    void(expand { 0, (func(elems), 0)... });
}

// a test structure which auto-initialises all members
struct X
{
    int i = 0;
    std::string s = "hello";
    double d = 4.4;
};

//
// The function foo
// introduces itself by writing intro to the console
// then performs the function object action on each of args
// @note all arguments are perfectly forwarded - no arguments are copied
//
template<class...Args>
auto foo(const std::string& intro, Args&&...args)
{
    std::cout << "introducing : " << intro << std::endl;
    auto action = [](auto&& arg)
    {
        std::cout << "performing action on: " << arg
        << " which is of type " << typeid(arg).name() << std::endl;
    };

    do_for_all(action, std::forward<Args>(args)...);
}

int main()
{
    // make an X
    auto x = X(); // make an X

    // foo it with the intro "my X"
    foo("my X", x.i, x.s, x.d);
}

example output:

introducing : my X
performing action on: 0 which is of type i
performing action on: hello which is of type NSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE
performing action on: 4.4 which is of type d
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
0

You can use variadic templates (since C++11):

template <typename ... Type>
void foo(Type& ... args) {
    // do whatever you want, but this may be tricky
}

foo(X(1), X(2), X(3));

Example of variadic templates: min function

This is the code I wrote to get rid of ugly calls to std::min when calculating minimum of many values.

#include <type_traits>

namespace my {
    template <typename A, typename B>
    auto min(const A& a, const B& b) -> typename std::common_type<A, B>::type {
        return (a<b)?a:b;
    }

    template <typename A, typename B, typename ... T >
    auto min(const A& a, const B& b, const T& ... c) -> typename std::common_type<A, B, T ...>::type {
        const typename std::common_type<A, B, T ...>::type tmp = my::min(b, c ...);
        return (a<tmp)?a:tmp;
    }
}


// calculating minimum with my::min
my::min(3, 2, 3, 5, 23, 98); 

// doing the same with std::min
std::min(3, std::min(2, std::min(3, std::min(5, std::min(23, 98))))); // ugh, this is ugly!

Here's the tricky part: you can't cycle through the parameter pack like you do with vectors. You'll have to do some recursion as shown in the example.

Community
  • 1
  • 1
ForceBru
  • 43,482
  • 10
  • 63
  • 98
  • Alright, and in that case how would I cycle through the given argument list (like with a `for` loop)? – Alex Apr 17 '16 at 16:46
  • @Alex, that's why I said 'this may be tricky'. It can be relatively difficult to do depending on your task. Please show the code of `foo` – ForceBru Apr 17 '16 at 16:47
  • Oh, I see, though I just wrote on my question all I'd need to do was to cycle through the arguments – Alex Apr 17 '16 at 16:52
0

You could write a variadic template function, pass the arguments into some std::initializer_list and iterate over the list, for example:

#include <initializer_list>

template <typename ... Args>
void foo(Args && ... args) {
    std::initializer_list<X> as{std::forward<Args>(args)...};
    for (auto const & x : as)
        // Use x here
}

int main() {
    foo(1, 2, 3, 4, 5);
}

Note also, that you might want to change the argument list and type of the initializer list to meet your exact use-case. E.g. use Args * ... args and std::initializer_list<X *> or similar.

jotik
  • 17,044
  • 13
  • 58
  • 123