2008-09-15 32 views

Respuesta

24

Una vez utilicé la metaprogramación de plantillas en C++ para implementar una técnica llamada "perturbación simbólica" para tratar la entrada degenerada en algoritmos geométricos. Al representar expresiones aritméticas como plantillas anidadas (es decir, básicamente escribiendo a mano los árboles de análisis) pude transferir todo el análisis de expresiones al procesador de plantillas.

Hacer este tipo de cosas con plantillas es más eficiente que, por ejemplo, escribir árboles de expresiones utilizando objetos y hacer el análisis en tiempo de ejecución. Es más rápido porque el árbol de expresiones modificado (perturbado) está disponible para el optimizador al mismo nivel que el resto del código, por lo que obtienes todos los beneficios de la optimización, tanto dentro de tus expresiones como (siempre que sea posible) entre tus expresiones y el código circundante.

Por supuesto, usted podría lograr lo mismo al implementar un DSL pequeño (lenguaje específico del dominio) para sus expresiones y pegar el código C++ traducido en su programa habitual. Eso le daría todos los mismos beneficios de optimización y también sería más legible, pero la compensación es que debe mantener un analizador.

+10

Prefiero mantener un analizador, personalmente, que mantener las plantillas anidadas de C++. –

+0

No necesita un analizador. Use el sistema de meta-programación JetBrains. Puede definir un idioma, un editor y un generador de código. No necesita un analizador en absoluto porque está editando el árbol de sintaxis directamente. Y no es un anuncio, este programa es FOSS. – naeron84

13

Lo he utilizado en los bucles internos de código de gráficos de un juego, donde desea cierto nivel de abstracción y modularidad, pero no pueden pagar el costo de sucursales o llamadas virtuales. En general, fue una mejor solución que una proliferación de funciones manuscritas para casos especiales.

1

Sí, lo tengo, sobre todo para hacer algunas cosas que se parecen a pato-mecanografiar cuando estaba envolviendo una API heredada en una interfaz C++ más moderna.

2

No, no lo he usado en el código de producción.

¿Por qué?

  1. Tenemos que apoyar 6+ plataformas con nativosplataforma compiladores. Es lo suficientemente duro como para usar STL en este entorno, y mucho menos las técnicas modernas de plantilla .
  2. Los desarrolladores ya no parecen seguir avanzando en C++. Usamos C++ cuando tenemos que hacerlo. Tenemos código heredado con diseños heredados. El nuevo código es hecho en otra cosa, por ejemplo, Java, Javascript, Flash.
+4

Lamentablemente este es el caso para nosotros también. – Motti

-4

Muchos programadores no usan plantillas mucho debido a la poca compatibilidad del compilador hasta hace poco. Sin embargo, aunque las plantillas han tenido muchos problemas en el pasado, los compiladores más nuevos tienen un soporte mucho mejor. Escribo código que tiene que funcionar con GCC en Mac y Linux, así como en Microsoft Visual C++, y es solo con GCC 4 y VC++ 2005 que estos compiladores han soportado muy bien el estándar.

La programación genérica a través de plantillas no es algo que necesite todo el tiempo, pero es definitivamente un código útil para tener en su caja de herramientas.

Las clases obvias de contenedores de ejemplo, pero las plantillas también son útiles para muchas otras cosas. Dos ejemplos de mi propio trabajo son:

  • punteros inteligentes (por ejemplo, contó-referencia, copia-en-escritura, etc.)
  • clases de apoyo de matemáticas tales como matrices, vectores, estrías, etc., que necesitan admite una variedad de tipos de datos y aún así es eficiente.
+2

La pregunta era sobre la programación de plantillas * meta *, no viejas plantillas simples que se usan mucho más comúnmente. – Motti

11

Las plantillas de metaprogramación y expresión de plantillas se están volviendo más populares en la comunidad científica como métodos de optimización que descargan parte del esfuerzo computacional en el compilador mientras mantienen cierta abstracción. El código resultante es más grande y menos legible, pero he usado estas técnicas para acelerar las bibliotecas de álgebra lineal y los métodos de cuadratura en las bibliotecas FEM.

Para lectura específica de la aplicación, Todd Veldhuizen es un gran nombre en esta área. Un libro popular es C++ and Object Oriented Numeric Computing for Scientists and Engineers por Daoqi Yang.

+2

Bueno, según la tabla de contenido, el libro de D. Yang contiene 7 páginas de material en expr. plantillas y metaprogramación de plantillas. El resto parece ser un libro de texto de programación general, con algunos ejemplos con métodos numéricos. No hay nada de malo en eso, por supuesto. – janneb

