Come creare una piega raggruppata o accoppiata del pacchetto di parametri?


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

So che posso semplicemente usare un elenco di coppie o qualcosa del genere, ma sono interessato a come farlo mantenendo la sintassi della funzione per:

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

Risposte:


9

Puoi usare un'espressione di piega! Non è il più grazioso *, ma è più corto di tutte le soluzioni non pieghevoli presentate:

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 con output di esempio: https://godbolt.org/z/Gs8d2x

Eseguiamo una piega sull'operatore virgola, in cui ogni operando è un output di uno argse il token alternato, oltre a cambiare l'indice token (gli ultimi due sono combinati con un altro operatore virgola).

* Per un lettore che ha familiarità con le espressioni fold (e l'operatore virgola) questo è probabilmente il codice "migliore", ma per tutti gli altri è assolutamente incomprensibile, quindi usa il tuo giudizio se vuoi infliggere questo sulla tua base di codice.


Immagino che questo potrebbe funzionare anche con un'ala bool (se è necessaria solo l'associazione). : b ^ = true; e poi forse operatore tenore (b? ": '", ":"' ")
darune

1
@darune Certo, ci sono altri modi per esprimere l'alternanza. Ho deciso di separare la logica di output / alternanza dai valori token effettivi, che l'array realizza bene. Non mi piace la conversione implicita da boola intdurante l'indicizzazione, quindi sono andato con un reale intper cambiare lo stato. E pre vs postfix ++richiede ulteriori cicli mentali per la verifica (almeno per me), mentre il separato 1 - non può davvero essere letto male. In breve, ho cercato di renderlo il più leggibile possibile, ma questo ovviamente dipende dal gusto personale (o dalla guida di stile applicabile). max66 lo ha condensato molto di più.
Max Langhof,

L'uso std::arraydi un array al posto di un array nativo sembra una complicazione inutile.
Deduplicatore il

@Deduplicatore Non sono fortemente d'accordo, poiché trovo std::array<const char*, 2>infinitamente più leggibile di const char**. Ma ancora una volta, questo è il mio colpo migliore per la leggibilità intorno ad una sintassi piuttosto oscura, puoi farci quello che ti piace nel tuo codice. Tutto quello che posso fare è darti il ​​punto dati di ciò che considero leggibile.
Max Langhof,

9

Questo è facile con un paio di funzioni di supporto che seguono il seguente schema.

void helper() {}

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

Questa non è un'espressione di piega ma il risultato netto è lo stesso.


la profondità di ricorsione del modello sarà diversa rispetto a un'espressione di piega? o sarà lo stesso
darune

1
@darune Non c'è ricorsione inerente con le espressioni fold ... Le espressioni Fold si espandono formalmente solo in alcune espressioni (in quella specifica istanza del modello variadico).
Max Langhof,

6

Suppongo che tu possa provare con un indice e un operatore ternario.

Qualcosa come segue

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();
 }

@MaxLanghof Questo ha il vantaggio (?) Di una facile estensione a più separatori.
Deduplicatore il

@Deduplicator Non capisco a cosa ti riferisci? Puoi spiegare?
Max Langhof,

@Deduplicator - Non mi è chiaro cosa intendi con "estensione a più separatori" ... comunque ... questa soluzione è molto simile a quella accettata; Non penso che sia più o meno estensibile. Suppongo che sia un po '(poco! Forse il compilatore ottimizza allo stesso modo) più leggero perché evita l'uso di un std::array(che, comunque, è una classe leggera) ma (quindi penso sia preferibile la risposta accettata) è meno leggibile.
max66,

2

Il seguente codice dovrebbe fare il trucco. Il pacchetto di parametri viene espanso in un elenco di inizializzatori.

#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;
}

Ciò richiede che tutto argssia convertibile in std::strings.
Noce

@walnut, è corretto. Se questo non può essere un requisito, allora dovrai risultare per piegare espressioni / ricorsione
Mattias De Charleroy,

1

Con 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...));
}

dimostrazione

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.