c++ reflection templates sfinae

¿Cómo puedo agregar reflexión a una aplicación de C++?



reflection templates (30)

RTTI no existe para C ++.

Esto es simplemente incorrecto. En realidad, el mismo término "RTTI" fue acuñado por el estándar C ++. Por otro lado, RTTI no va muy lejos en la implementación de la reflexión.

Me gustaría poder realizar una introspección de una clase de C ++ por su nombre, contenido (es decir, miembros y sus tipos), etc. Estoy hablando de C ++ nativo aquí, no de C ++ administrado, que tiene reflejo. Me doy cuenta de que C ++ proporciona información limitada usando RTTI. ¿Qué bibliotecas adicionales (u otras técnicas) podrían suministrar esta información?


¿Qué estás tratando de hacer con la reflexión?
Puede utilizar los rasgos de tipo Boost y las bibliotecas de tipos de archivos como una forma limitada de reflexión en tiempo de compilación. Es decir, puede inspeccionar y modificar las propiedades básicas de un tipo que se pasa a una plantilla.


Aunque la reflexión no se admite de forma inmediata en c ++, no es demasiado difícil de implementar. Me he encontrado con este gran artículo: http://replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

El artículo explica con gran detalle cómo se puede implementar un sistema de reflexión bastante simple y rudimentario. no es la solución más saludable, y quedan algunos aspectos por resolver, pero para mis necesidades fue suficiente.

La conclusión es que la reflexión puede ser rentable si se realiza correctamente, y es completamente factible en c ++.


Creo que podría encontrar interesante el artículo "Uso de plantillas para la reflexión en C ++" de Dominic Filion. Está en la sección 1.4 de Game Programming Gems 5 . Desafortunadamente no tengo mi copia conmigo, pero búsquela porque creo que explica lo que está pidiendo.


Cuando quise reflexionar en C ++, leí este artículo y mejoré lo que vi allí. Lo siento, no se puede tener. No soy dueño del resultado ... pero ciertamente puedes obtener lo que tenía y seguir desde allí.

Actualmente estoy investigando, cuando me da la gana, métodos para usar inherit_linearly para hacer que la definición de tipos reflectantes sea mucho más fácil. He llegado bastante lejos en realidad, pero todavía tengo mucho camino por recorrer. Es muy probable que los cambios en C ++ 0x sean de mucha ayuda en esta área.


Debe observar lo que está tratando de hacer, y si RTTI satisface sus requisitos. He implementado mi propia pseudo-reflexión para algunos propósitos muy específicos. Por ejemplo, una vez quise poder configurar de manera flexible lo que generaría una simulación. Se requería agregar algún código repetitivo a las clases que se generarían:

namespace { static bool b2 = Filter::Filterable<const MyObj>::Register("MyObject"); } bool MyObj::BuildMap() { Filterable<const OutputDisease>::AddAccess("time", &MyObj::time); Filterable<const OutputDisease>::AddAccess("person", &MyObj::id); return true; }

La primera llamada agrega este objeto al sistema de filtrado, que llama al método BuildMap() para averiguar qué métodos están disponibles.

Luego, en el archivo de configuración, puedes hacer algo como esto:

FILTER-OUTPUT-OBJECT MyObject FILTER-OUTPUT-FILENAME file.txt FILTER-CLAUSE-1 person == 1773 FILTER-CLAUSE-2 time > 2000

A través de un poco de magia de plantillas que incluye un boost , esto se traduce en una serie de llamadas de métodos en tiempo de ejecución (cuando se lee el archivo de configuración), por lo que es bastante eficiente. No recomendaría hacer esto a menos que realmente lo necesites, pero cuando lo hagas, puedes hacer algunas cosas realmente geniales.


EDITAR: Enlace roto actualizado a partir del 7 de febrero de 2017.

Creo que nadie mencionó esto:

En el CERN utilizan un sistema de reflexión completo para C ++:

Reflejo del CERN . Parece funcionar muy bien.


Echa un vistazo a Classdesc http://classdesc.sf.net . Proporciona reflexión en forma de "descriptores" de clase, funciona con cualquier compilador estándar de C ++ (sí, se sabe que funciona con Visual Studio y GCC), y no requiere anotación de código fuente (aunque existen algunos pragmas para manejar situaciones difíciles ). Ha estado en desarrollo durante más de una década y se ha utilizado en varios proyectos a escala industrial.