+0

Bastante justo. Para una referencia más completa, iría con el libro de Alexandrescu. Las referencias que cité eran específicas de las aplicaciones informáticas científicas, y pueden estar limitadas por mi conocimiento de la literatura. El libro [Barton/Nackman] (http://www.amazon.com/Scientific-Ingeniería-Introducción-Advanced-Tecnicas/dp/0201533936) también tiene una pequeña cantidad de material. –

6

La mayoría de los programadores que usan metaprogramación de plantillas lo usan indirectamente, a través de bibliotecas como boost. Probablemente ni siquiera saben lo que sucede entre bastidores, solo que hace que la sintaxis de ciertas operaciones sea mucho más fácil.

-1

No haga eso. La razón detrás de esto es la siguiente: por naturaleza de la metaprogramación de plantillas, si alguna parte de su lógica se realiza en tiempo de compilación, cada lógica de la que depende debe hacerse también en tiempo de compilación. Una vez que lo inicie, haga una parte de su lógica en tiempo de compilación, no hay devolución. La bola de nieve seguirá rodando y no hay forma de detenerla.

Por ejemplo, no puede iterar en los elementos de boost :: tuple <>, porque solo puede acceder a ellos en tiempo de compilación. Debe usar la metaprogramación de plantillas para lograr lo que hubiera sido sencillo y directo C++, y esto siempre ocurre cuando los usuarios de C++ no son lo suficientemente cuidadosos para no mover demasiadas cosas al tiempo de compilación. A veces es difícil ver cuándo un cierto uso de la lógica de compilación se volvería problemático, y a veces los programadores están ansiosos por probar y probar lo que han leído en Alexandrescu. En cualquier caso, esta es una muy mala idea en mi opinión.

+3

mpl :: for_each, boost.fusion, astutos contras especialización todos permiten puentear bastante fácil de compilar a tiempo de ejecución v – KitsuneYMG

+0

Bueno, no es tan difícil decidir un límite en el que desea pasar del tiempo de compilación a run- programación de tiempo Obviamente, no todos los códigos que usan una plantilla deben hacerse usando la programación de plantillas. Y, por lo general, no tiene tantos niveles entre el código genérico reutilizable y el código específico de la aplicación. – Phil1970

7

Uso la metaprogramación de plantillas todo el tiempo, pero en D, no en C++. El metalenguaje de plantillas de C++ se diseñó originalmente para la parametrización de tipo simple y se convirtió en un metalenguaje completo de Turing casi por accidente. Por lo tanto, es un tarpit de Turing que solo Andrei Alexandrescu, no simples mortales, puede usar.

El sublangauge de plantilla D, por otro lado, en realidad fue diseñado para la metaprogramación más allá de la parametrización de tipo simple. Andrei Alexandrescu seems to love it, pero otras personas pueden entender sus plantillas D. También es lo suficientemente potente como para que alguien haya escrito compile-time raytracer como prueba de concepto.

Supongo que el metaprograma más útil/no trivial que escribí en D era una plantilla de función que daba un tipo de estructura como parámetro de plantilla y una lista de nombres de encabezado de columna en un orden correspondiente a las declaraciones de variables en el struct como parámetro de tiempo de ejecución, se leerá en un archivo CSV y devolverá una matriz de estructuras, una para cada fila, con cada campo de estructura correspondiente a una columna. Todas las conversiones de tipo (cadena a flotante, int, etc.) se realizan automáticamente, en función de los tipos de campos de la plantilla.

Otra buena, que funciona principalmente, pero aún no maneja unos pocos casos correctamente, es una plantilla de función de copia profunda que maneja estructuras, clases y matrices correctamente.Utiliza solo reflexión/introspección en tiempo de compilación, de modo que puede funcionar con estructuras que, a diferencia de las clases completas, no tienen capacidades de reflexión/introspección en tiempo de ejecución en D porque se supone que son livianas.

+4

Es útil saber acerca de D, pero creo que la pregunta es sobre C++, no D. –

+1

Independientemente de esto, es útil saber qué tipo de cosas puedes lograr con la metaprogramación cuando no tienes que ser un gurú para entenderla :) – shambulator

+0

