TosinAl
TosinAl

Reputation: 166

Convert tuple to variadic arguments

I'm not sure what I want to achieve is possible, but I need my templated function (my_func) to be able to take both variable arguments as well as a tuple argument. Clearer description is in the code comments.

struct A{
    static void call_func(int a, std::string b) {
        std::cout<<"int is: "<<a<<", string is: "<<b<<"\n";
    }
};

struct B{
    static void call_func(std::string a, std::string b) {
        std::cout<<"string is: "<<a<<", string is: "<<b<<"\n";
    }
};

struct C{
    static void call_func(int a) {
        std::cout<<"int is: "<<a<<"\n";
    }
};

template<typename T, typename... Args> 
void my_func(Args... args) {
    T::call_func(args...);
}


int main() {

    my_func<A>(3, "ant");    //int is: 3, string is: ant
    my_func<B>("bat", "bee");  //string is: bat, string is: bee
    my_func<C>(5);  //int is: 5


    //Is there a way to call(or modify) my_func such that it also accepts a tuple
    std::tuple<int, std::string> a_params{3,"tuple-ant"};
    std::tuple<std::string, std::string> b_params{"tuple-bat","tuple-bee"};
    std::tuple<int> c_params{7};
    // my_func<A>(a_params); 
    // my_func<B>(b_params);
    // my_func<C>(c_params);   
 
}

Upvotes: 1

Views: 606

Answers (3)

Eightfold
Eightfold

Reputation: 71

If you don't want to use std::apply and are using c++20, you can just do this:

template <typename T, typename...Args>
void my_func(std::tuple<Args...>& args) {
    [&] <std::size_t...Is> (std::index_sequence<Is...>) {
        T::call_func(std::get<Is>(args)...);
    } (std::make_index_sequence<sizeof...(Args)>{});
}

It uses the lambda to expand the index sequence, allowing you to access each member, and call call_func with them.

Still, std::apply is probably the best choice for this.

Upvotes: 1

max66
max66

Reputation: 66200

Are you sure it's a good idea that both functions are called my_func()? To avoid confusion, what about my_func_tuple(), or something similar, for the second one?

Anyway... it seems to me that you're looking for std::apply(); with it the tuple version can be written as

template <typename T, typename ... Args>
void my_func(std::tuple<Args...> tpl) {
  std::apply(T::call_func, tpl);
}

Take in count that std::apply() is available starting from C++17; before C++17 (C++11 and C++14) you have to simulate it using a integer sequence; see the Mooing Duck answer for a good example.

Upvotes: 4

Mooing Duck
Mooing Duck

Reputation: 66922

This is almost exactly what integer_sequence was made for:

template<typename T, typename... Args, std::size_t... ints> 
void my_func(std::tuple<Args...> args, std::integer_sequence<std::size_t,ints...> sequence) {
    T::call_func(std::get<ints>(args)...);
}

std::get<ints>(args)... says for each item in the size_t...ints template parameter, call get<ints>(args). This lets you unpack args as a list of parameters to the method. But then we have to figure out how to make the right integer_sequence. Luckily, that's also easy:

template<typename T, typename... Args> 
void my_func(std::tuple<Args...> args){
    my_func<T, Args...>(args, std::make_index_sequence<sizeof...(Args)>{});
}

http://coliru.stacked-crooked.com/a/1dfdb24d5d21c756

Upvotes: 2

Related Questions