cocos2dx之tolua++全面分析(一):tolua++工具本身


在cocos2dx/tools/tolua++下面,有大量pkg文件,這些是按tolua++要求格式寫好的、需要導出到lua中的c++類描述文件。

每當在c++類里增加了新函數需要導出時,應同步修改相應的pkg文件,然后運行此目錄下的build.sh,就會重新生成cocos2dx/script/lua/cocos2dx_support/LuaCocos2d.cpp,里面就包含了對新增函數的封裝代碼。

build.sh的內容如下:

 ${TOLUA} -L basic.lua -o ../../scripting/lua/cocos2dx_support/LuaCocos2d.cpp Cocos2d.pkg  

在運行標准的tolua++之前,還加載了額外的腳本basic.lua,這是由於cocos2dx在lua綁定方面並未完全遵照tolua++的默認做法,因此需要對其進行定制,basic.lua主要做的事情是:

1、將所有導出的CCXXXX類的push函數(也就是將cpp obj傳進lua時調用的函數)修改為自己的toluafix_pushusertype_ccobject

2、將function和table兩種類型的to和is函數(分別是指將lua obj傳進cpp時調的函數、判斷一個變量是否為本類型時調的函數)修改為toluafix_is/to_funtion/table

以上兩條是tolua++本身提供的用戶類型定制方法,也就是通過定義tolua++認別的3個特殊變量_to/push/is_function[classname] = XXX來實現。

至於為什么要做這些定制到后面分析tolua++的綁定實現時再詳細說明。

下面還有一些通過強制替換輸出的cpp膠水代碼來實現的針對某一個類型的特殊定制:

3、將

ccColor3B color = *((ccColor3B*) tolua_tousertype(tolua_S,4,(void*)&(const ccColor3B)ccBLACK));

改為

const ccColor3B clr = ccBLACK;
ccColor3B color = *((ccColor3B*) tolua_tousertype(tolua_S,4,(void*)&clr));

這里的原因是某些函數聲明里,給ccColor3B類的參數帶了默認值ccBLACK,因此tolua++會轉出前者代碼,而ccBLACK實際是個const定義,無法取地址,因此強制替換成后者。這個問題其實很典型,在我以前自己做lua綁定庫時也遇到過,即c++的默認參數只是個語法糖,在實際生成匯編指令時,所有參數都是要齊備的,而調用方未提供的實參,自然是由編譯器幫助補上了(編譯期),因此在導出到lua里被調用時,早已無默認參數的概念(運行期),要么lua代碼必須提供所有參數,要么就是膠水層代碼提供。我當時的做法是膠水層沒有管這個事情,也就是寫lua代碼時根本不要想默認參數這回事。而cocos2dx這里的做法則是在膠水層搞定,給lua代碼提供了便利。

4、將

tolua_usertype(tolua_S,"LUA_FUNCTION");

刪除,將作為參數的

