lua绑定C++对象学习
来源:互联网 发布:民生证券mac版 编辑:程序博客网 时间:2024/06/05 14:13
1 extern "C" 2 { 3 #include <lua.h> 4 #include <lualib.h> 5 #include <lauxlib.h> 6 } 7 #include <stdio.h> 8 #include "luna.h" 9 10 11 class LuaTest12 {13 public:14 LuaTest() {}15 LuaTest(lua_State* L) {}16 17 static const char className[];18 static Luna<LuaTest>::RegType methods[];19 20 public:21 int TestString(lua_State* L) { printf("hello!\n"); return 0; }22 };23 24 const char LuaTest::className[] = "LuaTest";25 // Define the methods we will expose to Lua26 #define method(class, name) {#name, &class::name}27 Luna<LuaTest>::RegType LuaTest::methods[] =28 {29 method(LuaTest, TestString),30 {0,0}31 };32 33 int main()34 {35 lua_State* L = luaL_newstate();36 luaL_openlibs(L);37 38 Luna<LuaTest>::Register(L);39 luaL_dofile(L, "MyTest.lua");40 41 lua_close(L);42 43 44 45 return 0;46 }
MyTest.lua如下:
test = LuaTest()test:TestString()
运行结果:
可以看到,luna使用是比较MISS法则的,让我比较不习惯的是对象不是在宿主程序里创建的,而是在lua脚本里.大家都知道lua的C函数导出是很简单的,直接luaL_register()就行,那么对象导出是怎么实现的呢?打开luna.h,发现其不过百来行,关键是Luna类的Register这个方法.学习了lua创始人的书后,咱底气足足的,就来剖析一下它的实现吧:
1 #ifndef _luna_h_ 2 #define _luna_h_ 3 /** 4 * Taken directly from http://lua-users.org/wiki 5 */ 6 7 /* Lua */ 8 extern "C" { 9 #include <lua.h> 10 #include <lauxlib.h> 11 #include <lualib.h> 12 } 13 14 template <typename T> class Luna { 15 typedef struct { T *pT; } userdataType; 16 public: 17 typedef int (T::*mfp)(lua_State *L); 18 typedef struct { const char *name; mfp mfunc; } RegType; 19 20 static void Register(lua_State *L) { 21 //新建一个table,methods保存其栈索引,这个方法表就是用来保存要导出的成员函数 22 lua_newtable(L); 23 int methods = lua_gettop(L); 24 //在注册表中新建一个元表,metatable保存其栈索引. 25 //元表是模拟面向对象机制的关键.后面将会把该元表赋予fulluserdata(C++对象在lua中的映射对象). 26 luaL_newmetatable(L, T::className); 27 int metatable = lua_gettop(L); 28 29 //全局表[T::className]=methods表 30 lua_pushstring(L, T::className); 31 lua_pushvalue(L, methods); 32 lua_settable(L, LUA_GLOBALSINDEX); 33 34 //设置metatable元表的__metatable元事件 35 //作用是将元表封装起来,防止外部的获取和修改 36 lua_pushliteral(L, "__metatable"); 37 lua_pushvalue(L, methods); 38 lua_settable(L, metatable); // hide metatable from Lua getmetatable() 39 40 //设置metatable元表的__index元事件指向methods表 41 //该事件会在元表onwer被索引不存在成员时触发,这时就会去methods表中进行索引... 42 lua_pushliteral(L, "__index"); 43 lua_pushvalue(L, methods); 44 lua_settable(L, metatable); 45 46 //设置元表的__tostring和__gc元事件 47 //前者是为了支持print(MyObj)这样的用法.. 48 //后者是设置我们的lua对象被垃圾回收时的一个回调. 49 lua_pushliteral(L, "__tostring"); 50 lua_pushcfunction(L, tostring_T); 51 lua_settable(L, metatable); 52 53 lua_pushliteral(L, "__gc"); 54 lua_pushcfunction(L, gc_T); 55 lua_settable(L, metatable); 56 57 //下面一段代码干了这么些事: 58 //1.创建方法表的元表mt 59 //2.方法表.new = new_T 60 //3.设置mt的__call元事件.该事件会在lua执行到a()这样的函数调用形式时触发. 61 //这使得我们可以重写该事件使得能对table进行调用...如t() 62 lua_newtable(L); // mt for method table 63 int mt = lua_gettop(L); 64 lua_pushliteral(L, "__call"); 65 lua_pushcfunction(L, new_T); 66 lua_pushliteral(L, "new"); 67 lua_pushvalue(L, -2); // dup new_T function 68 lua_settable(L, methods); // add new_T to method table 69 lua_settable(L, mt); // mt.__call = new_T 70 lua_setmetatable(L, methods); 71 72 // fill method table with methods from class T 73 for (RegType *l = T::methods; l->name; l++) { 74 lua_pushstring(L, l->name); 75 lua_pushlightuserdata(L, (void*)l); 76 lua_pushcclosure(L, thunk, 1); //创建闭包,附带数据为RegType项 77 lua_settable(L, methods); //方法表[l->name]=闭包 78 } 79 80 //平衡栈 81 lua_pop(L, 2); // drop metatable and method table 82 } 83 84 // get userdata from Lua stack and return pointer to T object 85 static T *check(lua_State *L, int narg) { 86 userdataType *ud = 87 static_cast<userdataType*>(luaL_checkudata(L, narg, T::className)); 88 if(!ud) luaL_typerror(L, narg, T::className); 89 return ud->pT; // pointer to T object 90 } 91 92 private: 93 Luna(); // hide default constructor 94 95 // member function dispatcher 96 static int thunk(lua_State *L) { 97 // stack has userdata, followed by method args 98 T *obj = check(L, 1); // get 'self', or if you prefer, 'this' 99 lua_remove(L, 1); // remove self so member function args start at index 1100 // get member function from upvalue101 RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));102 return (obj->*(l->mfunc))(L); // call member function103 }104 105 // create a new T object and106 // push onto the Lua stack a userdata containing a pointer to T object107 static int new_T(lua_State *L) {108 lua_remove(L, 1); // use classname:new(), instead of classname.new()109 T *obj = new T(L); // call constructor for T objects110 userdataType *ud =111 static_cast<userdataType*>(lua_newuserdata(L, sizeof(userdataType)));112 ud->pT = obj; // store pointer to object in userdata113 luaL_getmetatable(L, T::className); // lookup metatable in Lua registry114 lua_setmetatable(L, -2);115 return 1; // userdata containing pointer to T object116 }117 118 // garbage collection metamethod119 static int gc_T(lua_State *L) {120 userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));121 T *obj = ud->pT;122 delete obj; // call destructor for T objects123 return 0;124 }125 126 static int tostring_T (lua_State *L) {127 char buff[32];128 userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));129 T *obj = ud->pT;130 sprintf(buff, "%p", obj);131 lua_pushfstring(L, "%s (%s)", T::className, buff);132 return 1;133 }134 };135 #endif
读了<<Programing In Lua>>后,这里的语义理解是木有问题的.现在只需走一遍程序执行流程,就能把一切串起来了.
(1)在宿主程序中,执行了这句:
Luna<LuaTest>::Register(L);
这是静态的准备工作,我们继续往下看
(2)开始执行脚本:
luaL_dofile(L, "MyTest.lua");
脚本第一句为:
test = LuaTest()
执行LuaTest(),这会导致什么发生呢?一切必有源头,查看Luna::Register()中的这几句:
1 //全局表[T::className]=methods表2 lua_pushstring(L, T::className);3 lua_pushvalue(L, methods);4 lua_settable(L, LUA_GLOBALSINDEX);
推出:LuaTest() => methods table() .而对一个表使用调用语法,导致其元表的__call事件被触发,于是 => new_T函数就被执行了...
(3)new_T()中,new了C++对象,并创建了lua中对应的映射对象(fulluserdata),然后从注册表中取出元表与lua对象绑定(可以看出,所有的对象是共享一个元表的).最后返回lua对象.
回过神来体会脚本这句代码 test = LuaTest(),竟然语如其意,同时构造了C++对象和lua对象.
(4)执行脚本第二句:
test:TestString()
这只是一个语法糖,等价于
test.TestString(test)
这导致test对象被索引"TestString"方法,当然它作为一个userdata是没有key的,故而触发其元表的__index事件,然后在methods table中搜索到了"TestString"项,最终调用其闭包thunk.
(5)执行到闭包thunk了.这里有一个传入参数,位于栈索引1,即test对象自身.先进行了类型检查check(),即检查test对象元表的名称是否等于T::className,不等则报错.这是为了检查出不正确的对象方法调用,如用A类对象去调用B类对象的方法..接下来几句代码妥妥的了,取出闭包附带数据RegType,调用C++对象成员函数TestString()...
(6)脚本执行结束,lua对象被垃圾回收,其__gc元事件被触发,gc_T()调用,C++对象被delete.
(7)程序结束... 水落石出,源码面前,了无秘密啊.
额外生成了这么一些数据结构,性能损失相对原生C++调用肯定是有的,具体是怎样的现在没必要去追究啦:
搞清楚了最简单的对象绑定实现后,可以选择看看更复杂的或者尝试在自己的作品中加入一点好玩的脚本.
<<游戏编程精粹>>5,6中都有关于lua与脚本支持的相关文章.
- lua绑定C++对象学习
- lua绑定C++对象学习
- LuaBridge:Lua绑定C/C++对象
- 为 Lua 绑定 C/C++ 对象
- 用LuaBridge为Lua绑定C/C++对象
- 用LuaBridge为Lua绑定C/C++对象
- 用LuaBridge为Lua绑定C/C++对象
- 用LuaBridge为Lua绑定C/C++对象
- C++对象绑定到Lua
- Lua轻量级绑定C++对象
- C++对象绑定到Lua
- 把C++对象绑定到Lua
- 使用LUA userdata手动绑定C++对象
- 使用LUA userdata手动绑定C++对象
- lua面向对象学习
- 备忘[cocos lua 绑定c方法] 3.10
- cocos2dx-3.7 lua绑定部分学习记录
- lua学习 lua,c++对象调用流程
- Android 2.3 SD卡挂载流程浅析(六)
- 使用接口还是基类
- C# 制作Com组件:java调用.net DLL的方法
- linux内存管理-20121217
- 无刷新分页前端代码
- lua绑定C++对象学习
- Programming with ATL
- Android设备10大不应忽略的功能
- 编辑距离
- linux 下获取当前网络连接状态的两种方法
- android 之 SQLite
- KMP算法的next详解
- SQL Server高可用——日志传送(4-4)——后续问题补充
- WIN7 USB每次启动后都需要手动禁用再启用解决方法