Come posso aggiungere una riflessione a un’applicazione C ++?

Mi piacerebbe essere in grado di introspezionare una class C ++ per il suo nome, i suoi contenuti (cioè i membri ei loro tipi) ecc. Sto parlando di C ++ nativo qui, non gestito C ++, che ha una riflessione. Mi rendo conto che C ++ fornisce alcune informazioni limitate usando RTTI. Quali librerie aggiuntive (o altre tecniche) potrebbero fornire queste informazioni?

Quello che devi fare è fare in modo che il preprocessore generi dati di riflessione sui campi. Questi dati possono essere memorizzati come classi nidificate.

In primo luogo, per rendere più facile e più pulito scrivere nel preprocessore useremo un’espressione digitata. Un’espressione digitata è solo un’espressione che mette il tipo tra parentesi. Quindi invece di scrivere int x scriverai (int) x . Ecco alcuni utili macro per aiutare con le espressioni digitate:

 #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 

Successivamente, definiamo una macro REFLECTABLE per generare i dati relativi a ciascun campo (più il campo stesso). Questa macro sarà chiamata così:

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

Quindi usando Boost.PP iteriamo su ogni argomento e generiamo i dati in questo modo:

 // A helper metafunction for adding const to a type template struct make_const { typedef T type; }; template struct make_const { typedef typename boost::add_const::type type; }; #define REFLECTABLE(...) \ static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \ friend struct reflector; \ template \ 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 \ struct field_data \ { \ Self & self; \ field_data(Self & self) : self(self) {} \ \ typename make_const::type & get() \ { \ return self.STRIP(x); \ }\ typename boost::add_const::type & get() const \ { \ return self.STRIP(x); \ }\ const char * name() const \ {\ return BOOST_PP_STRINGIZE(STRIP(x)); \ } \ }; \ 

Ciò che fa è generare una costante fields_n che è il numero di campi fields_n nella class. Quindi specializza i field_data di campo per ogni campo. È anche amico della class reflector , è così che può accedere ai campi anche quando sono privati:

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

Ora per scorrere i campi utilizziamo il pattern visitor. Creiamo un intervallo MPL da 0 al numero di campi e accediamo ai dati del campo in quell’indice. Quindi passa i dati del campo al visitatore fornito dall’utente:

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

Ora per il momento della verità mettiamo tutto insieme. Ecco come possiamo definire una class Person che sia riflessiva:

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

Ecco una funzione print_fields generalizzata che utilizza i dati di riflessione per scorrere i campi:

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

Un esempio di utilizzo di print_fields con la class Person reflectable:

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

Quali uscite:

 name=Tom age=82 

E voilà, abbiamo appena implementato la riflessione in C ++, in meno di 100 righe di codice.

Ci sono due tipi di reflection nuotano.

  1. Ispezione mediante iterazione su membri di un tipo, enumerazione dei suoi metodi e così via.

    Questo non è ansible con C ++.

  2. Ispezione controllando se un tipo di class (class, struct, union) ha un metodo o un tipo nidificato, è derivato da un altro tipo particolare.

    Questo tipo di cose è ansible con C ++ usando template-tricks . Usa boost::type_traits per molte cose (come controllare se un tipo è integrale). Per verificare l’esistenza di una funzione membro, utilizzare È ansible scrivere un modello per verificare l’esistenza di una funzione? . Per verificare se esiste un certo tipo annidato, utilizzare SFINAE semplice.

Se sei piuttosto alla ricerca di modi per realizzare 1), come guardare quanti metodi ha una class, o come ottenere la rappresentazione della stringa di un id di class, allora temo che non ci sia un modo standard in C ++ per farlo. Devi usare entrambi

  • Un Meta Compiler come il Qt Meta Object Compiler che traduce il tuo codice aggiungendo ulteriori meta-informazioni.
  • Un quadro che consttiene di macro che ti permettono di aggiungere le meta-informazioni richieste. Dovresti dire al framework tutti i metodi, i nomi delle classi, le classi base e tutto ciò di cui ha bisogno.

C ++ è fatto con velocità in mente. Se vuoi un’ispezione di alto livello, come C # o Java, allora temo di doverti dire che non c’è modo, senza alcuno sforzo.