Esta pregunta es un poco vieja ahora (no sé por qué sigo respondiendo preguntas viejas hoy) pero estaba pensando en BOOST_FUSION_ADAPT_STRUCT que introduce la reflexión en tiempo de compilación.

Depende de usted asignar esto a la reflexión en tiempo de ejecución, por supuesto, y no será demasiado fácil, pero es posible en esta dirección, mientras que no sería a la inversa :)

Realmente creo que una macro para encapsular BOOST_FUSION_ADAPT_STRUCT podría generar los métodos necesarios para obtener el comportamiento en tiempo de ejecución.


Hay dos tipos de reflection nadando alrededor.

  1. Inspección por iteración sobre miembros de un tipo, enumerando sus métodos y así sucesivamente.

    Esto no es posible con C ++.
  2. Inspección al verificar si un tipo de clase (clase, estructura, unión) tiene un método o un tipo anidado, se deriva de otro tipo en particular.

    Este tipo de cosas es posible con C ++ utilizando template-tricks . Use boost::type_traits para muchas cosas (como verificar si un tipo es integral). Para verificar la existencia de una función miembro, use ¿Es posible escribir una plantilla para verificar la existencia de una función? . Para verificar si existe un determinado tipo anidado, use SFINAE simple.

Si está buscando formas de lograr 1), como ver cuántos métodos tiene una clase o cómo obtener la representación de cadena de un ID de clase, me temo que no hay una forma estándar de C ++ de hacerlo. Tienes que usar cualquiera

  • Un Meta Compiler como el Qt Meta Object Compiler que traduce su código agregando metainformaciones adicionales.
  • Un marco consistente en macros que le permiten agregar las metainformaciones requeridas. Tendría que decirle al marco todos los métodos, los nombres de clase, las clases base y todo lo que necesita.

C ++ se hace con la velocidad en mente. Si desea una inspección de alto nivel, como C # o Java, me temo que tengo que decirle que no hay manera sin un poco de esfuerzo.


Hay otra biblioteca nueva para la reflexión en C ++, llamada RTTR (Run Time Type Reflection, vea también github ).

La interfaz es similar a la reflexión en C # y funciona sin ningún RTTI.


Hice algo como lo que buscabas una vez, y si bien es posible obtener cierto nivel de reflexión y acceso a funciones de nivel superior, el dolor de cabeza por el mantenimiento podría no valer la pena. Mi sistema se usó para mantener las clases de UI completamente separadas de la lógica de negocios a través de la delegación similar al concepto de Objective-C de pasar y reenviar mensajes. La forma de hacerlo es crear una clase base que sea capaz de asignar símbolos (utilicé un conjunto de cadenas, pero podría hacerlo con enumeraciones si prefiere la velocidad y el manejo de errores en tiempo de compilación sobre la flexibilidad total) para los punteros de función (en realidad no Punteros de función pura, pero algo similar a lo que Boost tiene con Boost.Function (al que no tenía acceso en ese momento). Puede hacer lo mismo para sus variables miembro siempre que tenga alguna clase base común capaz de representar cualquier valor. El sistema completo fue un timo sin contratiempos de la codificación y delegación de valor-clave, con algunos efectos secundarios que tal vez valieron la cantidad de tiempo necesaria para que cada clase que usó el sistema combinara todos sus métodos y miembros con llamadas legales. : 1) Cualquier clase podría llamar a cualquier método en cualquier otra clase sin tener que incluir encabezados o escribir clases base falsas, por lo que la interfaz podría estar predefinida para el compilador; y 2) Los captadores y definidores de las variables miembro eran fáciles de hacer seguros para los subprocesos porque el cambio o el acceso a sus valores siempre se hacía a través de 2 métodos en la clase base de todos los objetos.

También llevó a la posibilidad de hacer cosas realmente extrañas que de otra forma no serían fáciles en C ++. Por ejemplo, podría crear un objeto Array que contuviera elementos arbitrarios de cualquier tipo, incluyéndose a sí mismo, y crear nuevos arreglos dinámicamente pasando un mensaje a todos los elementos del arreglo y recolectando los valores de retorno (similar al mapa en Lisp). Otra fue la implementación de la observación de clave-valor, por medio de la cual pude configurar la interfaz de usuario para responder de inmediato a los cambios en los miembros de las clases de back-end en lugar de sondear constantemente los datos o volver a dibujar innecesariamente la pantalla.

