Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass variadic amount of `std::pair` with different 2nd types to a function

Sorry for the inability to explain the primary Q in the title itself due to complexity of the problem.
Need to pass various types of std::pairs to a method like below:

foo({1, 1} , {2, 2.2}, {3, "3"});  // 'first' is always `int`

However, I couldn't figure out a syntax for How to define foo() using variadic templates?

This is more of a cosmetic change, where the intention is to avoid the boiler plate code. Hence below suggestion is ruled out:

template<typename... Args>
void foo (Args&&... args) { ... }

template<typename T> using pair = std::pair<int, T>;
foo(pair<int>{1, 1} , pair<double>{2, 2.2}, pair<std::string>{3, "3"});

For anyone who is interested, what I am going to do with various pairs. An overloaded function will be invoked on all the args... (using array trick) and all the second values will be converted to a std::string. For those who don't know the famous array trick:

const string array[] = { MyFunc(std::forward<Pairs>(pairs)) ... };

Similar (but not duplicate) question: Passing multiple std::pair to variadic template constructor using { }, { }

like image 759
iammilind Avatar asked Jul 14 '16 09:07

iammilind


3 Answers

You can simply use this signature:

template<typename... Args>
void foo (std::pair<int, Args> ...args) { /*...*/}

or

template <typename ...Args> using pair = std::pair<int, Args...>;

Edit: As mentioned the question is about constructing std::pair withot providing template arguments and to convert second part of pair to string. with overloading the () operator can we write:

#include <iostream>
#include <vector>
#include <sstream>

struct Function{
    template <typename T>
    Function& operator()(int a, T b){
        ss << b;
        list.push_back(ss.str());
        ss.str("");
        return *this;
    }

    std::vector<std::string> get_string(){
        return list;
    }
    std::vector<std::string> list;
    std::stringstream ss;
};
int main(){
    Function foo;
    for(auto& s: foo(1, 3.0)(2,"foo")(3, 5.0f).get_string())
    {
        std::cout << s << std::endl;
    }
} 
like image 169
rahnema1 Avatar answered Nov 15 '22 18:11

rahnema1


If your intention is call foo() in this way

foo({1, 1} , {2, 2.2}, {3, "3"}); 

using curly braces with values instead of explicit std::pair<int, int>{1,1}, std::pair<int, double>{1, 2.2}, .... ... I don't think it's possible.

If you can give up the curly braces, so calling foo() in this way,

foo(1, 1 , 2, 2.2, 3, "3"); 

and constructing pair inside foo(), you can do something like

#include <string>
#include <utility>

void foo ()
 { 
   // do nothing ?
 }

template <typename T2, typename ... Ts>
void foo (int v1, const T2 & v2, const Ts & ... vs)
 { 
   std::pair<int, T2>  p { v1, v2 };

   // do something with p

   foo(vs...);
 }

int main()
 {
   foo(1, 1, 1, 2.2, 1, std::string("3"));

   return 0;
 }

But, frankly, I didn't like this solution, because isn't clear wich pairs are calling foo(), so I think it's a better way use rahnema1's solution calling foo() using make_pair()

foo(std::make_pair(1, 1), std::make_pair(1, 2.2),
    std::make_pair(1, std::string("3")));
like image 32
max66 Avatar answered Nov 15 '22 19:11

max66


Closest I was able to get was a variadic template of pairs, in which the 2nd option is a template type, but the same type for every pair.

template<typename DataType, template<class, class> typename ...Pair>
    void foo(const std::pair<int, DataType>& nextParam, const Pair<int, DataType>& ...remainingParams);
void foo({1, "a"}, {2, "b"}, {3, "c"}); // works
void foo({1, "a"}, {2, 3.14}, {3, 'A'}); // doesn't work

Perhaps there is a way to get the template parameter to be variadic. That said I'm a bit doubtful of that, you essentially need to be able to specify a single element of a pack or rather to use a single parameter from a pack without expanding it, but also acknowledging that it is in fact a pack.

like image 25
Robert Hull Avatar answered Nov 15 '22 20:11

Robert Hull