Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make grouped or paired fold of parameter pack?

template<class Msg, class... Args>
std::wstring descf(Msg, Args&&... args) {
    std::wostringstream woss;

    owss << Msg << ". " << ... << " " << args << ": '" << args << "' ";//not legal at all

    //or

    owss << Msg << ". " << args[0] << ": '" << args[1] << "'  " << args[2] << ": '" << args[3] << "' "; //... pseudo code, and so on...
}

I know I can just use a list of pairs or something like that instead, but I'm interested in how to do this while keeping the syntax of the function to:

const auto formatted = descf(L"message", "arg1", arg1, "arg2", arg2);
like image 620
darune Avatar asked Dec 04 '19 11:12

darune


4 Answers

You can use a fold expression! It's not the prettiest*, but it's shorter than all the non-fold solutions presented:

template<class T, class ... Args>
std::wstring descf(T msg, Args&&... args) {
    std::wostringstream owss;
    owss << msg << ". ";

    std::array<const char*, 2> tokens{": '", "' "};
    int alternate = 0;
    ((owss << args << tokens[alternate], alternate = 1 - alternate), ...);

    return owss.str();
}

Demo with sample output: https://godbolt.org/z/Gs8d2x

We perform a fold over the comma operator, where each operand is an output of one args and the alternating token, plus switching the token index (the latter two are combined with another comma operator).

*To a reader familiar with fold expressions (and the comma operator) this is probably the "best" code, but for everyone else it's utter gibberish, so use your own judgement whether you want to inflict this on your code base.

like image 176
Max Langhof Avatar answered Nov 13 '22 05:11

Max Langhof


This is easy with a couple of helper functions that follow the following pattern.

void helper() {}

template <class T1, class T2, class ... T>
void helper(T1 t1, T2 t2, T ... t)
{
     do_single_pair(t1, t2);
     helper(t...);
}

This is not a fold expression but the net result is the same.

like image 29
n. 1.8e9-where's-my-share m. Avatar answered Nov 13 '22 06:11

n. 1.8e9-where's-my-share m.


I suppose you can try with an index and a ternary operator.

Something as follows

template <typename ... Args>
std::wstring descf (std::wstring const & Msg, Args && ... args)
 {
   std::wostringstream woss;

   int i = 0;

   ((woss << Msg << ". "), ... ,(woss << args << (++i & 1 ? ": '" : "' ")));

   return woss.str();
 }
like image 6
max66 Avatar answered Nov 13 '22 05:11

max66


The following code should do the trick. The parameter pack is expanded in an initializer list.

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

template <typename...Args>
std::string descf(std::string msg, Args &&... args)
{
   auto argumentsVector = std::vector<std::string>{args...};

   std::stringstream ss;
   ss << msg << ". ";

   for (auto i = std::size_t{0}; i < argumentsVector.size() - 1; ++i)
      ss << argumentsVector[i] << ": '" << argumentsVector[i+1] << "' ";

   auto result = ss.str();
   if (!argumentsVector.empty())
       result.pop_back();
   return result;
}

int main()
{
   std::cout << descf("message", "arg1", "1", "arg2", "2") << std::endl;
}
like image 2
Mattias De Charleroy Avatar answered Nov 13 '22 05:11

Mattias De Charleroy