Quizás más interesante para usted es el hecho de que también puede volcar todos los métodos y miembros definidos para una clase, y en forma de cadena no menos.

Desventajas del sistema que podrían disuadirte de molestarte: agregar todos los mensajes y valores-clave es extremadamente tedioso; es más lento que sin ningún reflejo; crecerá hasta el odio al ver boost::static_pointer_cast y boost::dynamic_pointer_cast todo su código base con una pasión violenta; Las limitaciones del sistema fuertemente tipado todavía están ahí, en realidad solo las escondes un poco para que no sea tan obvio. Los errores tipográficos en tus cuerdas tampoco son una sorpresa divertida o fácil de descubrir.

En cuanto a cómo implementar algo como esto: simplemente use punteros compartidos y débiles para alguna base común (el mío se denominó muy imaginativamente "Objeto") y derive para todos los tipos que quiera usar. Recomendaría instalar Boost.Function en lugar de hacerlo como lo hice yo, que fue con una mierda personalizada y un montón de macros feas para envolver las llamadas de puntero de función. Como todo está mapeado, inspeccionar objetos es solo una cuestión de iterar a través de todas las claves. Ya que mis clases fueron esencialmente lo más cerca posible de una copia directa de Cocoa usando solo C ++, si quieres algo así, entonces sugeriría usar la documentación de Cocoa como modelo.


La información existe, pero no en el formato que necesita, y solo si exporta sus clases. Esto funciona en Windows, no sé de otras plataformas. Usando los especificadores de la clase de almacenamiento como en, por ejemplo:

class __declspec(export) MyClass { public: void Foo(float x); }

Esto hace que el compilador construya los datos de definición de clase en la DLL / Exe. Pero no está en un formato que puedas usar fácilmente para la reflexión.

En mi empresa construimos una biblioteca que interpreta estos metadatos y le permite reflejar una clase sin insertar macros adicionales, etc. en la clase en sí. Permite llamar a las funciones de la siguiente manera:

MyClass *instance_ptr=new MyClass; GetClass("MyClass")->GetFunction("Foo")->Invoke(instance_ptr,1.331);

Esto efectivamente hace:

instance_ptr->Foo(1.331);

La función Invocar (this_pointer, ...) tiene argumentos variables. Obviamente, al llamar a una función de esta manera usted está evadiendo cosas como const-safety, etc., por lo que estos aspectos se implementan como verificaciones de tiempo de ejecución.

Estoy seguro de que la sintaxis podría mejorarse, y hasta ahora solo funciona en Win32 y Win64. Hemos encontrado que es realmente útil para tener interfaces GUI automáticas para las clases, crear propiedades en C ++, transmitir desde y hacia XML, etc., y no es necesario derivar de una clase base específica. Si hay suficiente demanda, tal vez podríamos ponerlo en forma para su lanzamiento.


La reflexión en C ++ es muy útil, en caso de que necesite ejecutar algún método para cada miembro (por ejemplo: serialización, hashing, comparación). Vine con una solución genérica, con una sintaxis muy simple:

struct S1 { ENUMERATE_MEMBERS(str,i); std::string str; int i; }; struct S2 { ENUMERATE_MEMBERS(s1,i2); S1 s1; int i2; };

Donde ENUMERATE_MEMBERS es una macro, que se describe más adelante (ACTUALIZAR):

Supongamos que hemos definido la función de serialización para int y std :: string como esta:

void EnumerateWith(BinaryWriter & writer, int val) { //store integer writer.WriteBuffer(&val, sizeof(int)); } void EnumerateWith(BinaryWriter & writer, std::string val) { //store string writer.WriteBuffer(val.c_str(), val.size()); }

Y tenemos una función genérica cerca de la "macro secreta";)

template<typename TWriter, typename T> auto EnumerateWith(TWriter && writer, T && val) -> is_enumerable_t<T> { val.EnumerateWith(write); //method generated by ENUMERATE_MEMBERS macro }

Ahora puedes escribir

S1 s1; S2 s2; //.... BinaryWriter writer("serialized.bin"); EnumerateWith(writer, s1); //this will call EnumerateWith for all members of S1 EnumerateWith(writer, s2); //this will call EnumerateWith for all members of S2 and S2::s1 (recursively)

Por lo tanto, al tener la macro ENUMERATE_MEMBERS en la definición de estructura, puede crear serialización, comparación, hashing y otros elementos sin tocar el tipo original, el único requisito es implementar el método "EnumerateWith" para cada tipo, que no es enumerable, por enumerador (como BinaryWriter) . Por lo general, tendrá que implementar 10-20 tipos "simples" para admitir cualquier tipo en su proyecto.

Esta macro debe tener cero sobrecarga para la creación / destrucción de estructuras en tiempo de ejecución, y el código de T.EnumerateWith () debe generarse a pedido, lo que puede lograrse al hacer que sea una función de plantilla en línea, por lo que la única sobrecarga en Toda la historia es agregar ENUMERATE_MEMBERS (m1, m2, m3 ...) a cada estructura, mientras que la implementación de un método específico por tipo de miembro es una necesidad en cualquier solución, por lo que no lo asumo como una sobrecarga.

ACTUALIZACIÓN: Hay una implementación muy simple de la macro ENUMERATE_MEMBERS (sin embargo, podría extenderse un poco para admitir la herencia de una estructura enumerable)

#define ENUMERATE_MEMBERS(...) / template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) const { EnumerateWithHelper(enumerator, __VA_ARGS__ ); }/ template<typename TEnumerator> inline void EnumerateWith(TEnumerator & enumerator) { EnumerateWithHelper(enumerator, __VA_ARGS__); } // EnumerateWithHelper template<typename TEnumerator, typename ...T> inline void EnumerateWithHelper(TEnumerator & enumerator, T &...v) { int x[] = { (EnumerateWith(enumerator, v), 1)... }; } // Generic EnumerateWith template<typename TEnumerator, typename T> auto EnumerateWith(TEnumerator & enumerator, T & val) -> std::void_t<decltype(val.EnumerateWith(enumerator))> { val.EnumerateWith(enumerator); }

Y no necesita ninguna biblioteca de terceros para estas 15 líneas de código;)


La reflexión es esencialmente sobre lo que el compilador decidió dejar como huellas en el código que el código de tiempo de ejecución puede consultar. C ++ es famoso por no pagar por lo que no usas; Debido a que la mayoría de las personas no usan / desean la reflexión, el compilador de C ++ evita el costo al no registrar nada .

Entonces, C ++ no proporciona reflexión, y no es fácil "simularlo" como regla general, como lo han señalado otras respuestas.

Bajo "otras técnicas", si no tiene un lenguaje con reflexión, obtenga una herramienta que pueda extraer la información que desea en tiempo de compilación.

Nuestro kit de herramientas de reingeniería de software DMS es una tecnología de compilación generalizada parametrizada por definiciones de lenguaje explícitas. Tiene definiciones de lenguaje para C, C ++, Java, COBOL, PHP, ...

Para las versiones C, C ++, Java y COBOL, proporciona acceso completo a los árboles de análisis e información de la tabla de símbolos. La información de la tabla de símbolos incluye el tipo de datos que probablemente desee obtener de "reflexión". Si su objetivo es enumerar algún conjunto de campos o métodos y hacer algo con ellos, el DMS se puede usar para transformar el código de acuerdo con lo que encuentre en las tablas de símbolos de manera arbitraria.


Las dos soluciones de reflexión que conozco de mis días en C ++ son:

1) Use RTTI, que le proporcionará un programa de arranque para que construya su comportamiento similar a la reflexión, si puede hacer que todas sus clases se deriven de una clase base ''objeto''. Esa clase podría proporcionar algunos métodos como GetMethod, GetBaseClass, etc. En cuanto a cómo funcionan esos métodos, deberá agregar algunas macros manualmente para decorar sus tipos, que detrás de la escena crean metadatos en el tipo para proporcionar respuestas a GetMethods, etc.

2) Otra opción, si tiene acceso a los objetos del compilador es utilizar el SDK de DIA . Si recuerdo correctamente, esto te permite abrir pdbs, que deberían contener metadatos para tus tipos de C ++. Puede ser suficiente para hacer lo que necesita. Esta página muestra cómo puede obtener todos los tipos básicos de una clase, por ejemplo.

¡Estas dos soluciones son un poco feas! No hay nada como un poco de C ++ para que aprecies los lujos de C #.

Buena suerte.


Lo que debe hacer es que el preprocesador genere datos de reflexión sobre los campos. Estos datos se pueden almacenar como clases anidadas.

