2012-05-08 12 views
11

¿Hay una herramienta para autogenerar el operador ostream < < para una estructura o clase?Generador de flujo automático para estructura/clase

de entrada (tomado de One Debug-Print function to rule them all):

typedef struct ReqCntrlT /* Request control record */ 
{ 
    int    connectionID; 
    int    dbApplID; 
    char   appDescr[MAX_APPDSCR]; 
    int    reqID; 
    int   resubmitFlag; 
    unsigned int resubmitNo; 
    char   VCIver[MAX_VCIVER]; 
    int    loginID; 
} ReqCntrlT; 

Salida:

std::ostream& operator <<(std::ostream& os, const ReqCntrlT& r) 
{ 
    os << "reqControl { " 
     << "\n\tconnectionID: " << r.connectionID 
     << "\n\tdbApplID: " << r.dbApplID 
     << "\n\tappDescr: " << r.appDescr 
     << "\n\treqID: " << r.reqID 
     << "\n\tresubmitFlag: " << r.resubmitFlag 
     << "\n\tresubmitNo: " << r.resubmitNo 
     << "\n\tVCIver: " << r.VCIver 
     << "\n\tloginID: " << r.loginID 
     << "\n}"; 
    return os; 
} 

Cualquier herramienta estaría bien, se prefieren las secuencias de comandos de Python/Ruby.

+0

Se pueden probar las herramientas automáticas de serialización de clases, creo que boost tiene una. ¿Importa qué formato la salida del operador << es para usted? – johnathon

+0

JSON, XML, cualquier cosa que un humano pueda leer está bien para mí. –

+0

* podría * probablemente generarlo con boost PP & MPL – David

Respuesta

0

Entendí su pregunta de dos maneras.

Si desea generar un informe de estado automático de su programa, le sugiero que compruebe Boost.Serialization. Sin embargo, no generará el código en tiempo de compilación como primer paso o como inspiración. El siguiente código le ayudará a generar archivos xml o txt que puede leer después.

typedef struct ReqCntrlT /* Request control record */ 
{ 
    int    connectionID; 
    int    dbApplID; 
    char   appDescr[MAX_APPDSCR]; 
    int    reqID; 
    int   resubmitFlag; 
    unsigned int resubmitNo; 
    char   VCIver[MAX_VCIVER]; 
    int    loginID; 

    template<class Archive> 
    void serialize(Archive & ar, const unsigned int version) 
    { 
     ar & connectionID; 
     ar & reqID; 
     ... 
    } 
} ReqCntrlT; 

Ver el tutorial para más detalles: http://www.boost.org/doc/libs/1_49_0/libs/serialization/doc/index.html

Si sólo está tratando de "escribir" el código sólo dando nombre de parámetros. Luego debería echarle un vistazo a las expresiones regulares en python o perl, por ejemplo. El valor predeterminado principal de esta solución es que está "fuera de línea" de su estructura, es decir, tiene que ejecutarla cada vez que cambia algo.

Benoit.

+0

el principal problema es que no quiero escribir el método de serialización, ya que es un trabajo aburrido y tedioso. Como todas las estructuras que deseo imprimir son simples, quiero autogenerar el código serialize() o el operador <<(). –

1

Para lograr eso, la única forma es usar una herramienta externa que ejecute en sus archivos de origen.

En primer lugar, podría usar un c/c++ analysing tool, y usarlo para recuperar el árbol de análisis sintáctico de su código fuente. Luego, una vez que tienes el árbol de análisis sintáctico, solo tienes que buscar estructuras. Para cada estructura, ahora puede generar una sobrecarga operator<< que serialice los campos de la estructura. También puede generar operador deserialize.

Pero depende de la cantidad de estructuras que tenga: para una docena, es mejor escribir los operadores manualmente, pero si tiene varios cientos de estructuras, puede escribir el generador de operadores (de) serializar.

3

Lo que se necesita para esto es una herramienta que puede analizar con precisión C++, enumerar las diversas clases/estructuras, determinar y generar sus "serializaciones" por clase/estructura, y luego estacionar el código generado en el " lugar correcto "(presumbablemente el mismo ámbito en el que se encontró la estructura). Necesita un preprocesador completo para manejar la expansión de directivas en código real.

Nuestra DMS Software Reengineering Toolkit con su C++11 front end podría hacer esto. DMS permite la construcción de herramientas personalizadas al proporcionar análisis genérico/construcción AST, construcción de tabla de símbolos, análisis de flujo y personalizado, transformación y capacidad de regeneración de código fuente. El frente de C++ le permite a DMS analizar sintácticamente C++ y crear tablas de símbolos precisas, así como imprimir bonitos ASTs modificados o nuevos en forma de fuente compilable. DMS y su interfaz C++ se han utilizado para llevar a cabo transformaciones masivas en el código C++.

