¿Cómo hacer un pliegue agrupado o emparejado del paquete de parámetros?

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

Sé que puedo usar una lista de pares o algo así en su lugar, pero estoy interesado en cómo hacer esto mientras mantengo la sintaxis de la función para:

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

Respuestas:

9

¡Puedes usar una expresión de pliegue! No es el más bonito *, pero es más corto que todas las soluciones no plegables presentadas:

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

Demostración con salida de muestra: https://godbolt.org/z/Gs8d2x

Realizamos un pliegue sobre el operador de coma, donde cada operando es una salida de uno argsy el token alternativo, además de cambiar el índice del token (los dos últimos se combinan con otro operador de coma).

* Para un lector familiarizado con las expresiones de pliegue (y el operador de coma), este es probablemente el "mejor" código, pero para todos los demás es una tontería, así que use su propio criterio si desea infligir esto en su base de código.

Max Langhof
fuente
Supongo que esto también podría funcionar con un bool (si solo se necesita el emparejamiento) ala. : b ^ = verdadero; y luego quizás operador de tenary (b? ": '", ":"' ")
darune
1
@darune Claro, hay otras formas de expresar la alternancia. Decidí separar la lógica de salida / alternancia de los valores de token reales, que la matriz logra muy bien. No me gusta la conversión implícita de boola intal indexar, así que fui con un real intpara alternar el estado. Y el prefijo anterior al posterior ++requiere ciclos mentales adicionales para verificar (al menos para mí), mientras que el separado 1 - no se puede leer mal. En resumen, traté de mantener esto lo más legible posible, pero esto, por supuesto, depende del gusto personal (o de la guía de estilo correspondiente). max66 lo condensó mucho más.
Max Langhof
Usar una std::arraymatriz en lugar de una matriz nativa parece una complicación inútil.
Deduplicador el
@Deduplicator No estoy de acuerdo, ya que encuentro std::array<const char*, 2>infinitamente más legible que const char**. Pero, una vez más, esta es mi mejor oportunidad de legibilidad en torno a una sintaxis bastante oscura, puedes hacer lo que quieras en tu propio código. Todo lo que puedo hacer es darle el punto de datos de lo que considero legible.
Max Langhof
9

Esto es fácil con un par de funciones auxiliares que siguen el siguiente patrón.

void helper() {}

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

Esta no es una expresión de pliegue, pero el resultado neto es el mismo.

norte. 'pronombres' m.
fuente
¿La profundidad de recursión de plantilla será diferente que con una expresión de pliegue? o será lo mismo
darune el
1
@darune No hay recursión inherente con las expresiones de plegado ... Las expresiones de plegado solo se expanden formalmente a alguna expresión (en esa instanciación específica de la plantilla variadic).
Max Langhof
6

Supongo que puedes probar con un índice y un operador ternario.

Algo como sigue

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
fuente
@MaxLanghof Esto tiene la ventaja (?) De una fácil extensión a más separadores.
Deduplicador el
@Dupuplicator ¿No entiendo a qué te refieres? ¿Puedes explicar?
Max Langhof
@Dupuplicator: no me queda claro qué quiere decir con "extensión a más separadores" ... de todos modos ... esta solución es muy similar a la aceptada; No creo que sea más o menos extensible. Supongo que es un poco más ligero (¡pequeño! Tal vez el compilador optimice de la misma manera) porque evita el uso de un std::array(que, de todos modos, es una clase ligera) pero (así que creo que es preferible la respuesta aceptada) es menos legible.
max66
2

El siguiente código debería hacer el truco. El paquete de parámetros se expande en una lista de inicializador.

#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
fuente
Esto requiere que todo argssea ​​convertible a std::strings.
nuez
@walnut, eso es correcto. Si esto no puede ser un requisito, entonces tendrá que resultar para doblar expresiones / recursividad
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...));
}

Manifestación

Jarod42
fuente