Así que tengo algún tipo X:

typedef ... X;

y una plantilla de función f:

class <typename T>
void f(X& x_out, const T& arg_in);

y, a continuación, una función g:

void g(const X* x_array, size_t x_array_size);

Necesito escribir una variadic función de la plantilla h que hace esto:

template<typename... Args>
void h(Args... args)
{
    constexpr size_t nargs = sizeof...(args); //get number of args
    X x_array[nargs]; //create X array of that size

    for (int i = 0; i < nargs; i++) //foreach arg
        f(x_array[i], args[i]); //call f (doesn't work)

    g(x_array, nargs); //call g with x_array
}

La razón por la que no trabaja es porque no se puede subíndice argumentos como que en tiempo de ejecución.

¿Cuál es la mejor técnica para sustituir la parte media de h?

Y el ganador es Xeo:

template<class T> X fv(const T& t) { X x; f(x,t); return x; }

template<class... Args>
void h(Args... args)
{
  X x_array[] = { fv(args)... };

  g(x_array, sizeof...(Args));
}

(En realidad en mi caso particular puedo volver a escribir f para devolver la x por el valor en lugar de como un parámetro de salida, así que ni siquiera tengo fv arriba)

  • args no es una matriz — ¿qué esperabas?
  • Seguro. No me lo esperaba para trabajar, sólo la utilizan como pseudo-código para describir el problema.
  • Ya que usted está utilizando C++11, no puedes hacer h tomar una lista de inicializador y trabajar con vectores en lugar de matrices?
  • Los tipos de los parámetros h son heterogéneos. Creo que initializer_list es homogéneo y parámetros.

