lua_tinker浅析

来源:互联网 发布:油烟机 知乎 编辑:程序博客网 时间:2024/05/23 22:04

Lua也用了有段时间了,lua跟c++的交互以前用的是luabind,但是需要boost库,太大了,我的需求也没那么多。所以项目引入lua的时候,我决定找个简单点的库,这时候发现了lua_tinker,很小巧,就两个文件.h和.cpp,引入也很方便。网上看了看教程,我要的功能都有了,就是它了。

用了这么长时间,也没仔细研究研究,也就是机械的用而已,这样可不行。

花了两天时间研究它,下面是一点点心得。

传统的lua跟c交互,主要是利用虚拟栈,但是很不方便,基本也就是写写demo的样子,比如调用lua中的函数

function add(a,b) return a+b end

c代码中调用,就自己把两个参数push进去,然后lua_pcall一下,完事了。可是稍微复杂点的怎么弄,比如传一个对象的指针当参数?

先来分析一下lua/c相互操作的几种模式:

1.      调用lua的全局函数 大概形式 call(“test”,a,b,c) 其中test是lua中的函数名,abc是参数,abc有可能是对象的指针之类的。

2.      如果上面abc有可能是对象的指针,假如是a,那么有可能取到a的成员变量,在lua中是a.x 的形式

3.      同2,有可能调用a的成员函数,在lua中是a:test() 的形式

4.      lua中调用c中的全局函数

 

代码大概是下面这个形式:

a.cppstruct Role{         voidtest(int l)         {                   print(“helloworld”); }          int level;};void sayhello(){         print(“hello”);} aa.luafunction mytest(r,l)         print(r.level);                      //形式 2         r:test(l)                                //形式 3         r.level= l + 1;         sayhello();                           //形式 4end main.cppRole r;call(“mytest”,&r,5)                    //形式 1
 

现在来一个一个分析解决

先处理最简单的

1.      lua调用c函数的

c函数参数有多个,参数类型也不确定,返回值也不知道,而lua规定了注册到lua中的c函数的格式int (*)(lua_State*),那怎么处理呢,引入一个中间层,表面上满足注册的形式,可实际的函数体改调用 R (*)(a,b,c,d) 的形式,这时候就要用到模板,和模板特化技术了,让编译器根据参数的类型自动去匹配调用的函数。

lua_tinker中代码:

template<typename RVal>    void push_functor(lua_State *L, RVal (*func)())    {        lua_pushcclosure(L, functor<RVal>::invoke, 1);    }template<typename RVal, typename T1>    void push_functor(lua_State *L, RVal (*func)(T1))    {        lua_pushcclosure(L, functor<RVal,T1>::invoke, 1);    }template<typename RVal, typename T1, typename T2>    void push_functor(lua_State *L, RVal (*func)(T1,T2))    {        lua_pushcclosure(L, functor<RVal,T1,T2>::invoke, 1);    }

 

          //无参数template<typenameRVal>    struct functor<RVal>    {           static int invoke(lua_State *L) {push(L,upvalue_<RVal(*)()>(L)()); return 1; }    };             //1参数template<typenameRVal, typename T1>    struct functor<RVal,T1>    {           static int invoke(lua_State *L) {push(L,upvalue_<RVal(*)(T1)>(L)(read<T1>(L,1))); return 1; }    };           //2参数template<typenameRVal, typename T1, typename T2>    struct functor<RVal,T1,T2>    {           static int invoke(lua_State *L) {push(L,upvalue_<RVal(*)(T1,T2)>(L)(read<T1>(L,1),read<T2>(L,2)));return 1; }    };   

这时候引发一个新的问题,函数参数怎么让lua明白,这里面有lua/c能直接对应的,主要是lua_pushnumber之类的一系列函数,如果参数是个对象的指针,怎么弄?

其实lua中的table跟c中的class是对应的,比如lua中调用r.x 如果给r设置metatable,让他到元表中就能找到了,而且是共用的,只要是同一种类型的对象。__index如果是函数,lua会拿着table的地址,和元素名去调用该函数。这就好办了,把r设置成对象的指针,然后用class中成员变量的指针 的调用形式 int Role::*px = &Role::x 调用r->*px = 100。而r要能设置元表,就用userdata吧。

封装一个函数push(T x)根据T的不同调用不同的函数,如果是int char float等基本类型,就调用lua_pushnumber之类的吧,如果是对象的指针,这东西以后得查找元表的,所以定义一个结构体ptr2user,把指针保存下来,然后lua_newuserdata(l,sizeof(ptr2user)) 最重要得是得 设置userdata的元表,萃取出T的基本类型,class_add的时候,创建了一个table并且设置了元方法的,setmetatable吧。

这样 r.x 的时候,由于r是个userdata并且设置了元表,他里面没有x这个元素,会去元表中查找,然后就是 meta_get 这个函数干的事情了。


0 0
原创粉丝点击