*((LUA_FUNCTION*)

也刪除,也就是普通的lua function不做為usertype使用(注冊、取參)。照理說lua自己的基本類型本就不該做為usertype,為什么tolua++會生成這樣的本不該有的代碼呢?出現這種情況的原因是,cocos2dx里有一些接受某個lua回調函數作為參數的函數,如

void registerScriptObserver(CCObject *target,int handler,const char* name);

這種函數在c++代碼里,使用int作為回調函數參數的類型是很自然的,因為cocos2dx除了lua還要支持js等其它腳本語言,不可能直接使用某一個語言特有的函數類型來表示此參數,因此將其抽象為一個int型的handler。(雖然實際上lua本身也恰好是用int來表示函數引用的)

但是在pkg文件里,以上聲明被改寫為:

 void registerScriptObserver(CCObject *target,LUA_FUNCTION funcID,const char* name);  

這里int換成了LUA_FUNCTION。因為如果不換,那么tolua++生成的膠水代碼就不知道這里要提取一個function,而是直接生成提取一個int變量的錯誤代碼了。為了讓tolua++生成正確的代碼,需要hook它生成此處代碼的邏輯,所以這里實際上包含兩步,一是首先通過將int修改為LUA_FUNCTION使tolua++意識到這里有一個特殊類型(即不是基礎類型)的參數,二是通過上述第2條所做的事讓tolua++使用cocos2dx針對此類型提供的專有存取函數。如果不做第一步,直接給int提供push/to/is函數,當然也可以達到hook插入自有代碼的目的,但是所有使用int做參數的地方(而非僅是用int表示腳本回調函數處)就全受影響了,因此LUA_FUNCTION在這里就是起到一個標識回調函數——hook只限於此——用途的作用。

可以在basic.lua里將此處注釋掉(包括第2條)來檢查生成代碼的差異:

@@ -13018,7 +13018,7 @@ static int tolua_Cocos2d_CCNotificationCenter_registerScript
{
CCNotificationCenter* self = (CCNotificationCenter*) tolua_tousertype(tolua_S,1,
CCObject* target = ((CCObject*) tolua_tousertype(tolua_S,2,0));
- LUA_FUNCTION funcID = ( toluafix_ref_function(tolua_S,3,0));
+ LUA_FUNCTION funcID = *((LUA_FUNCTION*) tolua_tousertype(tolua_S,3,0));
const char* name = ((const char*) tolua_tostring(tolua_S,4,0));

-對應的是正確的代碼,調用專門提供的toluafix_ref_function來取得一個lua function的ref,其返回值類型定義正是pkg中形參類型,也恰好是lua本身的一個typedef,語法完全正確。

+對應的是注釋之后生成的錯誤的代碼,對於lua function型參數,用普通的tolua_tousertype去提取,完全對不上型號。

5、將

toluafix_pushusertype_ccobject(tolua_S,(void*)tolua_ret

替換為

int nID = (tolua_ret) ? (int)tolua_ret->m_uID : -1;
int* pLuaID = (tolua_ret) ? &tolua_ret->m_nLuaID : NULL;
toluafix_pushusertype_ccobject(tolua_S, nID, pLuaID, (void*)tolua_ret

這也是無奈之舉,第1條雖然用_push_function修改了usertype的push函數名,但傳參列表卻沒有改,為了結合cocos2dx自己的CCObject::nID/LuaID機制,這里只好強行替換函數調用語句,加上提取出這兩個參數。具體這兩個ID的用處也待后面說明。 

 

除了LuaCocos2d.cpp,同目錄下還有幾個膠水文件:

LuaCocoStudio.cp: 與上述一樣,用大量pkg和一個定制lua腳本通過tolua++自動生成

下面這些看起來似乎都是手寫的了,各有各的特殊邏輯要處理(都不是tolua++常規途徑能解決的),就先不一一細說了。

lua_cocos2dx_manual.cpp:

lua_cocos2dx_cocostudio_manual.cpp:

lua_web_socket.cpp:

lua_extensions_CCB.cpp:

CCBProxy.cpp:

 

關於tolua++自身的編譯:有個特別一點的地方,即它先編出一個tolua++_boostrap,用途是將src/bin/lua下的一堆lua文件轉成二進制字節數組的c文件(toluabind.c),然后再加進這個c文件生成最后的tolua++,好處是發布最終程序時,就是一個裸的可執行程序,不需要再攜帶一堆lua腳本了(通常容易帶來各種定位麻煩)。至於那一堆lua文件,除了package.lua是用來做lua文件轉字節數組外,大部份是用來做pkg文件解析的,也就相當於一個微型類c++頭文件解析器了,這一點使我覺得tolua++很蛋疼,因為c++語法解析(即使只是弱化版的頭文件)本來就很復雜,非要自己做,還寫那么大一堆冗長晦澀的lua代碼,不是原作者根本沒法看懂,想對pkg格式做點修改擴展什么的幾乎下不了手,只能把它當一個將就能用的東西用了,所以也才出現了上面cocos2dx對它各種修改替換的結果。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM