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);
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 d'un args
et 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 familier 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.
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.
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();
}
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;
}
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...));
}