Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

read arguments from variadic template

Tags:

c++

c++11

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.

like image 223
sami Avatar asked Sep 06 '10 12:09

sami


2 Answers

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.

like image 152
Motti Avatar answered Nov 08 '22 13:11

Motti


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;
    }
} 
like image 23
UncleBens Avatar answered Nov 08 '22 12:11

UncleBens