Wie mache ich eine gruppierte oder gepaarte Falte des Parameterpakets?

14
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...
}

Ich weiß, dass ich stattdessen nur eine Liste von Paaren oder ähnliches verwenden kann, aber ich bin daran interessiert, wie dies zu tun ist, während die Syntax der Funktion wie folgt beibehalten wird:

const auto formatted = descf(L"message", "arg1", arg1, "arg2", arg2);
Darune
quelle

Antworten:

9

Sie können einen Falzausdruck verwenden! Es ist nicht das schönste *, aber es ist kürzer als alle vorgestellten nicht gefalteten Lösungen:

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 mit Beispielausgabe: https://godbolt.org/z/Gs8d2x

Wir führen eine Faltung über den Kommaoperator durch, wobei jeder Operand eine Ausgabe von einem argsund dem alternierenden Token ist, plus das Umschalten des Tokenindex (die beiden letzteren werden mit einem anderen Kommaoperator kombiniert).

* Für einen Leser, der mit Fold-Ausdrücken (und dem Komma-Operator) vertraut ist, ist dies wahrscheinlich der "beste" Code, aber für alle anderen ist es völliger Kauderwelsch. Verwenden Sie also Ihr eigenes Urteil, ob Sie dies Ihrer Codebasis zufügen möchten.

Max Langhof
quelle
Ich denke, dies könnte auch mit einem Bool funktionieren (wenn nur eine Paarung erforderlich ist). : b ^ = wahr; und dann vielleicht Tenary Operator (b? ": '", ":"' ")
Darune
1
@darune Sicher, es gibt andere Möglichkeiten, die Abwechslung auszudrücken. Ich habe beschlossen, die Ausgabe- / Wechsellogik von den tatsächlichen Tokenwerten zu trennen, was das Array gut macht. Ich mag die implizite Konvertierung von boolzu intbei der Indizierung nicht, also habe ich mich für eine tatsächliche Konvertierung entschieden , intum den Status umzuschalten. Und Pre-vs-Postfix ++benötigt zusätzliche mentale Zyklen, um dies zu überprüfen (zumindest für mich), während das separate 1 - nicht wirklich falsch verstanden werden kann. Kurz gesagt, ich habe versucht, dies so lesbar wie möglich zu halten, aber dies hängt natürlich vom persönlichen Geschmack (oder dem entsprechenden Styleguide) ab. max66 verdichtete es viel mehr.
Max Langhof
Die Verwendung eines std::arrayanstelle eines nativen Arrays scheint eine sinnlose Komplikation zu sein.
Deduplikator
@Deduplicator Ich bin nicht einverstanden, da ich std::array<const char*, 2>unendlich lesbarer finde als const char**. Aber auch dies ist mein bester Versuch, die Lesbarkeit einer ziemlich obskuren Syntax zu verbessern. Sie können damit machen, was Sie in Ihrem eigenen Code mögen. Ich kann Ihnen nur den Datenpunkt geben, den ich für lesbar halte.
Max Langhof
9

Dies ist mit einigen Hilfsfunktionen, die dem folgenden Muster folgen, einfach.

void helper() {}

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

Dies ist kein Fold-Ausdruck, aber das Nettoergebnis ist dasselbe.

n. 'Pronomen' m.
quelle
Wird die Rekursionstiefe der Vorlage anders sein als bei einem Falzausdruck? oder wird es das gleiche sein
darune
1
@darune Es gibt keine inhärente Rekursion mit Faltausdrücken ... Faltausdrücke werden nur formal zu einem Ausdruck erweitert (in dieser spezifischen Instanziierung der variadischen Vorlage).
Max Langhof
6

Ich nehme an, Sie können es mit einem Index und einem ternären Operator versuchen.

Etwas wie folgt

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();
 }
max66
quelle
@MaxLanghof Dies hat den Vorteil (?) Der einfachen Erweiterung auf mehr Separatoren.
Deduplikator
@Deduplicator Ich verstehe nicht, worauf Sie sich beziehen? Können Sie erklären?
Max Langhof
@Deduplicator - Mir ist nicht klar, was Sie mit "Erweiterung auf mehr Trennzeichen" meinen ... sowieso ... diese Lösung ist der akzeptierten sehr ähnlich; Ich denke nicht, dass es mehr oder weniger erweiterbar ist. Ich nehme an, das ist ein wenig (wenig! Vielleicht optimiert der Compiler auf die gleiche Weise) leichter, weil die Verwendung von a std::array(das ist sowieso eine leichte Klasse) vermieden wird, aber (daher halte ich die akzeptierte Antwort für vorzuziehen) weniger lesbar ist.
Max66
2

Der folgende Code sollte den Trick machen. Das Parameterpaket wird in einer Initialisierungsliste erweitert.

#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;
}
Mattias De Charleroy
quelle
Dies erfordert, dass alle argsin std::strings konvertierbar sind .
Walnuss
@walnut, das ist richtig. Wenn dies nicht erforderlich sein kann, müssen Sie Ausdrücke / Rekursion falten
Mattias De Charleroy
1

Mit std::index_sequence:

template <class Msg, class... Pairs>
std::wstring descf_pair(const Msg& msg, const Pairs&... pairs)
{
    std::wstringstream woss;

    woss << msg << ". ";
    auto sep = L"";
    ((woss << sep << std::get<0>(pairs) << L": '"
                  << std::get<1>(pairs) << L"'", sep = L"  "), ...);
    return woss.str();
}

template <class Msg, std::size_t... Is, class Tuple>
decltype(auto) descf_impl(const Msg& msg, std::index_sequence<Is...>, Tuple&& t)
{
    return descf_pair(msg, std::tie(std::get<2 * Is>(t), std::get<2 * Is + 1>(t))...);
}

template <class Msg, typename ... Ts>
std::wstring descf(const Msg& msg, const Ts&... ts)
{
    static_assert(sizeof...(Ts) % 2 == 0);

    return descf_impl(msg,
                      std::make_index_sequence<sizeof...(Ts) / 2>(),
                      std::tie(ts...));
}

Demo

Jarod42
quelle