E mi piacerebbe un pony, ma i pony non sono liberi. :-p

http://en.wikibooks.org/wiki/C%2B%2B_Programming/RTTI è ciò che otterrai. Riflessione come la stai pensando – metadati completamente descrittivi disponibili in fase di esecuzione – non esiste per C ++ solo per impostazione predefinita.

Le informazioni esistono, ma non nel formato che ti serve e solo se esporti le tue classi. Funziona su Windows, non so su altre piattaforms. Utilizzando gli specificatori della class di archiviazione come, ad esempio:

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

Questo fa sì che il compilatore generi i dati di definizione della class nella DLL / Exe. Ma non è in un formato che puoi facilmente utilizzare per la riflessione.

Nella mia azienda abbiamo creato una libreria che interpreta questi metadati e consente di riflettere una class senza inserire extra macros ecc. Nella class stessa. Permette di chiamare le funzioni come segue:

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

Questo effettivamente fa:

 instance_ptr->Foo(1.331); 

La funzione Invoke (this_pointer, …) ha argomenti variabili. Ovviamente chiamando una funzione in questo modo si aggirano cose come const-safety e così via, quindi questi aspetti sono implementati come controlli di runtime.

Sono sicuro che la syntax potrebbe essere migliorata e funziona solo su Win32 e Win64 finora. Abbiamo trovato molto utile avere interfacce GUI automatiche per le classi, creare proprietà in C ++, streaming da e verso XML e così via, e non è necessario derivare da una class base specifica. Se c’è abbastanza domanda forse potremmo metterla in forma per il rilascio.

RTTI non esiste per C ++.

Questo è semplicemente sbagliato. In realtà, il termine stesso “RTTI” è stato coniato dallo standard C ++. D’altro canto, RTTI non si spinge molto oltre nell’attuazione della riflessione.

Hai bisogno di guardare a ciò che stai cercando di fare, e se RTTI soddisferà le tue esigenze. Ho implementato la mia pseudo-riflessione per scopi molto specifici. Ad esempio, una volta volevo essere in grado di configurare in modo flessibile l’output di una simulazione. È richiesto l’aggiunta di un codice boilerplate alle classi che verrebbero esportate:

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

La prima chiamata aggiunge questo object al sistema di filtraggio, che chiama il metodo BuildMap() per capire quali metodi sono disponibili.

Quindi, nel file di configurazione, puoi fare qualcosa del genere:

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

Attraverso alcuni template magic che coinvolgono boost , questo viene tradotto in una serie di chiamate di metodo in fase di esecuzione (quando viene letto il file di configurazione), quindi è abbastanza efficiente. Non consiglierei di farlo a meno che non ne abbia davvero bisogno, ma quando lo fai, puoi fare cose davvero interessanti.

Cosa stai cercando di fare con la riflessione?
È ansible utilizzare i tratti del tipo Boost e le librerie typeof come una forma limitata di riflessione in fase di compilazione. Cioè, puoi ispezionare e modificare le proprietà di base di un tipo passato a un modello.

Consiglierei di usare Qt .

C’è una licenza open-source e una licenza commerciale.

EDIT : CAMP non è più mantenuto; sono disponibili due forche:

  • Uno si chiama anche CAMP e si basa sulla stessa API.
  • Ponder è una riscrittura parziale, e deve essere preferito in quanto non richiede Boost; sta usando C ++ 11.

CAMP è una libreria con licenza MIT (precedentemente LGPL) che aggiunge riflessione al linguaggio C ++. Non richiede una fase di pre-elaborazione specifica nella compilazione, ma l’associazione deve essere eseguita manualmente.

L’attuale libreria di Tegesoft utilizza Boost, ma c’è anche un fork che usa C ++ 11 che non richiede più Boost .