Primero, para que sea más fácil y limpio escribirlo en el preprocesador, usaremos una expresión escrita. Una expresión escrita es solo una expresión que pone el tipo entre paréntesis. Entonces, en lugar de escribir int x , escribirás (int) x . Aquí hay algunas macros útiles para ayudar con expresiones escritas:

#define REM(...) __VA_ARGS__ #define EAT(...) // Retrieve the type #define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,) #define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__) #define DETAIL_TYPEOF_HEAD(x, ...) REM x #define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__), // Strip off the type #define STRIP(x) EAT x // Show the type without parenthesis #define PAIR(x) REM x

A continuación, definimos una macro REFLECTABLE para generar los datos sobre cada campo (más el campo en sí). Esta macro se llamará así:

REFLECTABLE ( (const char *) name, (int) age )

Entonces, usando Boost.PP , Boost.PP sobre cada argumento y generamos los datos de esta manera:

// A helper metafunction for adding const to a type template<class M, class T> struct make_const { typedef T type; }; template<class M, class T> struct make_const<const M, T> { typedef typename boost::add_const<T>::type type; }; #define REFLECTABLE(...) / static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); / friend struct reflector; / template<int N, class Self> / struct field_data {}; / BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) #define REFLECT_EACH(r, data, i, x) / PAIR(x); / template<class Self> / struct field_data<i, Self> / { / Self & self; / field_data(Self & self) : self(self) {} / / typename make_const<Self, TYPEOF(x)>::type & get() / { / return self.STRIP(x); / }/ typename boost::add_const<TYPEOF(x)>::type & get() const / { / return self.STRIP(x); / }/ const char * name() const / {/ return BOOST_PP_STRINGIZE(STRIP(x)); / } / }; /

Lo que esto hace es generar una constante fields_n que es el número de campos reflectantes en la clase. Luego se especializa el field_data para cada campo. También es amigo de la clase reflector , esto es para que pueda acceder a los campos incluso cuando son privados:

