tags:

views:

129

answers:

4

Hi all,

I am a little confused about how can I read each argument from the tuple by using variadic templates.

Consider this function:

template<class...A> int func(A...args){
int size = sizeof...(A);
.... }

I call it from the main file like:

func(1,10,100,1000);

Now, I don't know how I have to extend the body of func to be able to read each argument separately so that I can, for example, store the arguments in an array.

+3  A: 

You have to provide overrides for the functions for consuming the first N (usually one) arguments.

void foo() {
   // end condition argument pack is empty
}

template <class First, class... Rest> 
void foo(First first, Rest... rest) {
    // Do something with first
    cout << first << endl; 

    foo(rest...); // Unpack the arguments for further treatment
}

When you unpack the variadic parameter it finds the next overload.

Example:

foo(42, true, 'a', "hello");
// Calls foo with First = int, and Rest = { bool, char, char* }
// foo(42, Rest = {true, 'a', "hello"}); // not the real syntax

Then next level down we expand the previous Rest and get:

foo(true, Rest = { 'a', "hello"}); // First = bool

And so on until Rest contains no members in which case unpacking it calls foo() (the overload with no arguments).


Storing the pack if different types

If you want to store the entire argument pack you can use an std::tuple

template <class... Pack>
void store_pack(Pack... p) {
    std::tuple<Pack...> store( p... );
    // do something with store
}

However this seems less useful.

Storing the pack if it's homogeneous

If all the values in the pack are the same type you can store them all like this:

vector<int> reverse(int i) {
    vector<int> ret;
    ret.push_back(i);
    return ret;
}

template <class... R>
vector<int> reverse(int i, R... r) {
    vector<int> ret = reverse(r...);
    ret.push_back(i);
    return ret; 
}

int main() {
    auto v = reverse(1, 2, 3, 4);
    for_each(v.cbegin(), v.cend(), 
        [](int i ) { 
            std::cout << i << std::endl; 
        }
    );
}

However this seems even less useful.

Motti
@Motti: I understood what you mean. But I still have two questions:1- if I want to specify the data type, could I call the function foo in this way: foo<int,string...>(42, true, 'a', "hello") 2- where I could read the values to store it where I want to store after unpacking it and that was my main question!
sami
@sami, 1. I'm not sure what the standard says but g++ accepts `foo<char>(42)` in which case I get `*` rather than `42` which means it coerced the argument to a `char`.
Motti
@sami, 2. If these are of different types you cant store them in a regular array (unless you use `boost::any` or something similar as suggested by Kirill)
Motti
It seems the end condition should come first.
UncleBens
@Motti: Thnaks. I do not know why there are more than 100 examples describe how to unpack and output the arguments and at the same time there is no example which describes how the each parameter could be stored individually.
sami
@UncleBens, thanks, fixed.
Motti
@sami, I've updated my question see if it answers things for you.
Motti
@Motti: thanks, about the tuple, you have stopped where my question begins(where are the parameters stored and how can I get access to each one). the second suggestion seems pretty, but I think the body of the main function contains an error. may you clarify me how this line works vector<int> ret = reverse(r...);
sami
@sami, the second solution compiles and works as expected on g++ 4.5 what do you think is wrong with it? The way `vector int ret = reverse(r...);` works is it creates a pseudo recursive call that terminates when the argument pack contains exactly one element (the first function) this returns a `vector` with one element and each stack of the recursion adds another element until you have them all (in reverse order)
Motti
@Motti: when I compile your second example I get always an compile error. May you provide the example complete please!!
sami
@sami you have to include the following headers `tuple`, `vector`, `iostream` and `algorithm` and have `using namespace std`
Motti
@Motti: thanks. I have one question related to your example. What is the benefit of passing the first argument in this line vector<int> reverse(int i, R... r)? what is about if you only pass the variadic template?
sami
@Motti: I included the preprossers that you mentioned but I still get the this error: expected primary-expression before '[' token error: expected primary-expression before ']' token error: expected primary-expression before 'int'
sami
@sami, regarding the first argument, if you send the whole pack you're no closer to finishing, it's like recursion you want each step to reduce the problem (here the number of params). Regarding the error is this in the `for_each` (nowhere else has `[`) if so perhaps your compiler doesn't support lambda expressions, just use a regular `for` loop to print the content of the `vector`.
Motti
@Motti: thanks a lot. may you provide an example in more details for the first case in which the packs are from different types as you have done in the second case. I still missing the mechanism to access the pack parameters if they are form different types.
sami
@sami, please read up on `tuple`s the way to access them is `get<N>(tuple)` where `N` is the compile time constant of the index.
Motti
A: 

If you need to store arguments in the array you could use array of boost::any as follows:

template<typename... A> int func(const A&... args)
{
  boost::any arr[sizeof...(A)] = { args... };
  return 0;
}
Kirill V. Lyadvinsky
+2  A: 

If the arguments are all of the same type, you could store the arguments in an array like this (using the type of the first argument for the array):

template <class T, class ...Args>
void foo(const T& first, const Args&... args)
{
    T arr[sizeof...(args) + 1] = { first, args...};
}

int main()
{
    foo(1);
    foo(1, 10, 100, 1000);
}

If the types are different, I suppose you could use boost::any but then I don't see how you are going to find out outside of the given template, which item is of which type (how you are going to use the stored values).


Edit: If the arguments are all of the same type and you want to store them into a STL container, you could rather use the std::initializer_list<T>. For example, Motti's example of storing values in reverse:

#include <vector>
#include <iostream>
#include <iterator>

template <class Iter>
std::reverse_iterator<Iter> make_reverse_iterator(Iter it)
{
    return std::reverse_iterator<Iter>(it);
}

template <class T>
std::vector<T> reverse(std::initializer_list<T> const & init)
{

    return std::vector<T>(make_reverse_iterator(init.end()), make_reverse_iterator(init.begin()));
}

int main() {
    auto v = reverse({1, 2, 3, 4});
    for (auto it = v.begin(); it != v.end(); ++it) {
        std::cout << *it << std::endl;
    }
} 
UncleBens
@uncleBens: does the boost library support the STL containers for variadic templates too?
sami
@uncleBens: what is about if the arguments are not from the same data type?
sami
A standard "container" for values of different types is a `std::tuple`.
UncleBens
A: 

For sticking into an array if the arguments have different types, you can use also std::common_type<>

template<class ...A> void func(A ...args){
   typedef typename common_type<A...>::type common;
   std::array<common, sizeof...(A)> a = {{ args... }};
}

So for example, func(std::string("Hello"), "folks") creates an array of std::string.

Johannes Schaub - litb