Tengo un problema… no entiendo Plantilla de Metaprogramación.

El Problema es este: he leído un montón. Pero no tiene mucho sentido para mí :/

Hecho nr.1: Plantilla de Metaprogramación es más rápido

template <int N>
struct Factorial 
{
    enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> 
{
    enum { value = 1 };
};

//Factorial<4>::value == 24
//Factorial<0>::value == 1
void foo()
{
    int x = Factorial<4>::value; //== 24
    int y = Factorial<0>::value; //== 1
}

Por lo que este Metaprogram es más rápido … por la Constante Literal.

PERO: Donde en el Mundo real tenemos constantes Literales?

La mayoría de los programas que uso reaccionar de la entrada del usuario.

HECHO nr. 2 : Plantilla de Metaprogramación puede efectuar mejor mantenibilidad.

Sí. El Factorial Ejemplo Puede Ser el de mantener… pero cuando se trata de funciones complejas, yo y la mayoría de otros programadores de C++ no puede leer las funciones.

Y las opciones de Depuración son buenos (o al menos yo no sé cómo depurar).

¿De dónde Plantilla de Metaprogramación sentido?

  • Nunca hizo mucho sentido para mí…
  • No te sientas mal, casi nadie lo consigue.
InformationsquelleAutor n00ki3 | 2009-08-03

11 Comentarios

  1. 27

    Tan factorial no es un realista ejemplo de recursividad en no de los lenguajes funcionales, tampoco es un realista ejemplo de plantilla de metaprogramación. Es sólo el estándar de ejemplo a la gente a alcanzar para cuando se quiere mostrar la recursividad.

    En la escritura de plantillas para los realistas, a los efectos de, como en la vida cotidiana de las bibliotecas, a menudo, la plantilla se tiene que adaptar a lo que hace, dependiendo del tipo de parámetros se crea una instancia con. Esto puede llegar a ser muy complejo, ya que la plantilla efectivamente elige cuál es el código para generar, de forma condicional. Esto es lo que la plantilla de metaprogramación es; si la plantilla ha bucle (a través de la recursividad) y elegir entre alternativas, efectivamente es como un pequeño programa que se ejecuta durante la compilación para generar el código correcto.

    Aquí hay un muy buen tutorial de el impulso páginas de documentación (en realidad un extracto de un libro brillante, bien vale la pena leer).

    http://www.boost.org/doc/libs/1_39_0/libs/mpl/doc/tutorial/representing-dimensions.html

    • Buena respuesta. +1 – me gustaría que la gente se había acordado en un mejor estándar de ejemplo. En general, el uso de metaprogramación para calcular los valores es simplemente inútil. Es esclarecedor como un ejercicio de aprendizaje, cuando se trata de averiguar TMP, pero no como un caso de uso, y no como un punto de venta para convencer a la gente de lo increíble que es la característica.
    • Yo no escribiría fuera de la generación de valor completamente 🙂 stackoverflow.com/questions/699781/c-binary-constant-literal
    • Por supuesto que puede ser útil, pero no es realmente el gran punto de venta para convencer a la gente de que la característica que vale la pena. El binario ejemplo es mejor que de costumbre, pero aún así, la pregunta obvia es «¿por qué no hacerlo en tiempo de ejecución? No se como es carísimo.» Es que realmente la justificación para la estratificación de otro turing-completo idioma en la parte superior de C++? 😉
    • La forma en que Stroustrup lo dice, nadie se decidió a hacerlo, así que no había necesidad de una justificación! 🙂 Me di cuenta después. Las piezas son útiles por su propia cuenta, incluso antes de ponerlos juntos en una combinación compleja. Pero estoy de acuerdo con tu punto general, realmente se convierte en una característica interesante cuando se construye un DSL interno con él.
    • Como de costumbre, un comentario de la downvoter podría ser interesante (pero, de nuevo, puede que no).
    • Absolutamente. Su value_type ejemplo es mucho mejor como un punto de partida – es un «si» en lugar de un «bucle» y lo ejemplifica probablemente el 90% de los usos de la plantilla de la especialización. Y en un ejemplo más avanzado que combina todo, yo sugeriría que la gente trate de trabajar a través del impulso del parámetro de la biblioteca. O tal vez escribir sus propios (en realidad es más fácil de entender de otra persona!)

  2. 15

    Yo uso la plantilla de mete-programación para ESS swizzling operadores para optimizar la mezcla durante el tiempo de compilación.

    ESS swizzles (‘baraja’) sólo puede ser enmascarada como un byte literal (valor inmediato), así que hemos creado una máscara de fusión’ de la plantilla de clase que combina las máscaras durante el tiempo de compilación para cuando varios shuffle ocurrir:

