2010-02-12 12 views
7

Sto pensando di aggiungere qualche tipo di capacità di riflessione ad alcune classi C++ (in modo da non dover utilizzare RTTI): ottenere nomi di metodi, campi dichiarati, nome classe ... questo genere di cose.Come implementare la riflessione di base in C++?

Stavo pensando di analizzare i file sorgente esistenti, ottenere un elenco dei campi dichiarati metodi & e riscrivere ogni file sorgente, aggiungendo questo tipo di informazioni a ciascuna classe.

Cosa ne pensi di questo approccio? Mi piacerebbe fare tutto da zero, poiché penso che sia una grande opportunità per imparare. Suggeriresti altri modi per farlo?

// OFFTOPICO: è così che fa Qt?

+0

Questo non costerà più di RTTI, quindi cosa guadagna? – Mark

+0

Resistente. Non è possibile eseguire il marshalling del compilatore per generare i metadati necessari dal codice sorgente. RTTI è gravemente inadeguato. Dovrai scrivere il tuo parser di linguaggio C++. È stato fatto ... –

+0

@nobugz: Certo, le persone hanno scritto i loro parser C++. Questo è molto lontano dall'essere banale. Il C++ è, molto semplicemente, un vero dolore da analizzare. –

risposta

1

A meno che non si desideri modificare il compilatore C++ o dipendere dalle estensioni del fornitore, è necessario un mucchio di macro CPP che costruiscono strutture dati.

3

Controllare la libreria Boost.Mirror. Non è stato ancora accettato in Boost, quindi dovrai fare il download it dal Boost Vault.

La libreria è ancora in fase di sviluppo e i documenti sono scarsi ma studiando lo provided examples potresti riuscire a ottenere ciò che desideri.

EDIT: se si vuole veramente per analizzare il codice C++ proprio allora forse si dovrebbe prendere in considerazione clang

+0

Wow. Sembra così conciso e facile da usare. – Eric

+0

Sei sarcastico? – Manuel

+2

print_meta_data Eric

3

avrei sborsare gcc.

2

Sì, questo è come fa Qt - tranne che non aggiunge le informazioni alla classe stessa, crea una nuova classe chiamata metaclasse che contiene dati statici sulla classe. Questo è meglio per ovvi motivi.

Nessun approccio C++ puro fornirà una riflessione completamente automatica - il linguaggio non lo consente. Ci sono molti tentativi, tra cui Boost.Mirror e Boost.Reflection, ma tutti richiedono aggiunte alla tua fonte.

0

Forse è possibile utilizzare la libreria typeinfo, con la quale è ora possibile eseguire una classe di oggetti in runtime. Esempio:

#include<iostream> 

#include<typeinfo> 
class A{}; 

int main() 
{ 
A a; 

std::cout<<typeid(a).name(); 

} 

Si può vedere di più nel: http://www.cplusplus.com/reference/std/typeinfo/

