注册C函数与类成员函数到lua
在lua中调用c函数,我们通常要将c函数转换成相应的注册函数,也就是如下形式
int function(lua_State *L) {}
可是如果我们每个都函数都这么写,既重复了太多的工作量,又容易出错,所以自然想到了用一层代理来连接注册函数与本来的c函数。于是我们可以这样
int function(lua_State *L)
{
//do something
return callfun(); //这里callfun()就调用了我们本来的c函数
}
可是对于callfun我们怎么实现呢。在c语言里面,我们可以使用函数指针来实现我们的函数功能。如对于函数 int myprint(),我们可以声明一个 int(*fn)() = myprint指针,然后通过fu()调用,这样就跟直接调用myprint()一样了,所以我们考虑callfun函数如下:
template <typename RT>
int callfun(RT (*func)(), lua_State *L, int index )
{
RT ret = func(); //调用函数,得到返回值
Push(L, ret); //将返回值压入lua堆栈
return 1;
}
这里实现的是形如RT fun() 的函数调用,对于有多个参数的情况,我们可以定义不同的callfun函数重载实现,如
template <typename RT, typename P1>
int callfun(RT (*func)(P1), ……)
这样定义有虽然实现起来比较简单,但是对于参数较多的情况就比较难以书写了,不过对于一般的函数,参数都不可能太多(如果太多了我想应该就要重写该函数了),所以我们就按照这样实现。
由于考虑到简易说明,声明要处理的函数类型为 int fun(),既参数为0返回值为int。
于是我们可以定义我们的代理注册函数如下
template <typename Func>
int registry_function(lua_State *L);
现在我们需要做的就是怎样把我们的函数指针在代理注册里面传给callfun,这里我们需要用到一些lua的实现方法,当lua在把c函数压入堆栈的时候,会把参数存储到upvalue里面,而我们会把registry_function压入堆栈,所以很自然的我们将把upvalue的第一个值作为函数指针的值。而对于函数指针来说,在lua里面就是一个userdata,所以我们可以这样实现:
template <typename Func>
int registry_function(lua_State *L)
{
//取出函数指针
unsigned char* buffer = (unsigned char*)lua_touserdata(L, lua_upvalueindex(1));
//调用函数
return callFunc(*(Func*)(buffer), L, 1);
}
下面我们的问题就在于怎么样把函数指针放入upvalue中以及将我们的代理注册函数注册进lua,我们定义如下函数:
template <typename Func>
void lua_pushdirectclosure(lua_State *L, Func func, unsigned int nupvalues)
{
//创建userdata并把func指针的值拷贝进去
unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(func));
memcpy(buffer, &func, sizeof(func));
lua_pushcclosure(L, registry_function <Func>, nupvalues + 1);
}
首先我们创建一个userdata并把函数指针赋值给它,然后将registry_function注册给lua,这时由于userdata为第一个参数,所以我们可以在调用registry_function的时候取出使用。在通过call函数调用原本的c函数。
为了考虑到方便实现,我们定义一个宏:
#define lua_directregistry_function(L, func) /
lua_pushstring(L, #func); /
lua_pushdirectclosure(L, func, 0); /
lua_settable(L, LUA_GLOBALSINDEX);
在程序里面,假设我们的函数为 int myprint() {}
则我们在c语言里面使用如下
lua_directregistry_function(L, myprint);
然后在lua里面就可以通过myprint()调用该函数了。
以上是实现的在lua里面对不同类型的c语言函数进行封装调用,其实重点就是通过改函数的函数指针来进行操作,其实对于类里面的成员函数,我们同样可以注册进入lua,然后像一般函数进行调用。
对于类的成员函数,我们需要类的成员函数指针来操作,假设有一个类
class A
{
public:
int test();
};
我们可以这样定义test的函数指针
int (A::*fun)();
fun = &A::test;
使用的时候我们可以这样:
A a;
(a.*fun)();
对以对我们来说,实现类的成员函数注册重点就是操作类的成员函数的函数指针。我们仍然把该函数指针存放到upvalue的第一个值处。
首先是call函数声明,由于有了类,所以如下:
template <typename Cla, typename RT>
int callfunc(Cla &cla, RT (Cla::*func)(), … …)
{
RT ret = (cla.*func)();
//do something
}
然后就是把成员函数指针的值拷入userdata中:
unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(Cla) + sizeof(func));
memcpy(buffer, &cla, sizeof(Cla));
memcpy(buffer + sizeof(Cla), &func, sizeof(func));
而在我们的代理注册函数里面,调用call函数如下:
callfunc(*(Cla*)buffer, *(Func*)(buffer + sizeof(Cla)), L, 1);
由于对于一般的类成员函数来说(静态除外),我们的调用方式是class.function(),所以在userdata中我们需要保存两个值,一个是该类的地址,一个是类的函数的地址。
这样我么就可以把类的成员函数也注册给了lua。
以下为C函数与类成员函数封装代码(为了简便,函数都是 int fun() 形式):
/*
调用真正的C函数,现已int func()作为特例。
参数 func 函数指针,指向参数为返回值为int类型的函数
L lua变量
index lua栈中索引
对于其他的类型,可用模板实现
如对于一个参数的函数,实现如下
template <typname RT, typename P1>
int callFunc(RT (*func)(P1), lua_state *L, int index)
{
//Get 通过index索引得到在lua栈中的值并转换成P1类型
//Push 把函数的返回值压入堆栈
RT ret = func(Get(Type<P1>(), L, index + 0));
Push(L, ret);
return 1;
}
*/
int callFunc(int (*func)(), lua_State *L, int index)
{
int ret = func();
lua_pushnumber(L, ret);
return 1;
}
//函数指针相关数据会存到UpValue的第一个值中,此处从upvalue中取出
unsigned char* getFirstUpValue(lua_State *L)
{
unsigned char* buffer = (unsigned char*)lua_touserdata(L, lua_upvalueindex(1));
return buffer;
}
/*
实现callFunc的lua调用形式封装
*/
template <typename Func>
int directCallFunc(lua_State *L)
{
//得到函数指针
unsigned char* buffer = getFirstUpValue(L);
//转换成相应的函数调用
return callFunc(*(Func*)(buffer), L, 1);
}
/*
将directCallFunc注册进lua
*/
template <typename Func>
void lua_pushdirectclosure(lua_State *L, Func func, unsigned int nupvalues)
{
//创建userdata并把func指针的值拷贝进去
unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(func));
memcpy(buffer, &func, sizeof(func));
lua_pushcclosure(L, directCallFunc<Func>, nupvalues + 1);
}
/*
实现对class里面memeber function的调用
参数cla 要调用的类的实例
Cla::*func 类的函数指针
*/
template <typename Cla>
int callMemFunc(Cla &cla, int (Cla::*func)(), lua_State *L, int index)
{
int ret = (cla.*func)();
lua_pushnumber(L, ret);
return 1;
}
/*
实现callMemFunc的lua调用形式封装
*/
template <typename Cla, typename Func>
int directCallMemFunc(lua_State *L)
{
//得到函数指针
unsigned char* buffer = getFirstUpValue(L);
//转换成相应的函数调用
return callMemFunc(*(Cla*)(buffer), *(Func*)(buffer + sizeof(Cla)), L, 1);
}
/*
将directCallMemFunc注册进lua
*/
template <typename Cla, typename Func>
void lua_pushdirectmemclosure(lua_State *L, Cla &cla, Func func, unsigned int nupvalues)
{
//创建userdata并把cla和func指针的值拷贝进去
unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(Cla) + sizeof(func));
memcpy(buffer, &cla, sizeof(Cla));
memcpy(buffer + sizeof(Cla), &func, sizeof(func));
lua_pushcclosure(L, directCallMemFunc<Cla, Func>, nupvalues + 1);
}
#define lua_directregistry_function(L, func) /
lua_pushstring(L, #func); /
lua_pushdirectclosure(L, func, 0); /
lua_settable(L, LUA_GLOBALSINDEX);
#define lua_directregistry_memfunction(L, name, cla, func) /
lua_pushstring(L, name); /
lua_pushdirectmemclosure(L, cla, func, 0); /
lua_settable(L, LUA_GLOBALSINDEX);
使用的时候我们通过lua_directregistry_function()注册c函数,
通过lua_directregistry_memfunction()注册类成员函数,其中name为该成员函数在lua中使用的函数名。