在c++中實現反射的初步想法
最近在思考如何在c++中實現反射。事情的起因是這樣的:我們服務器是用c++開發的,如果需要寫一些測試用的GM指令的話,需要編寫完GM代碼後重新編譯並且重啟進程,工序繁瑣且比較耗時。因此就有了想用腳本(lua或py)來寫GM的想法。用腳本來做這事我覺得還挺適合的。首先可以免去編譯、重啟(通過腳本的reload)的過程。其次測試用的GM沒有高性能的需求,並且一般在進行自測的階段時,代碼往往是頻繁改動的,剛好適合用靈活的腳本:P
接下來的問題就是,要如何在項目中引入腳本。考慮到使用場景,我覺得這個腳本機制最核心的一點就是:不需要顯示的定義出c++暴露給腳本的接口。因為這塊主要是在測試環境下工作,因此主要關註的是使用更便捷。如果每一個c++模塊都需要再手動寫一個wrapper類才能用腳本調用它,這樣就太繁瑣了,完全背離了減負的初衷。
首先我想到的是自動生成wrapper類的代碼,比如說使用SWIG。方法可以是把需要生成wrapper的模塊(實際上c++沒有模塊,只有編譯單元)的文件名集中寫在一個文件,在編譯構建時掃描文件中記錄的模塊,統一生成wrapper類。但這樣其實並沒有做到業務代碼完全無感知,只是把寫wrapper類的工作優化成寫模塊名:-(
那麽掃描項目中的所有模塊呢?不管是否需要,統統都生成一份wrapper。這似乎可行。不過我先不考慮它,因為我心裏還有另外一個方法。
在前一個項目中,我了解到了一種通過反射來在c#中嵌入cpython虛擬機的方法。因此我在想,如果能在c++中實現一個反射,那不就可以做到用腳本調用c++代碼了嗎。
要在c++實現反射,不可避免的是要把一個模塊(編譯單元)的入口記錄下來。其中入口包括了函數、全局變量、類的類型信息等等。一般的做法可能是顯示的定義出一個模塊的入口。優雅一點的可能是在.h文件裏稍動手腳,悄悄的把入口信息記錄下來。
但是我更希望是,在實現反射時能對原有代碼做盡可能少的改動。並且最好可以和業務代碼完全的分離。在線上環境下,能夠不產生任何性能開銷。因此,我想到了是另外一種方法。
實際上在編譯的過程中,是有模塊的入口信息的(比如說符號表)。但是在編譯完之後,這些信息就被丟掉了。如果把這些編譯的中間信息存下來,存成一個單獨的文件,那麽在運行時就可以在通過在文件中查找對應的信息,來實現反射了。類似於在調試時通過載入可執行文件對應的pdb,來獲取調試信息一樣。
在c++中實現反射的初步想法