Parece que el enlace al ray-tracer estaba roto :( – Manu343726

20

que he encontrado, las políticas que se describen en Modern C++ Diseño, realmente útil en dos situaciones:

  1. Cuando estoy desarrollando un componente que espero que será reutilizada, pero de una manera ligeramente diferente. La sugerencia de Alexandrescu de usar una política para reflejar un diseño encaja muy bien aquí: me ayuda a hacer preguntas pasadas como: "Podría hacer esto con un hilo de fondo, pero ¿y si alguien más tarde quiere hacerlo en rodajas de tiempo?" De acuerdo, escribo mi clase para aceptar una ConcurrencyPolicy e implementar la que necesito en este momento. Entonces, al menos, sé que la persona que viene detrás de mí puede escribir y agregar una nueva política cuando la necesiten, sin tener que volver a trabajar totalmente en mi diseño. Advertencia: tengo que controlarme a veces o esto puede perder el control. ¡Recuerda el principio YAGNI!

  2. Cuando intento refactorizar varios bloques de código similares en uno. Por lo general, el código se copiará y se modificará ligeramente porque de lo contrario habría tenido demasiada lógica if/else, o porque los tipos involucrados eran demasiado diferentes. Descubrí que las políticas a menudo permiten una versión limpia para todos, donde la lógica tradicional o la herencia múltiple no lo hacen.

2

casi 8 meses después de pedir este fin he utilizado algunas TMP, utilizo un TypeList de interfaces con el fin de poner en práctica QueryInterface en una clase base.

+0

He querido haga lo mismo pero aún no lo haya hecho. Tenemos un gran código heredado envuelto como objetos COM para que se pueda acceder desde C#, pero nadie quiere trabajar en él o ampliarlo porque el texto estándar de COM es tan tedioso. Estoy seguro de que la metaprogramación de plantillas podría ser de gran ayuda, pero la simple programación de plantillas no es adecuada debido a la forma en que las reglas COM difieren de las de C++. –

2

Lo uso con boost :: statechart para máquinas de estado grandes.

+2

Deberías probar el siguiente impulso: msm - la máquina metaestatal (con suerte boost 2.0). Es un framework de máquina de estado de entrire basado en metaprogramación. Y es compatible con UML2. Lo usamos para algunos proyectos y es simplemente increíble. Bueno, a la baja, hay una larga compilación de tiempos ... – fmuecke

+1

Entonces, ¿cómo difiere en características de sc? Los únicos conceptos uml sc care se han derramado y se unen, que son fáciles de emular. – KitsuneYMG

+0

@fmuecke Solo una actualización. Miré a MSM ahora que está en impulso propiamente dicho. De ninguna manera es un reemplazo para statechart ya que parece carecer de cualquier tipo de jerarquía. MSM parece ser un diagrama de transición de estado donde statechart se parece más a un statechart de stateflow/simulink. – KitsuneYMG

4

Lo he usado bastante con el código DSP, especialmente las FFT, los búferes circulares de tamaño fijo, las transformaciones hadamard y similares. programación meta

9

plantilla es una técnica maravillosa y energía al escribir en C++ bibliotecas. Lo he usado algunas veces en soluciones personalizadas, pero generalmente una solución C++ de estilo antiguo menos elegante es más fácil de obtener a través de la revisión del código y más fácil de mantener para otros usuarios.

Sin embargo, cuando se escriben componentes/bibliotecas reutilizables saco mucho provecho de la meta programación de plantillas. No estoy hablando de algo tan grande como algunas de las cosas de Boost, solo componentes pequeños que se reutilizarán con frecuencia.

Utilicé TMP para un sistema singleton donde el usuario podía especificar qué tipo de singleton deseaban. La interfaz fue muy básica. Debajo estaba alimentado por TMP pesado.

template< typename T > 
T& singleton(); 

template< typename T > 
T& zombie_singleton(); 

template< typename T > 
T& phoenix_singleton(); 

Otro uso exitoso fue la simplificación de nuestra capa de IPC. Está construido con el estilo clásico de OO. Cada mensaje debe derivarse de una clase base abstracta y anular algunos métodos de serialización. Nada demasiado extremo, pero genera mucho código de placa de caldera.

Lanzamos algunos TMP y automatizamos la generación de todo el código para el caso simple de mensajes que contienen solo datos POD. Los mensajes TMP todavía utilizan el back-end OO pero reducen masivamente la cantidad de código de la placa de la caldera. El TMP también se usó para generar el mensaje vistor. Con el tiempo, todos nuestros mensajes migraron al método TMP. Era más fácil y menos código construir una estructura POD simple solo para pasar mensajes y agregar las pocas (quizás 3) líneas necesarias para que el TMP generara las clases de lo que era derivar un nuevo mensaje para enviar una clase regular a través del IPC marco de referencia.

4

Para aquellos familiarizados con Oracle Template Library (OTL), impulsar :: cualquiera y Loki biblioteca (el descrito en Modern C Diseño ++) aquí está la prueba de concepto de código TMP que le permite almacenar una fila de otl_stream en vector<boost::any> contenedor y acceder a los datos por número de columna. Y 'Sí', voy a incorporarlo en el código de producción.

#include <iostream> 
#include <vector> 
#include <string> 
#include <Loki/Typelist.h> 
#include <Loki/TypeTraits.h> 
#include <Loki/TypeManip.h> 
#include <boost/any.hpp> 
#define OTL_ORA10G_R2 
#define OTL_ORA_UTF8 
#include <otlv4.h> 

using namespace Loki; 

/* Auxiliary structs */ 
template <int T1, int T2> 
struct IsIntTemplateEqualsTo{ 
    static const int value = (T1 == T2); 
}; 

template <int T1> 
struct ZeroIntTemplateWorkaround{ 
    static const int value = (0 == T1? 1 : T1); 
}; 


/* Wrapper class for data row */ 
template <class TList> 
class T_DataRow; 


template <> 
class T_DataRow<NullType>{ 
protected: 
    std::vector<boost::any> _data; 
public: 
    void Populate(otl_stream&){}; 
}; 


/* Note the inheritance trick that enables to traverse Typelist */ 
template <class T, class U> 
class T_DataRow< Typelist<T, U> >:public T_DataRow<U>{ 
public: 
    void Populate(otl_stream& aInputStream){ 
     T value; 
     aInputStream >> value; 
     boost::any anyValue = value; 
     _data.push_back(anyValue); 

     T_DataRow<U>::Populate(aInputStream); 
    } 

    template <int TIdx> 
    /* return type */ 
    Select< 
     IsIntTemplateEqualsTo<TIdx, 0>::value, 
     typename T, 
     typename TL::TypeAt< 
      U, 
      ZeroIntTemplateWorkaround<TIdx>::value - 1 
     >::Result 
    >::Result 
    /* sig */ 
    GetValue(){ 
    /* body */ 
     return boost::any_cast< 
      Select< 
       IsIntTemplateEqualsTo<TIdx, 0>::value, 
       typename T, 
       typename TL::TypeAt< 
        U, 
        ZeroIntTemplateWorkaround<TIdx>::value - 1 
       >::Result 
      >::Result 
     >(_data[ TIdx ]); 
    } 
}; 


int main(int argc, char* argv[]) 
{ 
    db.rlogon("AMONRAWMS/[email protected]"); // connect to Oracle 
    std::cout<<"Connected to oracle DB"<<std::endl; 
    otl_stream o(1, "select * from blockstatuslist", db); 

    T_DataRow< TYPELIST_3(int, int, std::string)> c; 
    c.Populate(o); 
    typedef enum{ rcnum, id, name } e_fields; 
    /* After declaring enum you can actually acess columns by name */ 
    std::cout << c.GetValue<rcnum>() << std::endl; 
    std::cout << c.GetValue<id>() << std::endl; 
    std::cout << c.GetValue<name>() << std::endl; 
    return 0; 
}; 

Para aquellos que no estén familiarizados con las bibliotecas mencionadas.

El problema con containter otl_stream de comunicación cara a cara es que se puede acceder a las columnas de datos sólo en orden secuencial al declarar variables de tipo adecuado y la aplicación de la operator >> a otl_stream objeto de la siguiente manera:

otl_stream o(1, "select * from blockstatuslist", db); 
int rcnum; 
int id; 
std::string name; 
o >> rcnum >> id >> name; 

No siempre es conveniente . La solución consiste en escribir una clase contenedora y completarla con datos de otl_stream. El deseo es ser capaz de declarar la lista de tipos de columna y, a continuación:

  • tomar el tipo T de la columna
  • declaran variable de ese tipo
  • aplican olt_stream::operator >>(T&)
  • tienda el resultado (en el vector de impulso :: hubiera)
  • tomar el tipo de la columna siguiente y repetir hasta las todas las columnas se procesan

se puede hacer al Esto con la ayuda de la estructura Typelist de Loki, la especialización de plantillas y la herencia.

Con la ayuda de las construcciones de biblioteca de Loki, también puede generar un montón de funciones de GetValue que devuelven valores del tipo apropiado deduciéndolo del número de columna (en realidad, número de tipo en Typelist).

Cuestiones relacionadas