Tienes que explicar a DMS lo que quieres hacer; parece sencillo enumerar entradas de tablas de símbolos, preguntar si struct/class type declarations, determinar el alcance de la declaración (registrada en la entrada de la tabla de símbolos), construir un AST componiendo patrones de sintaxis de superficie y luego aplicar una transformación para insertar el AST construido.

Los patrones de sintaxis superficie básicas necesarias son aquellas para las ranuras y para el cuerpo de la función:

pattern ostream_on_slot(i:IDENTIFIER):expression = 
    " << "\n\t" << \tostring\(\i\) << r.\i "; -- tostring is a function that generates "<name>" 

pattern ostream_on_struct(i:IDENTIFIER,ostream_on_slots:expression): declaration = 
    " std::ostream& operator <<(std::ostream& os, const \i& r) 
    { os << \tostring\(\i\) << " { " << \ostream_on_slots << "\n}"; 
     return os; 
    } 

Uno tiene que componer los árboles individuales para ostream_on_slot:

pattern compound_ostream(e1:expression, e2:expression): expression 
    = " \e1 << \e2 "; 

Con estos patrones es sencillo para enumerar las ranuras de struct, construir el ostream para el cuerpo e insertarlo en la función general para una estructura.

2

Hay dos formas principales de hacerlo:

  • utilizando una herramienta de análisis externo (como un script en Python enganchados arriba en encuadernaciones Clang)
  • utilizando técnicas metaprogramación

.. y por supuesto, pueden mezclarse.

No tengo suficiente conocimiento sobre las vinculaciones de Clang Python para responder a su uso, así que me concentraré en la metapogetación.


Básicamente, lo que está solicitando requiere introspección. C++ no es compatible con la introspección completa, sin embargo, al utilizar trucos de metaprogramación (y la coincidencia de plantillas) puede admitir un subconjunto limitado de técnicas de introspección en tiempo de compilación, que es suficiente para nuestro propósito.

Para mezclar fácilmente la metaprogramación y la operación en tiempo de ejecución, es más fácil poner en funcionamiento una biblioteca: Boost.Fusion.

Si ajusta su estructura de modo que sus atributos se describan en términos de una secuencia de Boost.Fusion, entonces puede aplicar un montón de algoritmo en la secuencia automáticamente. Aquí, un associate sequence es lo mejor.

Porque estamos hablando metaprogramming, the map asocia un tipo a un valor mecanografiado.

Puede iterar sobre esa secuencia usando for_each.


voy a pasar por alto los detalles, simplemente porque ha pasado un tiempo y no me acuerdo la sintaxis involucrado, pero básicamente la idea es llegar a:

// Can be created using Boost.Preprocessor, but makes array types a tad difficult 
DECL_ATTRIBUTES((connectionId, int) 
       (dbApplId, int) 
       (appDescr, AppDescrType) 
       ... 
       ); 

que es sintáctica el azúcar que se declara el Mapa Fusion y sus etiquetas asociadas:

struct connectionIdTag {}; 
struct dbApplIdTag {}; 

typedef boost::fusion::map< 
    std::pair<connectionIdTag, int>, 
    std::pair<dbApplIdTag, int>, 
    ... 
    > AttributesType; 
AttributesType _attributes; 

Entonces, cualquier operación que necesitan ser aplicadas en los atributos puede ser construido simplemente con:

// 1. A predicate: 
struct Predicate { 
    template <typename T, typename U> 
    void operator()(std::pair<T, U> const&) const { ... } 
}; 

// 2. The for_each function 
for_each(_attributes, Predicate()); 
+0

@MatthieM: Di preferiría una solución externa, en la que pegué la estructura y presioné generar y luego volver a copiar el código de operador generado. La mayoría de las estructuras que quiero exportar están en bibliotecas externas. –

+0

@ChristopherOezbek: No conozco ninguna herramienta "integrada" para esto (creo que demasiado especializada), pero si no quiere pagar DMS (la respuesta de Ira), le sugiero que se interese en el proyecto Clang. Con libclang puede analizar archivos C++ y luego operar en AST. Puede hacer preguntas específicas en la lista de correo de desarrollo de clang. –

+0

@MatthieM: ¡Gracias! ¡Boost :: Fusion parece interesado en otro proyecto mío! –

Cuestiones relacionadas