[] `s

+0

Non voglio usare RTTI. – Geo

1

Si potrebbe trovare gccxml la pena dare un'occhiata. Ad esempio, convertirà this in this, il che significa che devi solo analizzare XML anziché C++.

C'è un interessante SP & E paper che descrive l'utilizzo di gccxml in combinazione con un linker modificato per "fornire funzionalità di Java-reflection-like alle applicazioni C++ in modo pulito e non intrusivo".

1

Modificare il compilatore per produrre un metodo GetClass statico, restituendo un puntatore a un oggetto Class che descrive la classe, per ogni classe definita.

Modificare il compilatore/linker per estrarre i metadati necessari e inserire quelli in sezioni/simboli speciali dell'immagine risultante risultante, analogamente ai simboli di debugging.

GetClass utilizza (leggi/carica/cache?) I metadati precedenti.

E sì. È un sacco di lavoro. (E un buon effetto collaterale sta nel fatto che stai quasi per eliminare i file di intestazione - perché ora le informazioni potrebbero essere estratte da un'immagine)

1

C++ non supporta la riflessione. Gli sforzi di boost e RTTI sono piuttosto limitati e metà cotti. Io non li consiglio.

Hai detto che puoi iniziare da scatch. Bene, puoi scrivere un lexer in C++ usando lex/yacc o ANTLR. Un sacco di lavoro, ma imparerai molto.

Se scrivere un parser è un'attività formidabile per te, ci sono alcune altre opzioni per ottenere i metadati di una classe. Microsoft ha DIA SDK che fornisce informazioni sui simboli. Doxygen può generare file XML che è possibile analizzare per ottenere i nomi dei campi, i nomi dei metodi di una classe. Ho avuto qualche successo nell'analisi dei file XML di doxygen.

Se si lavora solo con la piattaforma Microsoft, una soluzione valida è utilizzare la libreria dei tipi (TLB). Richiede la conversione della classe in un'interfaccia in MS IDL. MIDL compila l'IDL in .tlb e l'applicazione può caricare il file .tlb in fase di runtime. La tua applicazione può ottenere quasi tutte le informazioni sulla classe attraverso l'interfaccia, le proprietà e i metodi di ITypeInfo.

+0

Wow! Doxygen non mi ha nemmeno attraversato la mente! Fantastico! – Geo

1

Segui la decima legge di Greenspun e sei a posto: implementa solo un'implementazione ad hoc, informata, incentrata su bug e lenta di metà di Common Lisp. Scegli una metà che supporti l'implementazione della riflessione. :)

Oppure, ecco un'idea:

Accendere file mappa (ed eventualmente la generazione di montaggio) nella build. Implementare un'applicazione che generi codice per creare una libreria dinamica. Dovresti essere in grado di ottenere tutte le informazioni statiche (nomi di metodi, parametri, tipi, ecc.) Sulle classi dall'origine o da uno degli artefatti di costruzione. Utilizzando la mappa o l'assieme dovresti essere in grado di trovare indirizzi di funzione, offset di puntatori di funzioni virtuali, indirizzi di variabili globali, offset variabili, informazioni sullo stack e variabili allocate di registro, ecc. E così via. Da questa informazione crea una libreria contenente le chiamate per ottenere tutte queste informazioni passando nomi di tipo, puntatori, nomi di funzioni ecc.

Ora, in C++, scrivi una libreria contenente funzioni e macro che ti consentono di inserire ID univoci che essere compilato nel codice per identificare l'inizio della funzione, tutte le chiamate di riflessione, offset EIP su chiamate di riflessione, ecc. Utilizzare macro di assembly in linea in posizioni appropriate che lasciano spazio ove necessario con NOP per inserire alcune istruzioni se necessario, in genere subito dopo ID. Inoltre, fornisci le funzioni della libreria bridge che consentiranno alla funzionalità di riflessione di gestire ciò che può essere in linea (ad esempio ottenere l'indirizzo di una funzione) e di dover effettuare chiamate alla libreria dinamica incorporata nel passaggio post-generazione.

Infine, scrivere un linker di riflessione build post-post. Suggerisco "kniltsoPostlink" ma questo dipende da voi. In questo passaggio cerca i tuoi ID univoci (ho già detto che per semplificare dovresti probabilmente creare i GUID degli ID in modo da poterli cercare nel binario?) E, ovunque l'ID segni una chiamata di riflessione a una funzione, o un definizione di una classe, ecc., inserisci lì abbastanza dati (in un formato puoi facilmente determinare just-in-time mentre scrivi la libreria di reflector) e poi nell'ID prima di una chiamata alla libreria di reflector, riscrivi la chiamata in modo che tira fuori i parametri di cui ha bisogno da quei bit di dati, o semplicemente mette i bit di dati proprio lì, se applicabile, non so davvero in anticipo, ma mentre lo scrivi, questi piccoli dettagli si apriranno su di te.

In ogni caso, so che non ho dato molto codice, e in realtà ho intenzione di iniziare un progetto su questo a un certo punto, quando avrò abbastanza tempo libero. Voglio dire, ogni piccola parte dovrebbe essere molto semplice così come lo fai in modo incrementale, ogni piccolo pezzo dovrebbe diventare chiaro fintanto che seguirai le linee guida qui esposte. È possibile che alcune di esse siano addirittura più semplici di quelle che ho descritto qui, perché si trattava di uno scenario peggiore. Potresti anche andare via senza dover mai riscrivere codice per le chiamate dei riflettori; il semplice posizionamento dei dati negli appositi punti può consentire alla libreria di estrarre quei bit in base alle esigenze senza ulteriori informazioni.

Sono molto contento che tu abbia chiesto; Sono molto impegnato adesso, ma se hai una notte gratis, immagino che sarà un buon inizio in una prima versione, e sarò felice di presentarti il ​​prima possibile.

;)

+0

Wow, vedo che è passato un anno e mezzo da quando hai fatto questa domanda, mi dispiace che tu abbia dovuto rinunciare a una vera risposta per così tanto tempo, ma è bello sapere che potrei aiutarti. : D ♡ – shelleybutterfly

+0

Molto interessante e molto dettagliato. Farò qualche lettura su di esso. Grazie! – Geo

+0

bene, prego. :) essere consapevoli del fatto che, anche se effettivamente penso che questo può essere fatto funzionare, è solo una tabella di marcia. (In altre parole, i commenti su quanto sarebbe facile sono tutti scherzosi). Ma credo che, affrontato iterativamente per trovare la soluzione migliore, si possa fare. (Non sono nemmeno così sicuro che sia più facile che scrivere un'estensione in C++ ed estendere gcc per compilarlo.) ... Sembra un po 'divertente il modo in cui l'ho descritto, un po' come: dimenticarti di C++, sei riflettendo se ti piace o no ... :) – shelleybutterfly

Problemi correlati