5 Comentarios

  1. 25

    Podría refactorizar o envoltura f devolver una nueva X en lugar de tener que pasar, ya que esto juegan pack de expansión en la mano y hacer la función realmente concisa:

    template<class T>
    X fw(T const& t){ X x; f(x, t); return x; }
    
    template<class... Args>
    void h(Args... args){
      X xs[] = { fw(args)... };
      g(xs, sizeof...(Args));
    }

    Un vivo ejemplo.

    Y si pudiera cambiar g simplemente aceptar una std::initializer_list, que sería aún más concisa:

    template<class... Args>
    void h(Args... args){
      g({f(args)...});
    }

    Un vivo ejemplo. O (tal vez mejor), también se puede proporcionar simplemente un contenedor de g que reenvía a la real g:

    void g(X const*, unsigned){}
    
    void g(std::initializer_list<X> const& xs){ g(xs.begin(), xs.size()); }
    
    template<class... Args>
    void h(Args... args){
      g({f(args)...});
    }

    Un vivo ejemplo.

    Edición: Otra opción es el uso de una matriz temporal:

    template<class T>
    using Alias = T;
    
    template<class T>
    T& as_lvalue(T&& v){ return v; }
    
    template<class... Args>
    void h(Args... args){
      g(as_lvalue(Alias<X[]>{f(args)...}), sizeof...(Args));
    }

    Un vivo ejemplo. Tenga en cuenta que el as_lvalue función es peligroso, la matriz todavía vive sólo hasta el final de la expresión completa (en este caso g), así que tenga cuidado al usarlo. El Alias es necesario, ya que sólo X[]{ ... } no es posible debido a que el lenguaje de la gramática.

    Si todo eso no es posible, tendrás la recursividad para acceder a todos los elementos de la args pack.

    #include <tuple>
    
    template<unsigned> struct uint_{}; //compile-time integer for "iteration"
    
    template<unsigned N, class Tuple>
    void h_helper(X (&)[N], Tuple const&, uint_<N>){}
    
    template<unsigned N, class Tuple, unsigned I = 0>
    void h_helper(X (&xs)[N], Tuple const& args, uint_<I> = {}){
      f(xs[I], std::get<I>(args));
      h_helper(xs, args, uint_<I+1>());
    }
    
    template<typename... Args>
    void h(Args... args)
    {
        static constexpr unsigned nargs = sizeof...(Args);
        X xs[nargs];
    
        h_helper(xs, std::tie(args...));
    
        g(xs, nargs);
    }

    Un vivo ejemplo.

    Edición: Inspirado por ecatmur comentario, empleados de la los índices de truco para que funcione sólo con paquete de expansión y con f y g tal como es, sin alterarlas.

    template<unsigned... Indices>
    struct indices{
      using next = indices<Indices..., sizeof...(Indices)>;
    };
    template<unsigned N>
    struct build_indices{
      using type = typename build_indices<N-1>::type::next;
    };
    template <>
    struct build_indices<0>{
      using type = indices<>;
    };
    template<unsigned N>
    using IndicesFor = typename build_indices<N>::type;
    
    template<unsigned N, unsigned... Is, class... Args>
    void f_them_all(X (&xs)[N], indices<Is...>, Args... args){
      int unused[] = {(f(xs[Is], args), 1)...};
      (void)unused;
    }
    
    template<class... Args>
    void h(Args... args){
      static constexpr unsigned nargs = sizeof...(Args);
      X xs[nargs];
      f_them_all(xs, IndicesFor<nargs>(), args...);
      g(xs, nargs);
    }

    Un vivo ejemplo.

    • X xs[] = { f(args)... }; es bastante cool. En realidad el caso específico de mi problema es de MySQL MYSQL_BIND (= X), la estructura y no estoy seguro de si se puede copiar. Pero si que es muy agradable.
    • Añadido ejemplos en vivo para todos los fragmentos de código, corregidos varios errores tipográficos y proporciona un contenedor opción para g. Espero algo de ayuda con MYSQL cosas. 🙂
    • En realidad si nos fijamos en el ejemplo de código aquí: dev.mysql.com/doc/refman/5.5/en/mysql-stmt-execute.html. El MYSQL_BIND struct puede ser copiado así que creo que la solución va a trabajar.
    • En su primera muestra creo que se puede descartar length_of y sólo llamar sizeof... más, ¿no?
    • Sí, es un remanente de la refactorización. En primer lugar, yo sólo tenía X xs[], pero necesitaba cambiar X xs[sizeof...(Args)] porque el anterior no coincide T(&)[N] por el motivo que sea. Sin embargo, la adición de la matriz de tamaño explícitamente un poco anula la necesidad de length_of sí mismo, así que sí… 😛
    • También es posible crear una matriz temporal? Si es así la primera muestra de cuerpo de la función puede ser { g(X[] { f(args)...}, sizeof...(args)); }. No estoy seguro si esto funciona.
    • Usted no necesita la recursividad si expande la llama a f como un parámetro pack de expansión; ver mi respuesta.
    • Pensé que al principio, pero no está permitido tomar la dirección de un temporal, y aquí la matriz había necesidad de caries. Un poco de función auxiliar de las obras, aunque, para hacer de él un lvalue. Editado en. Cuidado de la as_lvalue función, aunque.
    • Creo que su primer ejemplo es el ganador por ser más limpio.
    • Buen punto, pero no como se lo muestra en su respuesta. 🙂 Se puede hacer con los índices de truco que hace que un paquete de índices a partir de un parámetro de paquete y, a continuación, hace f(xs[indices], args)...). Demasiado perezoso para escribir que, sin embargo.
    • Los otros tres de la misma idea de evitar las copias, sin embargo. Y usted puede incluso hacer que el contenedor g un lambda dentro de h: auto gw = [](std::initializer_list<X> const& xs){ g(xs.begin(), xs.size()); }; gw({f(args)...});, y debo decir que personalmente me gusta mejor que uno.
    • nah, brace-inicializador-las listas se ordenan. He añadido el parámetro de índice pack técnica, aunque.
    • Meh, yo hice eso también. xD Y gracias, no sabía de la parte de braced-init-lists de ser secuenciado de izquierda a derecha.

  2. 6

    Es obvio: no utilizar la iteración, pero la recursividad. Cuando se trata con variadic plantillas de algo recursiva siempre viene en. Incluso cuando la unión de los elementos para un std::tuple<...> utilizando tie() es recursiva: lo que ocurre Es que la recursiva de hacer negocios en la tupla. En tu caso, parece que quiere algo como esto (probablemente hay un par de erratas, pero en general esto debería funcionar):

    template <int Index, int Size>
    void h_aux(X (&)[Size]) {
    }
    
    template <int Index, int Size, typename Arg, typename... Args>
    void h_aux(X (&xs)[Size], Arg arg, Args... args) {
        f(xs[Index], arg);
        h_aux<Index + 1, Size>(xs, args...);
    }
    
    template <typename... Args>
    void h(Args... args)
    {
        X xs[sizeof...(args)];
        h_aux<0, sizeof...(args)>(xs, args...);
        g(xs, sizeof...(args));
    }

    Creo que usted no será capaz de utilizar nargs para definir el tamaño de la matriz: Nada indica al compilador que debe ser una expresión constante.

    • He gcc extensiones en lo que creo que puede manejar dynamicly matriz de tamaño parecido. Pero si usted no, usted puede poner la sizeof… directamente en el tamaño de la matriz, como lo han hecho.
    • Usted no debe confiar en el compilador de extensiones.
    • Depende de la portabilidad de los requisitos de su proyecto. Muchas de las extensiones son muy útiles, así que tiene más sentido usar si usted está apuntando a una única plataforma.
    • Incluso entonces usted no debería, los requisitos pueden cambiar, como el soporte para extensiones.
    • Es una cuestión de equilibrio.
    • Yo estaría de acuerdo con algo si la extensión que realmente aporta algo que de otro modo sería difícil o imposible de expresar. Pero en los casos en los que un simple estándar, existe una alternativa (como este), la extensión se convierte en inútil y es innecesariamente poco fiables.
    • La verdad que sí, estoy de acuerdo con usted. Una mejor solución sería la de declarar nargs constexpr.
    • Creo que este foro es acerca de C++ no se trata de C++ con extensiones. Útil código está obligado a ser portado a un ambiente diferente y el uso de extensiones que se pueden obtener en el camino. Debido a que el soporte para la variable tamaño de las matrices no es realmente necesario en este contexto, me gustaría pensar que es mejor evitar. El código de ejemplo que he publicado evita el problema mediante el uso de sizeof...() pero el uso de constexpr es otra alternativa. En cualquier caso, el uso de nargs no funciona como argumento de plantilla a menos que se realice una expresión constante.
    • Creo que este no es un foro!

  3. 5

    Buena plantilla como respuesta para la primera parte de la pregunta:

    template <class F, class... Args> 
    void for_each_argument(F f, Args&&... args) {
        [](...){}((f(std::forward<Args>(args)), 0)...);
    }
    • Muy interesante el método de simulación de veces expresiones.
    • Esta solución, aunque bonito, no garantiza el orden de evaluación. Ver: godbolt.org/g/2KiZCD
  4. 4

    Es bastante simple de hacer con el parámetro pack de expansión, incluso si usted no puede reescribir f para devolver la salida de parámetros por valor:

    struct pass { template<typename ...T> pass(T...) {} };
    
    template<typename... Args>
    void h(Args... args)
    {
        const size_t nargs = sizeof...(args); //get number of args
        X x_array[nargs]; //create X array of that size
    
        X *x = x_array;
        int unused[]{(f(*x++, args), 1)...}; //call f
        pass{unused};
    
        g(x_array, nargs); //call g with x_array
    }

    Debe ser posible escribir

        pass{(f(*x++, args), 1)...}; //call f

    pero parece que g++ (4.7.1 al menos) tiene un error en el que falla a fin de que la evaluación de la llave-inicializador-lista de parámetros como la clase de inicializadores. Matriz de inicializadores son bien; ver La secuenciación entre un variadic de expansión para obtener más información y ejemplos.

    Ejemplo en vivo.


    Como una alternativa, he aquí la técnica mencionada por Xeo el uso de un índice generado pack; lamentablemente no requieren un extra de llamada de función y los parámetros, pero es bastante elegante (especialmente si le sucede que tiene un índice de generación de paquetes en todo mentira):

    template<int... I> struct index {
        template<int n> using append = index<I..., n>; };
    template<int N> struct make_index { typedef typename
        make_index<N - 1>::type::template append<N - 1> type; };
    template<> struct make_index<0> { typedef index<> type; };
    template<int N> using indexer = typename make_index<N>::type;
    
    template<typename... Args, int... i>
    void h2(index<i...>, Args... args)
    {
        const size_t nargs = sizeof...(args); //get number of args
        X x_array[nargs]; //create X array of that size
    
        pass{(f(x_array[i], args), 1)...}; //call f
    
        g(x_array, nargs); //call g with x_array
    }
    
    template<typename... Args>
    void h(Args... args)
    {
      h2(indexer<sizeof...(args)>(), std::forward<Args>(args)...);
    }

    Ver C++11: puedo ir de múltiples argumentos para tupla, pero puedo ir de tupla a múltiples argumentos? para obtener más información.
    Ejemplo en vivo.

    • Es el parámetro de paquete garantizado para ser evaluados en orden? Me parece recordar que no es, pero podría ser misremembering.
    • parámetro paquetes de no tener ningún tipo especial de orden de evaluación; en este caso estoy usando una llave-lista adjunta que está garantizado para ser evaluados de izquierda a derecha (8.5.4:4).
    • Usted no necesita std::forward si usted no hace h aceptar universal referencias (Args&&...).
  5. 0

    Xeo es en la idea de derecho – usted quiere construir algún tipo de «variadic iterador» que esconde una gran cantidad de este rudo del resto del código.

    Me gustaría tener el índice cosas y se esconden detrás de un iterador de la interfaz de modelado después de std::vector, desde un std::tupla es también lineal contenedor de datos. A continuación, puede volver a utilizar toda su variadic las funciones y las clases sin tener que explícitamente código recurrente en cualquier otro lugar.

Dejar respuesta

Please enter your comment!
Please enter your name here