tolua++
C++最常用的類對(duì)象,它的導(dǎo)入要比函數(shù)導(dǎo)入復(fù)雜許多。
本質(zhì)上C++的對(duì)象是一個(gè)指針, 一般是用UserData包裝,綁定元表信息。
元表用C++類結(jié)構(gòu)建立起來(lái),告訴lua如何讀寫C++對(duì)象。
有個(gè)自動(dòng)導(dǎo)出C++的工具,tolua++。
步驟:
- 寫個(gè)描述文件,和C++頭文件很像
- 運(yùn)行tolua++命令生成cpp代碼,里面有tolua_module_open的注冊(cè)函數(shù)
- 在Lua引擎初始化后,調(diào)用tolua_module_open注入。
$#include"hello.h"
namespace hello{
void show();
class MyClass
{
public:
MyClass();
~MyClass();
void DoSomething();
};
}
/* Open function */
TOLUA_API int tolua_hello_open (lua_State* tolua_S)
{
tolua_open(tolua_S);
tolua_reg_types(tolua_S);
tolua_module(tolua_S,NULL,0);
tolua_beginmodule(tolua_S,NULL);
tolua_module(tolua_S,"hello",0);
tolua_beginmodule(tolua_S,"hello");
tolua_function(tolua_S,"show",tolua_hello_hello_show00);
#ifdef __cplusplus
tolua_cclass(tolua_S,"MyClass","hello::MyClass","",tolua_collect_hello__MyClass);
#else
tolua_cclass(tolua_S,"MyClass","hello::MyClass","",NULL);
#endif
tolua_beginmodule(tolua_S,"MyClass");
tolua_function(tolua_S,"new",tolua_hello_hello_MyClass_new00);
tolua_function(tolua_S,"new_local",tolua_hello_hello_MyClass_new00_local);
tolua_function(tolua_S,".call",tolua_hello_hello_MyClass_new00_local);
tolua_function(tolua_S,"delete",tolua_hello_hello_MyClass_delete00);
tolua_function(tolua_S,"DoSomething",tolua_hello_hello_MyClass_DoSomething00);
tolua_endmodule(tolua_S);
tolua_endmodule(tolua_S);
tolua_endmodule(tolua_S);
return 1;
}
C++集成Lua的麻煩
C++集成lua有不少坑點(diǎn)。
- 內(nèi)存管理【這是最麻煩的地方】
lua有自動(dòng)垃圾回收,C++要手動(dòng)管理。 - 常用類型的導(dǎo)出
c++的基本類型和模版類型非常多,lua通常只支持double和Lua.Table 常用的int64,vector,map的處理就那么順心了。 - 繼承體系
這個(gè)反而簡(jiǎn)單了,C++的多繼承體系很容易可以用lua的元表模擬起來(lái)。 - 遞歸調(diào)用、異常、協(xié)程
難免出現(xiàn)這種情況,C++ -> Lua -> C++ -> Lua。中間發(fā)生異常如何處理?協(xié)程中斷如何處理?一不小心就程序狀態(tài)不對(duì),內(nèi)存泄漏啥的。
Lua管理C++對(duì)象
實(shí)際也就是Lua管理C++的指針和內(nèi)存。
兩個(gè)簡(jiǎn)單情況:
- Lua完全負(fù)責(zé)C++對(duì)象的生命周期
這時(shí)需要把C++對(duì)象包裝成UserData,在元表里加入__gc的方法,lua垃圾回收時(shí)就可以自動(dòng)回收函數(shù)了。
也可以在元表里導(dǎo)入new和delete方法,lua代碼里調(diào)用。
在tolua++里,如果導(dǎo)入了構(gòu)造函數(shù)和析構(gòu)函數(shù),就有三個(gè)元表鍵值new,delete,new_local。 - Lua運(yùn)行期間C++對(duì)象一直有效
如C++靜態(tài)變量,導(dǎo)出到lua里時(shí)不需要負(fù)責(zé)內(nèi)存回收的,這時(shí)最好使用lightuserdata,就保存?zhèn)€指針到lua里。
C++管理Lua對(duì)象
這是有一種復(fù)雜情況,C++和Lua里都可以創(chuàng)建和釋放對(duì)象。
這時(shí)的核心是一方操作要通知另一方。創(chuàng)建和釋放都不能簡(jiǎn)單的處理了。
一般是維護(hù)一個(gè)表,記錄對(duì)象,釋放時(shí)修改下表,來(lái)通知對(duì)方。LuaL_ref就是為此設(shè)計(jì)的。
具體實(shí)現(xiàn)上有一個(gè)大的分歧:
- C++和Lua都可以刪除對(duì)象,一方刪除需要通知另一方。
- C++刪除通知Lua,這個(gè)相對(duì)好處理些,lua里可以做到一個(gè)C++對(duì)象只有一個(gè)UserData。C++可以獲取到這個(gè)UserData,然后清空指針。
- Lua刪除通知C++,這個(gè)就不好辦,Lua是不可能知道C++哪兒保存了對(duì)象的指針的。最常見的方法是使用引用計(jì)數(shù)了。
- C++和Lua不都可以刪除對(duì)象
- 只有一方可以刪除。一般是C++刪除,Lua可以判斷是否被刪了。
- 引用計(jì)數(shù)。小缺點(diǎn)是引用計(jì)數(shù)維護(hù)不好就慘了。
關(guān)于對(duì)象內(nèi)存管理的小結(jié)
實(shí)際使用中三種情況比較常見
- Lua完全管理C++對(duì)象,Lua里創(chuàng)建刪除,C++層不保存指針。
- C++完全管理對(duì)象,刪除時(shí)會(huì)通知Lua,Lua里可以判斷對(duì)象是否還有效。Lua只使用,創(chuàng)建對(duì)象也是通過(guò)C++層特殊處理。
- C++獲取保存Lua的表和閉包回調(diào)。對(duì)象的生命周期是lua負(fù)責(zé),但是C++會(huì)設(shè)置下,讓Lua不能把用著的對(duì)象給回收了。
- 同樣也可以C++層維護(hù)引用計(jì)數(shù),lua里操作引用計(jì)數(shù),創(chuàng)建UserData時(shí)加一,回收時(shí)減一。
tolua++的實(shí)現(xiàn)里,是Lua可以選擇是否管理C++對(duì)象的生命周期,有兩個(gè)典型的方法takeownership和releaseownership。比較弱啊。
Cocos2dx-lua對(duì)tolua++有不少改動(dòng)。
主要參考:
http://blog.csdn.net/wtyqm/article/details/8977975
http://blog.csdn.net/wtyqm/article/details/9106137