为LUA封装C/C++函数(不涉及结构体等参数形式)

四、为LUA封装C/C++函数(不涉及结构体等参数形式)

        由上例中的int _cdecl MyCMax(lua_State* L)函数的实现,可以看出lua调用一个非lua_CFunction类型的函数的过程

(1) 为该函数实现一个lua_CFunction类型的函数(或模板)封装。

(2) 调用LUA库的注册函数以某name注册封装函数

    (3) 封装函数从LUA栈中为原函数(被封装)获取参数。

    (4) 封装函数使用(3)所获取参数call原函数。

    (5) 最后封装函数将原函数的返回值pushLUA栈。

   

        要注册一个lua_CFunction类型的函数funcLUA中去,我们至少需要提供该函数的地址以及注册名。那么,想像中的封装函数最简略的形式看起来可能是这样的:

    这是我们希望的最简略的注册方式(一步到位,那是最好不过的,暂且假设它就是我们要实现的形式)。接下来是处理封装函数,封装函数必须具有lua_CFunction的形式:

        template <typename _Ft> int lua_CFAdapter (lua_State* L);

但是,这就少了具体函数地址的信息。可以用类或结构体(它们能携带一些额外的信息,当然全局变量也可以)来弥补这个缺点(如下面代码中的struct lua_CFAdapter)。下面直接给出基本代码部分,以及最终注册C函数的方式:

上面就是封装的基本框架(指的是我正在做的,而不是专业封装的基本框架),全部完成后可以下面的方式注册C函数(这些函数的参数类型为基本类型和各类指针)

            void MyCPrint(const char* str); 

 lua_fbinder< void MyCPrint(const char* str, MyCPrint)>:: Register (L, "MyCPrint");

(非常遗憾的是要传函数类型,还要直接传递函数名, 如果不考虑重载函数的情况,可以这样处理。lua_fbinder<MyCPrint)>:: Register (L, "MyCPrint",MyCPrint); 这样可做个宏:#define Lua_CFRegister(L, rname, fname)   /

lua_fbinder< fname >:: Register (L, # rname, fname); 

然后,用宏Lua_CFRegister(L, RegisterName, MyCPrint);即可。 止外接下来的实现还存在一个问题:不能注册_stdcall的函数和可变参数的函数,当然在适当的地方加上_stdcall或修改即可,但这要拷贝一份部分代码。)

对于C++函数,由于有重载的情况会出现歧义,因此,需要指明函数类型。

struct lua_fbinder 中,模板参数_Fv是函数地址,每个函数都需要要封装一次,而static int _cdecl lua_CFunction(lua_State* L)也很方便从中获取函数地址

            从调用方式上看,还有比起全手写还有那么一点吸引力(有很多库完成了很好的封装,这里仅是一种自我尝试,娱乐),但是如何封装呢?注意到struct lua_fbinder结构,其静态函数int lua_CFunction ( lua_State *L )可由其获得的信息只有C函数指针_Fv,但是函数参数类型和返回值类型都不十分明确。我们可以通过模板编程来自动匹配,从而获取具体的信息。先看一下封装函数的实现:

                 

                   

       静态函数int lua_CFunction ( lua_State *L )调用了一个函数_call_cfuncion(),这个函数,应该说是函数模板族,它有两个参数,其中第二个就是函数类型,但在模板实现中,模板参数个数不再是单独的_Ft,具体如下:

template <typename _R> inline int _call_cfuncion(lua_State* L, _R (*func)());

template <typename _R, typename _A> inline int _call_cfuncion(lua_State* L, _R (*func)(_A));

//当然不仅这两个,上面只是无参数和一个参数的情况,由于处理相同,只给出两个做示例。

 

函数特征模板集成了函数参数和返回值的特征,也就是映射了C数据类型与LUA数据类型的对应关系,即封装了数据的pushpop操作。

   下面是函数特征模板function_traits的实现(其中数据特征模板data_traits随后给出)

         

 

    数据特征模板data_traits的实现(数据类型映射,并实现存取操作):   

       

这是最后的一个模板_call_cfuncion2_t,也是实际进行栈操作和执行原函数的具体实现,下面是是上面提到的“错误做法”(别外的正确的做法可像上面的按参数个数实现)

这里由于原函数的返回值类型为void时,data_traits没有实现任何操作(也就没有入栈操作),所以需要分情况处理,就有了data_traits模板特化的情况。

最后,要指出的是,这里没有对不定参数的处理,但可以轻松(只是代码不少,copy有点累)地来实现它。

 

参考资料:

(1)Lua 5.1 Reference Manual

(2)Programming in Lua

(3)http://www.cppblog.com/zzxhang/

(4)http://www.cppblog.com/kevinlynx/archive/2008/08/13/58684.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章