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

3

10

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 { }, { }

Orme answered 14/7, 2016 at 9:51 Comment(1)
You've already got the answer, but you've ruled it out for cosmetic reasons.Medical
L
5

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;
    }
} 
Lantha answered 14/7, 2016 at 10:25 Comment(1)
that's not the question, the OP wants to call foo({...}, {...}, {...}...) rather than with the pair<int>{}, pair<double>{} etc..Heuer
U
5

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")));
Undetermined answered 14/7, 2016 at 12:48 Comment(2)
I still find this solution to be quite commendable. Nice effort.Orme
@Orme - thanks; but I'm fascinated by the rahnema1 solution (the improved: the recursive operator()); not exactly based on variadic templates but (IMHO) really elegant.Undetermined
H
0

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.

Hemimorphic answered 17/5, 2020 at 3:32 Comment(1)
I have to call your method using: foo<const char*, std::pair, std::pair>({1, "a"}, {2, "b"}, {3, "c"});. If I try this: foo<const char*>({1, "a"}, {2, "b"}, {3, "c"}); // works then I get error: too many arguments to function. If I try this: foo({1, "a"}, {2, "b"}, {3, "c"}); then I get error: no matching function ... couldn't deduce template parameter ‘DataType’. Any ideas? I'd love to use your method...Beaker

© 2022 - 2024 — McMap. All rights reserved.