Comment faire un pli groupé ou apparié d'un pack de paramètres?

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

Je sais que je peux simplement utiliser une liste de paires ou quelque chose comme ça à la place, mais je suis intéressé par la façon de le faire tout en conservant la syntaxe de la fonction pour:

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

Réponses:

9

Vous pouvez utiliser une expression de pli! Ce n'est pas la plus jolie *, mais elle est plus courte que toutes les solutions non pliantes présentées:

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

Démo avec exemple de sortie: https://godbolt.org/z/Gs8d2x

Nous effectuons un repli sur l'opérateur virgule, où chaque opérande est une sortie de l'un argset du jeton alterné, plus la commutation de l'index du jeton (les deux derniers sont combinés avec un autre opérateur virgule).

* Pour un lecteur familiarisé avec les expressions de repli (et l'opérateur de virgule), c'est probablement le "meilleur" code, mais pour tout le monde, c'est du charabia absolu, alors utilisez votre propre jugement si vous voulez infliger cela à votre base de code.

Max Langhof
la source
Je suppose que cela pourrait également fonctionner avec un booléen (si seulement l'appariement est nécessaire) ala. : b ^ = vrai; et puis peut-être opérateur tenaire (b? ": '", ":"' ")
darune
1
@darune Bien sûr, il existe d'autres façons d'exprimer l'alternance. J'ai décidé de séparer la logique de sortie / alternance des valeurs de jeton réelles, ce que le tableau accomplit bien. Je n'aime pas la conversion implicite de boolà intlors de l'indexation, je suis donc allé avec un réel intpour basculer l'état. Et pré-vs postfix ++prend des cycles mentaux supplémentaires pour vérifier (pour moi au moins), tandis que le séparé 1 - ne peut pas vraiment être mal lu. En bref, j'ai essayé de garder cela aussi lisible que possible, mais cela dépend bien sûr du goût personnel (ou du guide de style applicable). max66 l'a condensé beaucoup plus.
Max Langhof
Utiliser un std::arrayau lieu d'un tableau natif semble une complication inutile.
Déduplicateur
@Deduplicator Je suis en profond désaccord, car je trouve std::array<const char*, 2>infiniment plus lisible que const char**. Mais encore une fois, c'est ma meilleure chance de lisibilité autour d'une syntaxe assez obscure, vous pouvez en faire ce que vous aimez dans votre propre code. Tout ce que je peux faire, c'est vous donner le point de données de ce que je considère comme lisible.
Max Langhof
9

C'est facile avec quelques fonctions d'assistance qui suivent le modèle suivant.

void helper() {}

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

Ce n'est pas une expression de pli mais le résultat net est le même.

n. «pronoms» m.
la source
la profondeur de récursivité du modèle sera-t-elle différente de celle d'une expression de pli? ou sera-ce la même
darune
1
@darune Il n'y a pas de récursivité inhérente avec les expressions de repli ... Les expressions de repli se développent simplement formellement en une expression (dans cette instanciation spécifique du modèle variadique).
Max Langhof
6

Je suppose que vous pouvez essayer avec un index et un opérateur ternaire.

Quelque chose comme suit

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
la source
@MaxLanghof Cela a l'avantage (?) D'être facilement étendu à plus de séparateurs.
Déduplicateur
@Deduplicator Je ne comprends pas de quoi vous parlez? Peux-tu expliquer?
Max Langhof
@Deduplicator - Je ne comprends pas ce que vous voulez dire par "extension à plus de séparateurs" ... de toute façon ... cette solution est très similaire à la solution acceptée; Je ne pense pas que ce soit plus ou moins extensible. Je suppose que c'est un peu (peu! Peut-être que le compilateur optimise de la même manière) plus léger car évitez d'utiliser un std::array(qui, de toute façon, est une classe légère) mais (donc je pense que c'est préférable la réponse acceptée) est moins lisible.
max66
2

Le code suivant devrait faire l'affaire. Le pack de paramètres est développé dans une liste d'initialisation.

#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
la source
Cela exige que tout argssoit convertible en std::stringart.
noyer
@walnut, c'est exact. Si cela ne peut pas être une exigence, alors vous devrez faire plier les expressions / récursivité
Mattias De Charleroy
1

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

Démo

Jarod42
la source