Ho fatto qualcosa di simile a quello che stai cercando una volta, e mentre è ansible ottenere un certo livello di riflessione e accesso a funzionalità di livello superiore, il mal di testa potrebbe non valerne la pena. Il mio sistema è stato utilizzato per mantenere le classi dell’interfaccia utente completamente separate dalla logica di business attraverso la delega simile al concetto di passaggio e inoltro dei messaggi di Objective-C. Il modo per farlo è creare una class base che sia in grado di mappare i simboli (ho usato un pool di stringhe ma potresti farlo con enumerazione se preferisci la gestione degli errori in fase di compilazione rispetto alla flessibilità totale) ai puntatori di funzione (in realtà no puntatori di funzioni puri, ma qualcosa di simile a ciò che Boost ha con Boost.Function – a cui non avevo accesso in quel momento). Puoi fare la stessa cosa per le tue variabili membro finché hai una class base comune capace di rappresentare qualsiasi valore. L’intero sistema è stato un imbroglio schiacciante di codifica e delega di valori-chiave, con alcuni effetti collaterali che valevano forse la mera quantità di tempo necessaria per ottenere ogni class che utilizzava il sistema per abbinare tutti i suoi metodi e membri alle chiamate legali : 1) Qualsiasi class può chiamare qualsiasi metodo su qualsiasi altra class senza dover includere intestazioni o scrivere classi base false in modo che l’interfaccia possa essere predefinita per il compilatore; e 2) I getter e i setter delle variabili membro erano facili da rendere thread-safe perché la modifica o l’accesso ai loro valori veniva sempre eseguito attraverso 2 metodi nella class base di tutti gli oggetti.

Ha anche portato alla possibilità di fare cose davvero strane che altrimenti non sono facili in C ++. Ad esempio, potrei creare un object Array che contiene elementi arbitrari di qualsiasi tipo, incluso se stesso, e creare dynamicmente nuovi array passando un messaggio a tutti gli elementi dell’array e raccogliendo i valori restituiti (simile alla mappa in Lisp). Un altro era l’implementazione dell’osservazione dei valori-chiave, in base al quale ero in grado di configurare l’interfaccia utente per rispondere immediatamente ai cambiamenti nei membri delle classi di backend invece di interrogare costantemente i dati o di ridisegnare inutilmente il display.

Forse è più interessante per te il fatto che puoi anche scaricare tutti i metodi e i membri definiti per una class, e in forma di stringa non meno.

Svantaggi del sistema che potrebbe scoraggiarti dal preoccuparti: aggiungere tutti i messaggi e i valori-chiave è estremamente noioso; è più lento che senza alcuna riflessione; diventerai odioso vedendo boost::static_pointer_cast e boost::dynamic_pointer_cast su tutto il tuo codebase con una passione violenta; i limiti del sistema fortemente tipizzato sono ancora lì, li stai davvero nascondendo un po ‘, quindi non è così ovvio. Anche gli errori di battitura nelle corde non sono divertenti o facili da scoprire.

Per quanto riguarda come implementare qualcosa di simile: basta usare i puntatori condivisi e deboli su una base comune (il mio era chiamato in modo molto fantasioso “Oggetto”) e derivare per tutti i tipi che si desidera utilizzare. Io suggerirei di installare Boost.Function invece di farlo nel modo in cui lo feci, che era con alcune schifezze personalizzate e una tonnellata di brutti macchi per racchiudere le chiamate del puntatore alla funzione. Poiché tutto è mappato, l’ispezione degli oggetti è solo una questione di iterare attraverso tutti i tasti. Dato che le mie classi erano essenzialmente vicine a un ripudio diretto di Cocoa ansible usando solo C ++, se vuoi qualcosa del genere, ti suggerisco di usare la documentazione di Cocoa come progetto.

Le due soluzioni di riflessione che conosco dai miei giorni C ++ sono:

1) Usa RTTI, che fornirà un bootstrap per build il tuo comportamento di riflessione, se sei in grado di ottenere che tutte le tue classi derivino da una class base ‘object’. Quella class potrebbe fornire alcuni metodi come GetMethod, GetBaseClass ecc. Per quanto riguarda il funzionamento di questi metodi, dovrai aggiungere manualmente alcune macro per decorare i tuoi tipi, che dietro le quinte creano metadati nel tipo per fornire risposte a GetMethods, ecc.

2) Un’altra opzione, se si ha accesso agli oggetti del compilatore, è usare DIA SDK . Se ricordo bene, questo ti permette di aprire pdbs, che dovrebbe contenere metadati per i tuoi tipi C ++. Potrebbe essere sufficiente fare quello che ti serve. Questa pagina mostra come è ansible ottenere tutti i tipi di base di una class, per esempio.

