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.
;)
Questo non costerà più di RTTI, quindi cosa guadagna? – Mark
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 ... –
@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. –