最近學(xué)習(xí)ruby語言后,頓時就喜歡上了ruby語言,它的簡潔,優(yōu)美,靈活給我留下了深刻的印象。 之前一直從事游戲服務(wù)器研發(fā)相關(guān)工作,而核心語言是c++和lua, c++是一門編譯型語言,所以運行效率非常高,但缺點是每次代碼的一個小改動都得重新編譯,這大大增加了項目的開發(fā)時間,也不適合需求多變的業(yè)務(wù)環(huán)境,而lua腳本語言正好能解決這種矛盾。 所以, 可以將那些需求多變的業(yè)務(wù)放在腳本中來執(zhí)行, c++提供一些基礎(chǔ)功能供lua調(diào)用,這樣做既能保證運行效率,也能兼顧靈活多變,實乃一舉兩得。(順便澄清一下, 雖然c++和lua這樣的組合經(jīng)常用在游戲開發(fā)中, 但在其他有同樣需求的領(lǐng)域里也能采用這樣的方式進行開發(fā)) 以往需要將c++的函數(shù)或?qū)ο蠓椒▽?dǎo)入到lua腳本的時候,也采用過luaplus, tolua++等開源工具,但給我的感覺是用起來太繁瑣,不簡潔,不方便,因此,我才打算自己寫一套能更方便將c++中的對象方法或函數(shù)導(dǎo)入進lua腳本的解析器。我的思路是通過一個純文本txt文件來描述需要導(dǎo)入的對象方法或普通函數(shù),然后通過對文本的解析來自動生成注冊和綁定的代碼。ruby語言的文本解析能力非常強,而且ruby語言本身設(shè)計得非常靈活,簡潔,同樣的功能如果用python或c++來編寫的話,估計要多出來很多代碼。這個ruby版本的解析器我已經(jīng)編寫完成了,寄存在github上面,地址是:https://github.com/lichuan/lua2cpp ,解析器文件是lua2cpp.rb,描述注冊信息文件是lua2cpp.txt,運行l(wèi)ua2cpp.rb解析器后生成的綁定和注冊代碼文件是lua2cpp.cpp(運行ruby文件之前請確保已安裝ruby解釋器),還有測試的代碼放在test目錄下, 歡迎有需要的同學(xué)pull下來查看,或是修改,如果你有更好的建議或意見,希望你能告訴我,我的qq是:308831759 描述文件lua2cpp.txt的語法非常簡單,比如在c++類定義如下: #include <string> #include <cstring> #include <iostream> #include "lua.hpp" using namespace std; typedef unsigned int uint32; typedef int int32; int32 get_global_id() { return 0; } class Cls_Global { public: Cls_Global(string name) { m_name = name; } string get_name() { return m_name; } private: string m_name; }; namespace ns1 { class Cls1 { public: Cls1(string name) { m_name = name; } string get_1_name() { return m_name; } private: string m_name; }; } namespace ns2 { class Cls2 { public: Cls2(string name) { m_name = name; } string get_2_name() { return m_name; } private: string m_name; }; } 因為函數(shù)get_global_id位于全局命名空間中,所以描述文件如下書寫: _{ int32 get_globa_id() } 只有一個下劃線時,就表進這個塊是全局命名空間,里面可以定義全局命名空間的函數(shù),而類Cls_Global雖然位于全局命名空間, 但描述文件中一個塊要么表求一個命名空間, 要么表示一個類,類在描述文件中有單獨的區(qū)塊, 應(yīng)該按照下面的方式書寫: Cls_Global { (string) string get_name() } 上面的塊中的單獨一行括號表示這個類的構(gòu)造函數(shù), 里面是構(gòu)造函數(shù)的參數(shù)類型, 如果有多個參數(shù), 需要寫成 (int32, string, number)類似這樣的形式。 類Cls1和Cls2都位于各自的命名空間中,這時需要加上命名空間前綴, 如下所示: _ns1.Cls1 { (string) string get_1_name() } _ns2.Cls2 { (string) string get_2_name() } 描述文件還支持指針和引用, 以及垃圾回收的標(biāo)志等語法, 假如有一個c++文件定義如下: namespace dat_ns { struct Data { Data() { id = 0; name = "no name"; } uint32 id; string name; }; } class Test_Lua { public: Test_Lua() { } dat_ns::Data get_data() { return m_data; } dat_ns::Data& get_ref_data() { return m_data; } dat_ns::Data* get_ptr_data_no_gc() { return &m_data; } dat_ns::Data* get_ptr_data_gc() { dat_ns::Data *new_obj = new dat_ns::Data; *new_obj = m_data; return new_obj; } void set_data_id(uint32 id) { m_data.id = id; } void set_data_name(string name) { m_data.name = name; } string get_data_name() { return m_data.name; } uint32 get_data_id() { return m_data.id; } void replace_data(dat_ns::Data &data) { m_data = data; } private: dat_ns::Data m_data; }; 那么如果我要導(dǎo)出Test_Lua這個類給lua腳本呢,應(yīng)該如何做呢? 這個也簡單, 描述文件如下: _dat_ns.Data { () uint32 id set_id : id=uint32 string name set_name : name=string } Test_Lua { () _dat_ns.Data get_data() _dat_ns.Data& get_ref_data() _dat_ns.Data* get_ptr_data_no_gc() _dat_ns.Data*|gc| get_ptr_data_gc() set_data_id(uint32) set_data_name(string) string get_data_name() uint32 get_data_id() replace_data(_dat_ns.Data&) } 如果是返回的是引用類型,就加上一個&符號, 如果是指針就加上一個*符號,如果返回的對象需要在lua進行垃圾回收的時候,釋放掉,就加上|gc|標(biāo)志, 而且|gc|必須放在&或*的后面。 再看一下上面的一個塊: _dat_ns.Data { () uint32 id set_id : id=uint32 string name set_name : name=string } 這里有新的語法了, uint32 id而不是uint32 id(),為什么沒有括號呢,這是因為c++里dat_ns::Data類里沒有id()這個函數(shù),但有時候需要在lua里訪問這個id值,怎么辦呢? 不可能對每個類似Data這種結(jié)構(gòu)體的字段都加上一個訪問函數(shù)吧,那樣也太繁瑣,太不簡潔了,我之所以編寫這個lua2cpp解析器,就是為了達到簡潔,方便。類似這樣的直接訪問字段的導(dǎo)出函數(shù)叫is_get函數(shù),解析器在對uint32 id這一行進行解析的時候,會直接對Data結(jié)構(gòu)體的id字段進行訪問,然后把這個值傳遞到lua中,lua里如果要訪問這個值的話,可以類似這樣的寫法: obj = _dat_ns.Data.new() print(obj:id()) 同樣的道理,上面的描述塊中,還有一行 set_id : id=uint32這種導(dǎo)出函數(shù)叫is_set函數(shù),解析器會生成對結(jié)構(gòu)或類或命名空間中的一個成員直接存儲的代碼,但是這個冒號是干什么用呢? 因為在lua中,obj:id()這個id函數(shù)名稱已經(jīng)占用了,所以必須要取一個不同的名字來表示這個is_set函數(shù),冒號之前的名字是在lua中可以直接訪問的名稱,其實在描述文件中,除了構(gòu)造函數(shù)不能有冒號之外,其他的導(dǎo)出函數(shù)都可以加一個冒號來重新取一個在lua中使用的名字,那么在lua代碼中可以這樣使用is_set函數(shù): obj = _dat_ns.Data.new() obj:set_id(123) print(obj:id()) -----------------------> output: 123 其實,上面講的這些,一部分正好是lua2cpp在github上的tes測試目錄中的內(nèi)容, test目錄下的lua2cpp.txt內(nèi)容如下: _dat_ns.Data { () uint32 id set_id : id=uint32 string name set_name : name=string } Test_Lua { () _dat_ns.Data get_data() _dat_ns.Data& get_ref_data() set_data_id(uint32) set_data_name(string) string get_data_name() uint32 get_data_id() replace_data(_dat_ns.Data&) } 在命令行上執(zhí)行:./lua2cpp.rb, 會生成這個描述文件所對應(yīng)的注冊綁定代碼文件叫l(wèi)ua2cpp.cpp,其內(nèi)容如下: /* author: lichuan qq: 308831759 email: 308831759@qq.com homepage: www.lichuan.me github: https://github.com/lichuan/lua2cpp date: 2013-05-11 desc: this is the binding code between lua and c++ generated by lua2cpp.rb */ static void get_global_table(lua_State *lua_state, const char *nodes_name) { char buf[1024]; strcpy(buf, nodes_name); char *p = buf; const char *q = p; int count = 0; while(*p != 0) { if(*p == '.') { *p = 0; if(count == 0) { lua_getglobal(lua_state, q); if(lua_isnil(lua_state, -1)) { return; } } else { lua_pushstring(lua_state, q); lua_rawget(lua_state, -2); if(lua_isnil(lua_state, -1)) { return; } } q = p + 1; ++count; } ++p; } if(count == 0) { lua_getglobal(lua_state, q); if(lua_isnil(lua_state, -1)) { return; } } else { lua_pushstring(lua_state, q); lua_rawget(lua_state, -2); if(lua_isnil(lua_state, -1)) { return; } } } static void build_global_table(lua_State *lua_state, const char *nodes_name) { char buf[1024]; strcpy(buf, nodes_name); char *p = buf; const char *q = p; int count = 0; while(*p != 0) { if(*p == '.') { *p = 0; if(count == 0) { lua_getglobal(lua_state, q); if(lua_isnil(lua_state, -1)) { lua_newtable(lua_state); lua_pushvalue(lua_state, -1); lua_setglobal(lua_state, q); } } else { lua_pushstring(lua_state, q); lua_rawget(lua_state, -2); if(lua_isnil(lua_state, -1)) { lua_pop(lua_state, 1); lua_pushstring(lua_state, q); lua_newtable(lua_state); lua_pushvalue(lua_state, -1); lua_insert(lua_state, -4); lua_rawset(lua_state, -3); lua_pop(lua_state, 1); } } q = p + 1; ++count; } ++p; } if(count == 0) { lua_getglobal(lua_state, q); if(lua_isnil(lua_state, -1)) { lua_newtable(lua_state); lua_setglobal(lua_state, q); } } else { lua_pushstring(lua_state, q); lua_rawget(lua_state, -2); if(lua_isnil(lua_state, -1)) { lua_pop(lua_state, 1); lua_pushstring(lua_state, q); lua_newtable(lua_state); lua_rawset(lua_state, -3); } } lua_settop(lua_state, 0); } static int lua____dat_ns___Data__new(lua_State *lua_state) { lua_settop(lua_state, 0); uint32 *udata = (uint32*)lua_newuserdata(lua_state, sizeof(uint32) + sizeof(dat_ns::Data*)); uint32 &gc_flag = *udata; gc_flag = 1; /* need gc default in constructor */ udata += 1; *(dat_ns::Data**)udata = new dat_ns::Data(); luaL_setmetatable(lua_state, "_dat_ns.Data"); return 1; } static int lua____dat_ns___Data__id(lua_State *lua_state) { uint32 *udata_self = (uint32*)luaL_checkudata(lua_state, 1, "_dat_ns.Data"); udata_self += 1; dat_ns::Data *obj = *(dat_ns::Data**)udata_self; uint32 v = obj->id; lua_pushunsigned(lua_state, v); return 1; } static int lua____dat_ns___Data__set_id(lua_State *lua_state) { uint32 *udata = (uint32*)luaL_checkudata(lua_state, 1, "_dat_ns.Data"); udata += 1; dat_ns::Data *obj = *(dat_ns::Data**)udata; uint32 arg_1 = luaL_checkunsigned(lua_state, 2); lua_settop(lua_state, 0); obj->id = arg_1; return 0; } static int lua____dat_ns___Data__name(lua_State *lua_state) { uint32 *udata_self = (uint32*)luaL_checkudata(lua_state, 1, "_dat_ns.Data"); udata_self += 1; dat_ns::Data *obj = *(dat_ns::Data**)udata_self; std::string v = obj->name; lua_pushstring(lua_state, v.c_str()); return 1; } static int lua____dat_ns___Data__set_name(lua_State *lua_state) { uint32 *udata = (uint32*)luaL_checkudata(lua_state, 1, "_dat_ns.Data"); udata += 1; dat_ns::Data *obj = *(dat_ns::Data**)udata; const char *arg_1 = luaL_checkstring(lua_state, 2); lua_settop(lua_state, 0); obj->name = arg_1; return 0; } static int lua____dat_ns___Data__garbage_colloect(lua_State *lua_state) { uint32 *udata = (uint32*)luaL_checkudata(lua_state, 1, "_dat_ns.Data"); uint32 &gc_flag = *udata; if(gc_flag == 1) { udata += 1; dat_ns::Data *obj = *(dat_ns::Data**)udata; delete obj; } return 0; } static int lua___Test_Lua__new(lua_State *lua_state) { lua_settop(lua_state, 0); uint32 *udata = (uint32*)lua_newuserdata(lua_state, sizeof(uint32) + sizeof(Test_Lua*)); uint32 &gc_flag = *udata; gc_flag = 1; /* need gc default in constructor */ udata += 1; *(Test_Lua**)udata = new Test_Lua(); luaL_setmetatable(lua_state, "Test_Lua"); return 1; } static int lua___Test_Lua__get_data(lua_State *lua_state) { uint32 *udata_self = (uint32*)luaL_checkudata(lua_state, 1, "Test_Lua"); udata_self += 1; Test_Lua *obj = *(Test_Lua**)udata_self; lua_settop(lua_state, 0); dat_ns::Data *v = new dat_ns::Data; *v = obj->get_data(); uint32 *udata = (uint32*)lua_newuserdata(lua_state, sizeof(uint32) + sizeof(dat_ns::Data*)); uint32 &gc_flag = *udata; gc_flag = 1; /* no ptr, no ref, it's a new obj, so it need gc */ udata += 1; *(dat_ns::Data**)udata = v; luaL_setmetatable(lua_state, "_dat_ns.Data"); return 1; } static int lua___Test_Lua__get_ref_data(lua_State *lua_state) { uint32 *udata_self = (uint32*)luaL_checkudata(lua_state, 1, "Test_Lua"); udata_self += 1; Test_Lua *obj = *(Test_Lua**)udata_self; lua_settop(lua_state, 0); const dat_ns::Data *v = &obj->get_ref_data(); uint32 *udata = (uint32*)lua_newuserdata(lua_state, sizeof(uint32) + sizeof(dat_ns::Data*)); uint32 &gc_flag = *udata; gc_flag = 0; udata += 1; *(dat_ns::Data**)udata = (dat_ns::Data*)v; luaL_setmetatable(lua_state, "_dat_ns.Data"); return 1; } static int lua___Test_Lua__set_data_id(lua_State *lua_state) { uint32 *udata_self = (uint32*)luaL_checkudata(lua_state, 1, "Test_Lua"); udata_self += 1; Test_Lua *obj = *(Test_Lua**)udata_self; uint32 arg_1 = luaL_checkunsigned(lua_state, 2); lua_settop(lua_state, 0); obj->set_data_id(arg_1); return 0; } static int lua___Test_Lua__set_data_name(lua_State *lua_state) { uint32 *udata_self = (uint32*)luaL_checkudata(lua_state, 1, "Test_Lua"); udata_self += 1; Test_Lua *obj = *(Test_Lua**)udata_self; const char *arg_1 = luaL_checkstring(lua_state, 2); lua_settop(lua_state, 0); obj->set_data_name(arg_1); return 0; } static int lua___Test_Lua__get_data_name(lua_State *lua_state) { uint32 *udata_self = (uint32*)luaL_checkudata(lua_state, 1, "Test_Lua"); udata_self += 1; Test_Lua *obj = *(Test_Lua**)udata_self; lua_settop(lua_state, 0); std::string v = obj->get_data_name(); lua_pushstring(lua_state, v.c_str()); return 1; } static int lua___Test_Lua__get_data_id(lua_State *lua_state) { uint32 *udata_self = (uint32*)luaL_checkudata(lua_state, 1, "Test_Lua"); udata_self += 1; Test_Lua *obj = *(Test_Lua**)udata_self; lua_settop(lua_state, 0); uint32 v = obj->get_data_id(); lua_pushunsigned(lua_state, v); return 1; } static int lua___Test_Lua__replace_data(lua_State *lua_state) { uint32 *udata_self = (uint32*)luaL_checkudata(lua_state, 1, "Test_Lua"); udata_self += 1; Test_Lua *obj = *(Test_Lua**)udata_self; uint32 *udata_1 = (uint32*)luaL_checkudata(lua_state, 2, "_dat_ns.Data"); udata_1 += 1; dat_ns::Data *arg_1 = *(dat_ns::Data**)udata_1; lua_settop(lua_state, 0); obj->replace_data(*arg_1); return 0; } static int lua___Test_Lua__garbage_colloect(lua_State *lua_state) { uint32 *udata = (uint32*)luaL_checkudata(lua_state, 1, "Test_Lua"); uint32 &gc_flag = *udata; if(gc_flag == 1) { udata += 1; Test_Lua *obj = *(Test_Lua**)udata; delete obj; } return 0; } static void register_lua(lua_State *lua_state) { /* register non-global namespace */ lua_settop(lua_state, 0); build_global_table(lua_state, "_dat_ns.Data"); get_global_table(lua_state, "_dat_ns.Data"); luaL_newmetatable(lua_state, "_dat_ns.Data"); lua_pushvalue(lua_state, -2); lua_setfield(lua_state, -2, "__index"); lua_settop(lua_state, 0); build_global_table(lua_state, "Test_Lua"); get_global_table(lua_state, "Test_Lua"); luaL_newmetatable(lua_state, "Test_Lua"); lua_pushvalue(lua_state, -2); lua_setfield(lua_state, -2, "__index"); { luaL_Reg _dat_ns_Data[] = { {"new", lua____dat_ns___Data__new}, {"id", lua____dat_ns___Data__id}, {"set_id", lua____dat_ns___Data__set_id}, {"name", lua____dat_ns___Data__name}, {"set_name", lua____dat_ns___Data__set_name}, {"__gc", lua____dat_ns___Data__garbage_colloect}, {NULL, NULL} }; lua_settop(lua_state, 0); get_global_table(lua_state, "_dat_ns.Data"); luaL_setfuncs(lua_state, _dat_ns_Data, 0); } { luaL_Reg Test_Lua[] = { {"new", lua___Test_Lua__new}, {"get_data", lua___Test_Lua__get_data}, {"get_ref_data", lua___Test_Lua__get_ref_data}, {"set_data_id", lua___Test_Lua__set_data_id}, {"set_data_name", lua___Test_Lua__set_data_name}, {"get_data_name", lua___Test_Lua__get_data_name}, {"get_data_id", lua___Test_Lua__get_data_id}, {"replace_data", lua___Test_Lua__replace_data}, {"__gc", lua___Test_Lua__garbage_colloect}, {NULL, NULL} }; lua_settop(lua_state, 0); get_global_table(lua_state, "Test_Lua"); luaL_setfuncs(lua_state, Test_Lua, 0); } }
test目錄下還有一個測試c++文件叫test.cpp, 其內(nèi)容如下: #include <string> #include <cstring> #include <list> #include <map> #include <iostream> #include "lua.hpp" using namespace std; typedef unsigned int uint32; typedef int int32; namespace dat_ns { struct Data { Data() { id = 0; name = "no name"; } uint32 id; string name; }; } class Test_Lua { public: Test_Lua() { } dat_ns::Data get_data() { return m_data; } dat_ns::Data& get_ref_data() { return m_data; } void set_data_id(uint32 id) { m_data.id = id; } void set_data_name(string name) { m_data.name = name; } string get_data_name() { return m_data.name; } uint32 get_data_id() { return m_data.id; } void replace_data(dat_ns::Data &data) { m_data = data; } private: dat_ns::Data m_data; }; #include "lua2cpp.cpp" int main() { lua_State *lua_state = luaL_newstate(); luaL_openlibs(lua_state); register_lua(lua_state); if(luaL_dofile(lua_state, "test.lua") != 0) { cout << "err: " << lua_tostring(lua_state, -1) << endl; } } 看到了嗎, 在test.cpp文件里有一行是 #include "lua2cpp.cpp", 這里就將生成的代碼包含到test.cpp中了,main函數(shù)里,會執(zhí)行test.lua這個lua腳本,編譯好test.cpp后,運行就可以看到執(zhí)行test.lua的輸出了, test.lua的內(nèi)容如下(箭頭后面表示實際的輸出結(jié)果): print "------start lua script--------" dat_obj = _dat_ns.Data.new() print(dat_obj:id()) ---------------------------------------------------------> output: 0 print(dat_obj:name()) -------------------------------------------------------> output: no name dat_obj:set_id(92) dat_obj:set_name("I'm old object") print(dat_obj:id()) ---------------------------------------------------------> output: 92 print(dat_obj:name()) -------------------------------------------------------> output: I'm old object print "" test_lua_obj = Test_Lua.new() print(test_lua_obj:get_data_id()) -------------------------------------------> output: 0 print(test_lua_obj:get_data_name()) -----------------------------------------> output: no name test_lua_obj:replace_data(dat_obj) print(test_lua_obj:get_data_id()) -------------------------------------------> output: 92 print(test_lua_obj:get_data_name()) -----------------------------------------> output: I'm old object print "" tmp_obj = test_lua_obj:get_data() tmp_obj:set_name("I'm tmp object") print(tmp_obj:name()) -------------------------------------------------------> output: I'm tmp object print(test_lua_obj:get_data_name()) -----------------------------------------> output: I'm old object print "" ref_obj = test_lua_obj:get_ref_data() ref_obj:set_name("I'm ref object") print(ref_obj:name()) -------------------------------------------------------> output I'm ref object print(test_lua_obj:get_data_name()) -----------------------------------------> output I'm ref object print "------end lua script-------" 好了,大概介紹到這里了,如果要深入研究,可以去github上查看ruby語言寫成的解析器的源碼,歡迎留言或qq交流。
作者:lcabcdefg 發(fā)表于2013-8-14 20:10:06 原文鏈接
閱讀:25 評論:0 查看評論
如果您喜歡IT技術(shù)或者對IT技術(shù)感興趣,請加入腳本百事通QQ交流群:246889341 請記住永久域名:http://www. |
|