Entrambe queste soluzioni sono un po ‘brutte però! Non c’è niente come un po ‘di C ++ per farti apprezzare i lussi di C #.

In bocca al lupo.

C’è un’altra nuova libreria per la riflessione in C ++, chiamata RTTR (Run Time Type Reflection, vedi anche github ).

L’interfaccia è simile al riflesso in C # e funziona senza RTTI.

EDIT: collegamento interrotto aggiornato a partire dal 7 febbraio 2017.

Penso che nessuno abbia menzionato questo:

Al CERN usano un sistema di riflessione completa per C ++:

Riflesso del CERN . Sembra funzionare molto bene.

Questa domanda è un po ‘vecchia ora (non so perché continuo a fare domande vecchie oggi) ma stavo pensando a BOOST_FUSION_ADAPT_STRUCT che introduce una riflessione in fase di compilazione.

Spetta a te mappare questo al riflesso del tempo di esecuzione, naturalmente, e non sarà troppo facile, ma è ansible in questa direzione, mentre non sarebbe nel contrario 🙂

Penso davvero che una macro per incapsulare il BOOST_FUSION_ADAPT_STRUCT possa generare i metodi necessari per ottenere il comportamento di runtime.

Penso che potresti trovare interessante l’articolo “Usare i modelli per la riflessione in C ++” di Dominic Filion. È nella sezione 1.4 di Game Programming Gems 5 . Purtroppo non ho la mia copia con me, ma cercalo perché penso che spieghi cosa stai chiedendo.

Reflection non è supportato da C ++ out of the box. Questo è triste perché rende un test difensivo un dolore.

Ci sono diversi approcci per fare la riflessione:

  1. usa le informazioni di debug (non portabili).
  2. Cosparga il tuo codice con macro / modelli o qualche altro approccio alla fonte (sembra brutto)
  3. Modificare un compilatore come clang / gcc per produrre un database.
  4. Usa l’approccio Qt moc
  5. Aumenta il riflesso
  6. Riflessione precisa e piatta

Il primo link sembra il più promettente (usa mod per clangare), il secondo discute un numero di tecniche, il terzo è un approccio diverso che usa gcc:

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

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

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

Ora c’è un gruppo di lavoro per la riflessione C ++. Vedi le notizie per C ++ 14 @ CERN:

Modifica 13/08/17: Dal post originale ci sono stati un certo numero di potenziali progressi sulla riflessione. Quanto segue fornisce maggiori dettagli e una discussione sulle varie tecniche e stato:

  1. Riflessione statica in poche parole
  2. Riflessione statica
  3. Un design per la riflessione statica

Tuttavia non sembra promettente su un approccio di riflessioni standardizzate in C ++ nel prossimo futuro a meno che non ci sia molto più interesse da parte della comunità nel supportare la riflessione in C ++.

I seguenti dettagli lo stato corrente in base al feedback dell’ultima riunione degli standard C ++:

  • Riflessioni sulle proposte di riflessione

Modifica il 13/12/2017

La riflessione sembra muoversi verso C ++ 20 o più probabilmente un TSR. Il movimento è comunque lento.

  • Specchio
  • Speculare proposta standard
  • Carta specchio
  • Herb Sutter – meta programmazione che include la riflessione

Ponder è una libreria di riflessione C ++, in risposta a questa domanda. Ho preso in considerazione le opzioni e ho deciso di farcela da sola, dal momento che non riuscivo a trovarne una che comprendesse tutte le mie scatole.

Sebbene ci siano ottime risposte a questa domanda, non voglio usare tonnellate di macro o fare affidamento su Boost. Boost è una grande libreria, ma ci sono molti piccoli progetti personalizzati su C ++ 0x che sono più semplici e hanno tempi di compilazione più veloci. Ci sono anche dei vantaggi nell’essere in grado di decorare esternamente una class, come il wrapping di una libreria C ++ che non supporta (ancora?) C ++ 11. È un fork di CAMP, che usa C ++ 11, che non richiede più Boost .