    template <unsigned target, unsigned mask>
    struct _mask_merger
    {
        enum
        {
            ROW0 = ((target >> (((mask >> 0) & 3) << 1)) & 3) << 0,
            ROW1 = ((target >> (((mask >> 2) & 3) << 1)) & 3) << 2,
            ROW2 = ((target >> (((mask >> 4) & 3) << 1)) & 3) << 4,
            ROW3 = ((target >> (((mask >> 6) & 3) << 1)) & 3) << 6,
    
            MASK = ROW0 | ROW1 | ROW2 | ROW3,
        };
    };

    Esto funciona y produce notables código sin código generado una sobrecarga y un poco mas de tiempo de compilación.

  3. 9

    por lo que este Metaprogram es más rápido … por la Constante Literal.
    PERO: ¿Donde en el Mundo real tenemos constantes Literales ?
    La mayoría de los programas que yo uso reaccionar de la entrada del usuario.

    Es por eso que casi nunca se utiliza para los valores. Generalmente, se utiliza en los tipos. uso de tipos de calcular y generar nuevos tipos.

    Hay muchos en el mundo real los usos, algunos de los cuales usted ya está familiarizado con, incluso si usted no darse cuenta de ello.

    Uno de mis ejemplos favoritos es el de los iteradores. La mayoría son diseñados con la programación genérica, sí, pero de la plantilla de la metaprogramación es útil en un lugar en particular:

    A la revisión de los punteros para que puedan ser utilizados como iteradores. Un iterador debe exponer un puñado de typedef, tales como value_type. Los punteros no hacerlo.

    Así que el código como el siguiente (básicamente idéntico a lo que se encuentra en el Impulso.Iterador)

    template <typename T>
    struct value_type {
      typedef typename T::value_type type;
    };
    
    template <typename T>
    struct value_type<T*> {
      typedef T type;
    };

    es muy simple, plantilla metaprogram, pero que es muy útil. Te permite obtener el tipo de valor de cualquier tipo de iterador T, si se trata de un puntero o una clase, simplemente por value_type<T>::type.

    Y creo que el de arriba tiene algunas muy claras ventajas cuando se trata de la capacidad de mantenimiento. Su algoritmo de funcionamiento en los iteradores sólo debe ser aplicado de una sola vez. Sin este truco, usted tendría que hacer una aplicación para los punteros, y otro para la «adecuada» a la clase de los iteradores.

    Trucos como boost::enable_if puede ser muy valioso, demasiado. Usted tiene una sobrecarga de una función que debe estar habilitada para un conjunto específico de un solo tipo. En lugar de definir una sobrecarga para cada tipo, puede utilizar la metaprogramación para especificar la condición y pasa a enable_if.

    Earwicker ya se ha mencionado otro buen ejemplo, un marco para expresar en unidades físicas y dimensiones. Permite expresar computaciones como con unidades físicas conectadas, y aplica el tipo de resultado. Multiplicando los metros cuadrados rinde un número de metros cuadrados. Plantilla de metaprogramación puede ser utilizado para producir automáticamente el tipo de derecho.

    Pero la mayoría del tiempo, de la plantilla de la metaprogramación es utilizado (y útil) en pequeños casos aislados, básicamente para suavizar las irregularidades y casos excepcionales, para hacer un conjunto de tipos parecen y se comportan de manera uniforme, permitiendo el uso de genéricos de programación de manera más eficiente

  4. 9

    La adscripción de la recomendación para Alexandrescu del C++ Moderno Diseño.

    Plantillas de brillar realmente cuando estás escribiendo una biblioteca que dispone de piezas que se pueden montar combinatorically en un «elegir un Foo, Bar y Baz», y se espera que los usuarios que hacen uso de estas piezas en alguna forma que se fija en tiempo de compilación. Por ejemplo, yo coautor de una de minería de datos de la biblioteca que utiliza la plantilla de metaprogramación para permitir que el programador decida qué DecisionType a utilizar (clasificación, la clasificación o regresión), lo que InputType a esperar (flotadores, enteros, valores enumerados, lo que sea), y lo KernelMethod a utilizar (es una de minería de datos de la cosa). Hemos implementado varias clases diferentes para cada categoría, de tal manera que hubo varias decenas de combinaciones posibles.

    La aplicación de 60 clases separadas para ello habría implicado un montón de molestos, difícil de mantener la duplicación de código. Plantilla de metaprogramación significaba que podíamos aplicar cada una de las concepto como una unidad de código, y dar al programador una interfaz sencilla para crear instancias de combinaciones de estos conceptos en tiempo de compilación.

    Análisis Dimensional es también un excelente ejemplo, pero otras personas han visto eso.

    También me escribió una vez algo simple en tiempo de compilación pseudo-generadores de números aleatorios sólo para meterse con la cabeza de la gente, pero que realmente no cuentan IMO.

    • Jugar con las cabezas de las personas es uno de los mejores usos que la programación se puede poner! No subestime el valor de eso. :p
  5. 5

    El factorial ejemplo, es igual de útil para el mundo real TMP como «¡Hola, mundo!» es para el común de programación: Es mostrar algunas técnicas útiles (recursividad en lugar de iteración, «else-if-then», etc.) de una forma muy sencilla, relativamente fácil de entender el ejemplo que no tiene mucha relevancia para el día a día de codificación. (¿Cuándo fue la última vez que usted necesita para escribir un programa que emitían «Hola, mundo»?)

    TMP es acerca de la ejecución de los algoritmos en tiempo de compilación y esto implica algunas ventajas obvias:

    • Ya que estos algoritmos fallando significa que su código no compila, a falta de los algoritmos de no llegar nunca a su cliente y por lo tanto no puede fallar en casa del cliente. Para mí, durante la última década, esta fue la única ventaja más importante que me llevó a introducir TMP en el código de las empresas que he trabajado.
    • Ya que el resultado de la ejecución de plantilla-meta de los programas es ordinario código que luego compiladas por el compilador, con todas las ventajas del código de generación de algoritmos de reducción de la redundancia, etc.) se aplican.
    • De curso, ya que se ejecutan en tiempo de compilación, estos algoritmos no necesita ningún tiempo de ejecución y por lo tanto se ejecute más rápido. TMP es principalmente acerca de la compilación en tiempo de computación con pocos, en su mayoría pequeños, entre líneas las funciones de roció en el medio, de modo que los compiladores tienen suficientes oportunidades para optimizar el código resultante.

    Por supuesto, hay desventajas también:

    • Los mensajes de error pueden ser horrible.
    • No hay depuración.
    • El código a menudo es difícil de leer.

    Como siempre, vas a tener peso las ventajas y desventajas en cada caso.

    Como para una información más útil ejemplo: una Vez que han entendido tipo de listas y basic en tiempo de compilación algoritmos que operan sobre ellos, que podría comprender lo siguiente:

    typedef 
        type_list_generator< signed char
                           , signed short
                           , signed int
                           , signed long
                           >::result_type
        signed_int_type_list;
    
    typedef 
        type_list_find_if< signed_int_type_list
                         , exact_size_predicate<8>
                         >::result_type
        int8_t;
    
    typedef 
        type_list_find_if< signed_int_type_list
                         , exact_size_predicate<16>
                         >::result_type
        int16_t;
    
    typedef 
        type_list_find_if< signed_int_type_list
                         , exact_size_predicate<32>
                         >::result_type
        int32_t;

    Esto es (ligeramente simplificado) código real que escribí hace un par de semanas. Se elegirá a los tipos apropiados de un tipo de lista, en sustitución de la #ifdef orgías común en el código portable. No necesita mantenimiento, funciona sin necesidad de adaptación en cada plataforma que su código puede ser que necesite para obtener portado, y emite un error de compilación si la plataforma actual no tiene el tipo correcto.

    Otro ejemplo es este:

    template< typename TFunc, typename TFwdIter >
    typename func_traits<TFunc>::result_t callFunc(TFunc f, TFwdIter begin, TFwdIter end);

    Dada una función f y una secuencia de cadenas, esto va a diseccionar la firma de la función, convertir las cadenas de la secuencia en el derecho de los tipos, y la llamada a la función con estos objetos. Y es sobre todo TMP en el interior.

  6. 3

    He aquí un ejemplo trivial, una binaria constante del convertidor, a partir de una pregunta anterior aquí en StackOverflow:

    C++ binaria constante/literal

    template< unsigned long long N >
    struct binary
    {
      enum { value = (N % 10) + 2 * binary< N / 10 > :: value } ;
    };
    template<>
    struct binary< 0 >
    {
      enum { value = 0 } ;
    };
  7. 3

    TMP no significa necesariamente más rápido o más fácil de mantener el código. He utilizado el impulso espíritu biblioteca para implementar una simple expresión SQL analizador que se basa en una evaluación de la estructura de árbol. Mientras que el tiempo de desarrollo se reduce ya que tenía cierta familiaridad con la TMP y lambda, la curva de aprendizaje es una pared de ladrillos para «C con clases» a los desarrolladores, y el rendimiento no es tan bueno como un tradicional LEX/YACC.

    Veo Plantilla Meta de la Programación como una herramienta más en mi herramienta de correa. Cuando se trabaja para que usted use, si no, utilizar otra herramienta.

  8. 3

    Scott Meyers ha estado trabajando en la aplicación de las restricciones de código de uso de TMP.

    Que es bastante una buena lectura:

    http://www.artima.com/cppsource/codefeatures.html

    En este artículo introduce los conceptos de Conjuntos de Tipos (no es un concepto nuevo, pero su trabajo se basa encima de este concepto). A continuación, utiliza TMP para asegurarse de que no importa en qué orden se especifican los miembros del conjunto que si dos conjuntos son de la misma los miembros de la entonces son equavalent. Esto requiere que sea capaz de ordenar y re-ordenar una lista de los tipos y comparar en forma dinámica y por lo tanto la generación de errores de tiempo de compilación cuando no coinciden.

    • En realidad su nombre es «Meyers». (Lo siento por pequeñeces.)
    • Corregidos. 🙂
  9. 2

    Le sugiero que lea C++ Moderno Diseño por Andrei Alexandrescu – este es probablemente uno de los mejores libros sobre el mundo real de los usos de plantillas de C++ metaprogramación; y describe muchos de los problemas que las plantillas de C++ son una excelente solución.

    • Cada desarrollador de C++ debe leer ese libro, pero no es del todo útiles para responder a la pregunta, por la misma razón que simplemente proporcionando un enlace a Google es generalmente mal visto. ASÍ que se supone que es un lugar para encontrar respuestas, no enlaces a otros recursos donde puede buscar su lugar.
    • La 3ª edición de Scott Meyers Efectiva de C++ también contiene un ejemplo que explica bastante bien. (A pesar de que se trata de la sobrecarga de funciones en lugar de meta funciones y por lo tanto podría no parecer tan «real TMP» en primer lugar.)
  10. 2

    TMP puede ser utilizado de cualquier cosa como garantizar la exactitud dimensional (para Asegurar que la masa no puede ser dividida por el tiempo, pero la distancia puede ser dividida por el tiempo, y se asigna a una velocidad variable) para la optimización de las operaciones de matrices mediante la eliminación temporal de los objetos y la fusión de los bucles, cuando muchas de las matrices involucradas.

    • la masa puede ser dividida por el tiempo. El resultado tiene que ser de tipo «masa por hora» 😉 Pero sí, TMP puede ser utilizado para reforzar eso.
    • Cualquier cosa puede ser dividido/multiplicado por algo, es la suma/resta que los viajes de la gente.
  11. 2

    ‘static const’ valores que funcionan así. Y punteros-a-miembro. Y no olvides que el mundo de tipos (explícito y dedujeron) como en tiempo de compilación argumentos!

    PERO: ¿Donde en el Mundo real tenemos constantes Literales ?

    Supongamos que usted tiene algún código que tiene que correr tan rápido como sea posible. Contiene la crítica bucle interno de la CPU-bound cálculo de hecho. Usted estaría dispuesto a aumentar el tamaño de su archivo ejecutable un poco para hacerlo más rápido. Se parece a:

    double innerLoop(const bool b, const vector<double> & v)
    {
        //some logic involving b
    
        for (vector::const_iterator it = v.begin; it != v.end(); ++it)
        {
            //significant logic involving b
        }
    
        //more logic involving b
        return ....
    }

    Los detalles no son importantes, pero el uso de la » b » es omnipresente en la aplicación.

    Ahora, con plantillas, puede refactorizar un poco:

    template <bool b> double innerLoop_B(vector<double> v) { ... same as before ... }
    double innerLoop(const bool b, const vector<double> & v)
    { return b ? innerLoop_templ_B<true>(v) : innerLoop_templ_B<false>(v) ); }

    Cualquier momento usted tiene una relativamente pequeños, discretos, conjunto de valores para un parámetro, puede automáticamente crear instancias de versiones separadas para ellos.

    Considerar las posibilidades cuando ‘b’ se basa en la detección de CPU. Puede ejecutar de una manera diferente-conjunto optimizado de código dependiendo de detección en tiempo de ejecución. Todo desde el mismo código fuente, o se puede especializar algunas de las funciones para algunos conjuntos de valores.

    Como un ejemplo concreto, una vez vi algo de código que se necesita para combinar algunas coordenadas enteras. Sistema de coordenadas de la ‘a’ fue uno de los dos resoluciones (conocido en tiempo de compilación), y el sistema de coordenadas ‘b’ fue una de las dos resoluciones diferentes (también conocido en tiempo de compilación). El objetivo del sistema de coordenadas necesarias para ser el mínimo común múltiplo de los dos de origen de los sistemas de coordenadas. Una biblioteca se utiliza para calcular el MCM a la hora de compilar y crear el código para las diferentes posibilidades.

Dejar respuesta

Please enter your comment!
Please enter your name here