struct reflector { //Get field_data at index N template<int N, class T> static typename T::template field_data<N, T> get_field_data(T& x) { return typename T::template field_data<N, T>(x); } // Get the number of fields template<class T> struct fields { static const int n = T::fields_n; }; };

Ahora para iterar sobre los campos usamos el patrón de visitante. Creamos un rango de MPL de 0 a la cantidad de campos y accedemos a los datos de campo en ese índice. Luego pasa los datos de campo al visitante proporcionado por el usuario:

struct field_visitor { template<class C, class Visitor, class I> void operator()(C& c, Visitor v, I) { v(reflector::get_field_data<I::value>(c)); } }; template<class C, class Visitor> void visit_each(C & c, Visitor v) { typedef boost::mpl::range_c<int,0,reflector::fields<C>::n> range; boost::mpl::for_each<range>(boost::bind<void>(field_visitor(), boost::ref(c), v, _1)); }

Ahora para el momento de la verdad lo ponemos todo junto. Aquí es cómo podemos definir una clase de Person que es reflejable:

struct Person { Person(const char *name, int age) : name(name), age(age) { } private: REFLECTABLE ( (const char *) name, (int) age ) };

Aquí hay una función de print_fields generalizada que utiliza los datos de reflexión para iterar sobre los campos:

struct print_visitor { template<class FieldData> void operator()(FieldData f) { std::cout << f.name() << "=" << f.get() << std::endl; } }; template<class T> void print_fields(T & x) { visit_each(x, print_visitor()); }

Un ejemplo del uso de los print_fields con la clase Person print_fields :

int main() { Person p("Tom", 82); print_fields(p); return 0; }

Qué salidas:

name=Tom age=82

Y listo, acabamos de implementar la reflexión en C ++, en menos de 100 líneas de código.


Me gustaría anunciar la existencia del kit de herramientas automático de introspección / reflexión "IDK". Utiliza un meta-compilador como Qt y agrega información meta directamente a los archivos de objetos. Se dice que es fácil de usar. No hay dependencias externas. Incluso le permite reflejar automáticamente std :: string y luego usarlo en scripts. Por favor mire IDK


Parece que C ++ todavía no tiene esta característica. Y C++11 pospuso la reflexión también ((

Busca algunas macros o hazlas propias. Qt también puede ayudar con la reflexión (si se puede usar).


Puede encontrar otra biblioteca aquí: http://www.garret.ru/cppreflection/docs/reflect.html Admite 2 formas: obtener información de tipo de la información de depuración y dejar que el programador proporcione esta información.

También me interesó la reflexión para mi proyecto y encontré esta biblioteca, todavía no la he probado, pero probé otras herramientas de este tipo y me gusta cómo funcionan :-)


Reflexión no es compatible con C ++ fuera de la caja. Esto es triste porque hace que las pruebas defensivas sean un dolor.

Hay varios enfoques para hacer la reflexión:

  1. Utilice la información de depuración (no portátil).
  2. Espolvoree su código con macro / templates o algún otro enfoque de fuente (se ve feo)
  3. Modifique un compilador como clang / gcc para producir una base de datos.
  4. Utilice el enfoque Qt moc
  5. Refuerza Refuerza
  6. Reflexión precisa y plana

El primer enlace parece el más prometedor (usa mod''s para sonar), el segundo discute una serie de técnicas, el tercero es un enfoque diferente usando gcc:

  1. http://www.donw.org/rfl/

  2. https://bitbucket.org/dwilliamson/clreflect

  3. https://root.cern.ch/how/how-use-reflex

Ahora hay un grupo de trabajo para la reflexión de C ++. Vea las noticias de C ++ 14 @ CERN:

Edición 13/08/17: Desde la publicación original, ha habido una serie de avances potenciales en la reflexión. Lo siguiente proporciona más detalles y una discusión sobre las diversas técnicas y el estado:

  1. Reflexión estática en pocas palabras
  2. Reflexión estática
  3. Un diseño para la reflexión estática.

Sin embargo, no parece prometedor en un enfoque de reflexiones estandarizado en C ++ en un futuro próximo, a menos que la comunidad tenga mucho más interés en apoyar la reflexión en C ++.

Lo siguiente detalla el estado actual basado en los comentarios de la última reunión de estándares de C ++:

Editar 13/12/2017

La reflexión parece estar moviéndose hacia C ++ 20 o más probablemente un TSR. Sin embargo, el movimiento es lento.

Editar 15/09/2018

Se ha enviado un borrador del TS a los organismos nacionales para su votación.

El texto se puede encontrar aquí: https://github.com/cplusplus/reflection-ts



Y me encantaría un pony, pero los ponis no son gratis. :-pag

http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI es lo que vas a obtener. La reflexión en la que está pensando (metadatos totalmente descriptivos disponibles en tiempo de ejecución) simplemente no existe para C ++ de forma predeterminada.


Yo recomendaría usar Qt .

Hay una licencia de código abierto, así como una licencia comercial.


Ponder es una biblioteca de reflexión de C ++, en respuesta a esta pregunta. Consideré las opciones y decidí hacer las mías ya que no podía encontrar una que cumpliera todos los requisitos.

Aunque hay grandes respuestas a esta pregunta, no quiero usar toneladas de macros ni confiar en Boost. Boost es una gran biblioteca, pero hay muchos proyectos pequeños a medida de C ++ 0x que son más simples y tienen tiempos de compilación más rápidos. También hay ventajas de poder decorar una clase externamente, como envolver una biblioteca de C ++ que (¿aún no?) Es compatible con C ++ 11. Es la bifurcación de CAMP, que usa C ++ 11, que ya no requiere Boost .


Actualizado el 24.2.2017

Anteriormente, he analizado la compatibilidad con el uso de # define y, como se recomienda en algunos de los artículos de la red. He encontrado que define en visual C ++ no funcionaba de manera idéntica en comparación con la definición que se usaba en gcc (por ejemplo, en internet, esto se suele denominar "MSVC walkaround"). Además de no poder entender fácilmente lo que está sucediendo detrás de la maquinaria de expansión / macro expansión, es bastante difícil depurar cada expansión de macro.

Hay un par de maneras de sortear las complejidades de la expansión de definición; un enfoque es activar el indicador del compilador "/ P" (solo antes del proceso de archivo); después, puede comparar cómo se abre su definición. (Anteriormente, también he utilizado activamente el operador stringfy (#))

He recopilado todas las definiciones útiles de varios foros, las he consultado y comentado lo que sucede detrás de la maquinaria. Ahora puede encontrar el archivo completo del encabezado aquí:

https://sourceforge.net/p/testcppreflect/code/HEAD/tree/MacroHelpers.h

Pensé que era bastante trivial usar estas macros para habilitar la reflexión de C ++, pero se necesita un poco más de magia para hacer la reflexión.

He recolectado un código de muestra de trabajo, y lo puse como proyecto de sourceforge, se puede descargar aquí:

https://sourceforge.net/p/testcppreflect/code/HEAD/tree/

El código de demostración se ve así:

#include "CppReflect.h" using namespace std; class Person { public: REFLECTABLE( Person, (CString) name, (int) age ) }; class People { public: REFLECTABLE( People, (CString) groupName, (vector<Person>) people ) }; void main(void) { People ppl; ppl.groupName = "Group1"; Person p; p.name = L"Roger"; p.age = 37; ppl.people.push_back(p); p.name = L"Alice"; p.age = 27; ppl.people.push_back( p ); p.name = L"Cindy"; p.age = 17; ppl.people.push_back( p ); CStringA xml = ToXML( &ppl ); CStringW errors; People ppl2; FromXml( &ppl2, xml, errors ); CStringA xml2 = ToXML( &ppl2 ); printf( xml2 ); }

REFLECTABLE define utiliza el nombre de la clase + el nombre del campo con offsetof - para identificar en qué lugar de la memoria se encuentra el campo en particular. He intentado captar la terminología .NET en la medida de lo posible, pero C ++ y C # son diferentes, por lo que no es 1 a 1. El modelo de reflexión de C ++ completo reside en las clases TypeInfo y FieldInfo para el calendario, es posible expandir el soporte también al método , pero he decidido mantener las cosas simples por ahora.

He utilizado el analizador pugi xml para obtener el código de demostración en xml y restaurarlo de nuevo desde xml.

Así que la salida producida por el código de demostración es así:

<?xml version="1.0" encoding="utf-8"?> <People groupName="Group1"> <people> <Person name="Roger" age="37" /> <Person name="Alice" age="27" /> <Person name="Cindy" age="17" /> </people> </People>

También es posible habilitar el soporte de cualquier clase / estructura de terceros a través de la clase TypeTraits y la especificación de plantilla parcial, para definir su propia TypeTraitsTclase, de manera similar a CStringo int- ver el código de ejemplo en

https://sourceforge.net/p/testcppreflect/code/HEAD/tree/TypeTraits.h#l65

template <> class TypeTraitsT<CString> : public TypeTraits { public: virtual CStringW ToString( void* pField ) { CString* s = (CString*)pField; return *s; } virtual void FromString( void* pField, const wchar_t* value ) { CString* s = (CString*)pField; *s = value; } }; template <> class TypeTraitsT<int> : public TypeTraits { public: virtual CStringW ToString( void* pField ) { int* p = (int*) pField; return std::to_string(*p).c_str(); } virtual void FromString( void* pField, const wchar_t* value ) { int* p = (int*)pField; *p = _wtoi(value); } };

Supongo que el único inconveniente de mi propia implementación es el uso de la __if_existsextensión específica del compilador de Microsoft. Si alguien sabe cómo caminar alrededor de él, hágamelo saber.


EDITAR : CAMP no se mantiene más; Dos tenedores están disponibles:

  • Uno también se llama CAMP , y se basa en la misma API.
  • Ponder es una reescritura parcial, y se preferirá ya que no requiere Boost; está utilizando C ++ 11.

CAMP es una biblioteca con licencia de MIT (anteriormente LGPL) que agrega reflexión al lenguaje C ++. No requiere un paso de preprocesamiento específico en la compilación, pero el enlace debe hacerse manualmente.

La biblioteca actual de Tegesoft usa Boost, pero también hay una bifurcación que usa C ++ 11 que ya no requiere Boost .



la falta de reflexión incorporada en C ++ es la única razón por la que C ++ moderno no se utiliza para el desarrollo web (y carece de ORM y otros marcos)

Puedes probar http://www.extreme.indiana.edu/reflcpp/


Una forma sencilla es usar el dynamic_cast<>()operador que, cuando se asigna a un tipo incorrecto, devuelve NULL, de modo que puede pasar a una clase concreta básica de una manera fácil, verificando el valor del puntero, si no es NULL, la conversión fue hecho, y tienes el tipo del objeto.

Pero esto es solo una solución simple, y solo proporciona el tipo de objetos, no puede preguntar qué métodos tiene, como en Java. Si necesita una solución avanzada, hay algunos marcos para elegir.