La riflessione riguarda essenzialmente ciò che il compilatore ha deciso di lasciare come impronte nel codice che il codice runtime può interrogare. C ++ è famoso per non pagare per quello che non usi; poiché molte persone non usano / vogliono riflettere, il compilatore C ++ evita il costo non registrando nulla .

Quindi, C ++ non fornisce la riflessione, e non è facile “simularlo” come regola generale come hanno notato altre risposte.

Sotto “altre tecniche”, se non hai una lingua con la riflessione, prendi uno strumento che può estrarre le informazioni che vuoi al momento della compilazione.

Il nostro software DMS Reengineering Toolkit è una tecnologia di compilazione generalizzata parametrizzata da definizioni esplicite di langauge. Ha definizioni langauge per C, C ++, Java, COBOL, PHP, …

Per le versioni C, C ++, Java e COBOL, fornisce l’accesso completo agli alberi di analisi e alle informazioni sulla tabella dei simboli. Queste informazioni sulla tabella dei simboli includono il tipo di dati che probabilmente vorrai dal “riflesso”. Se l’objective è quello di enumerare alcuni set di campi o metodi e fare qualcosa con essi, DMS può essere utilizzato per trasformare il codice in base a ciò che si trova nelle tabelle dei simboli in modi arbitrari.

Puoi trovare un’altra libreria qui: http://www.garret.ru/cppreflection/docs/reflect.html Supporta 2 modi: ottenere informazioni sul tipo dalle informazioni di debug e lasciare che il programmatore fornisca queste informazioni.

Mi interessava anche la riflessione per il mio progetto e ho trovato questa libreria, non l’ho ancora provata, ma ho provato altri strumenti da questo ragazzo e mi piace come funzionano 🙂

Dai un’occhiata a Classdesc http://classdesc.sf.net . Fornisce la riflessione sotto forma di “descrittori” di class, funziona con qualsiasi compilatore C ++ standard (sì, è noto che funziona con Visual Studio e GCC) e non richiede l’annotazione del codice sorgente (sebbene esistano alcuni pragma per gestire situazioni difficili ). È in sviluppo da oltre un decennio e viene utilizzato in una serie di progetti su scala industriale.

Quando volevo una riflessione in C ++ leggevo questo articolo e miglioravo ciò che vedevo lì. Spiacente, non può avere. Non possiedo il risultato … ma puoi certamente ottenere ciò che ho e andare da lì.

Attualmente sto ricercando, quando ne ho voglia, metodi per usare inherit_linearly per rendere la definizione dei tipi di riflessione più semplice. In realtà sono arrivato abbastanza lontano ma ho ancora dei modi per andare. Le modifiche in C ++ 0x sono molto probabilmente di grande aiuto in quest’area.

Sembra che il C ++ non abbia ancora questa caratteristica. E anche C ++ 11 riflessione posticipata ((

Cerca alcuni macro o creane uno. Qt può anche aiutare con la riflessione (se può essere usata).

Prova a guardare questo progetto http://www.garret.ru/cppreflection/docs/reflect.html vengono aggiunte riflessioni a C ++. Ha aggiunto i metadati alle classi che è ansible utilizzare.

anche se il reflection non è supportato immediatamente in c ++, non è troppo difficile da implementare. Ho incontrato questo fantastico articolo: http://replicaisland.blogspot.co.il/2010/11/building-reflective-object-system-in-c.html

l’articolo spiega in dettaglio come implementare un sistema di riflessione piuttosto semplice e rudimentale. non è stata la soluzione più salutare, e ci sono degli spigoli da risolvere, ma per i miei bisogni era sufficiente.

la linea di fondo – la riflessione può ripagare se eseguita correttamente, ed è completamente fattibile in c ++.

Vorrei pubblicizzare l’esistenza del toolkit automatico di introspezione / riflessione “IDK”. Usa un meta-compilatore come Qt’s e aggiunge meta informazioni direttamente nei file object. Si afferma che sia facile da usare. Nessuna dipendenza esterna. Permette anche di riflettere automaticamente std :: string e quindi usarlo negli script. Per favore guarda IDK

Reflection in C ++ è molto utile, nei casi in cui è necessario eseguire un metodo per ciascun membro (ad esempio: serializzazione, hashing, confronto). Sono arrivato con una soluzione generica, con una syntax molto semplice:

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

Dove ENUMERATE_MEMBERS è una macro, che è descritta più avanti (UPDATE):

Supponiamo di aver definito la funzione di serializzazione per int e std :: string come questo:

 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()); } 

E abbiamo una funzione generica vicino alla “macro segreta”;)

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

Ora puoi scrivere

 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) 

Quindi, avendo la macro ENUMERATE_MEMBERS nella struct definition, è ansible creare serializzazione, confronto, hashing e altre cose senza toccare il tipo originale, l’unico requisito è implementare il metodo “EnumerateWith” per ogni tipo, che non è enumerabile, per enumeratore (come BinaryWriter) . Di solito dovrai implementare 10-20 tipi “semplici” per supportare qualsiasi tipo nel tuo progetto.

Questa macro dovrebbe avere zero-overhead per strutturare la creazione / distruzione in fase di esecuzione e il codice di T.EnumerateWith () dovrebbe essere generato su richiesta, che può essere ottenuto rendendolo funzione template-inline, quindi l’unico overhead in tutta la storia è di aggiungere ENUMERATE_MEMBERS (m1, m2, m3 …) a ogni struct, mentre l’implementazione di un metodo specifico per tipo di membro è un must in ogni soluzione, quindi non lo presumo come overhead.

AGGIORNAMENTO: Esiste un’implementazione molto semplice della macro ENUMERATE_MEMBERS (tuttavia potrebbe essere un po ‘estesa per supportare l’ereditarietà dalla struttura enumerabile)

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

And you do not need any 3rd party library for these 15 lines of code 😉

Root Reflex project has support for this.

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

Updated 24.2.2017

Previously I have analyzed support for using #define’s and like it’s recommended in some of web articles – I’ve hit across defines in visual C++ were not working identically compared to define’s used in gcc (for example, on internet this is quite often referred as “MSVC walkaround”). Besides not be able to easily understand what is happening behind define / macro expansion machinery – it’s rather difficult to debug each macro expansion.

There are couple of ways to walk around complexities of define expansion, one approach is to turn on “/P” compiler flag (pre-process to file only) – after that you can compare how your define’s opened up. (Previously I have also used also actively stringfy operator (#))

I have collected all useful defines from multiple forums, resorted them, and commented out what is happening behind machinery, you can find whole header file in here now:

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

I thought it was rather trivial to use these macros to enable C++ reflection, but it requires bit more magic to do the reflection.

I’ve recollected a working sample code, and put it as sourceforge project, can be downloaded here:

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

Demo code looks like this:

 #include "CppReflect.h" using namespace std; class Person { public: REFLECTABLE( Person, (CString) name, (int) age ) }; class People { public: REFLECTABLE( People, (CString) groupName, (vector) 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 uses class name + field name with offsetof – to identify at which place in memory particular field is located. I have tried to pick up .NET terminology for as far as possible, but C++ and C# are different, so it’s not 1 to 1. Whole C++ reflection model resides in TypeInfo and FieldInfo classs for timebeing, it’s possible to expand support also to method, but I’ve decided to keep things simple for now.

I have used pugi xml parser to fetch demo code into xml and restore it back from xml.

So output produced by demo code looks like this:

         

It’s also possible to enable any 3-rd party class / structure support via TypeTraits class, and partial template specification – to define your own TypeTraitsT class, in similar manner to CString or int – see example code in

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

 template <> class TypeTraitsT : 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 : 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); } }; 

I guess only downside of my own implementation is use of __if_exists – which might be Microsoft compiler specific extension. If someone knows how to walk around it, let me know.

lack of built in reflection in C++ is the single reason why modern C++ is not used for web development (and lacks ORM and other frameworks)

You can try http://www.extreme.indiana.edu/reflcpp/

A simple way is to use the dynamic_cast<>() operator which, when assigned to an wrong type, returns NULL, so you can upcast to a base concrete class in an easy way, checking the value of the pointer, if it is not NULL, the cast was done, and you got the type of the object.

But this is just a simple solution, and it only provides the type of the objects, you cannot ask what methods it has, like in Java. If you need an advanced solution, there are some frameworks to choose from.