在你的游戏中应用Lua(3):using lua in cpp(注册不同类型的c函数)之三

下面,让我们集中精力来解决Call重载函数的问题吧。

前面已经说过来,Call重载函数接受一个函数指针,然后从lua栈中根据函数指针的类型,取得相关的参数,并调用这个函数,然后将返回值压入lua栈,类似于这样:

//伪代码
int Call(pfn, lua_State *L, int idx)

现在的问题是pfn该如何声明?我们知道这是一个函数指针,然而其参数,以及返回值都是未知的类型,如果我们知道返回值和参数的类型,我们可以用一个typedef来声明它:

typedef void (*pfn)();

int Call(pfn fn, lua_State *L, int idx);

我们知道的返回值以及参数的类型只是一个模板参数T,在cpp中,我们不能这样写:

template <typename T>
typedef T (*Func) ();

一种解决办法是使用类模板:

template <typename T>
struct CallHelper
{
typedef T (*Func) ();
};

然后在Call中引用它:

template <typename T>
int Call(typename CallHelper::Func fn, lua_State *L, int idx)

注意typename关键字,如果没有这个关键字,在g++中会产生一个编译警告,它的意思是告诉编译器,CallHelper::Func是一个类型,而不是变量。

如果我们这样来解决,就需要在CallHelper中为每种情况大量定义各种类型的函数指针,还有一种方法,写法比较古怪,考虑一个函数中参数的声明:

void (int n);

首先是类型,然后是变量,而应用于函数指针上:

typedef void (*pfn) ();
void (pfn fn);

事实上,可以将typedef直接在参数表中写出来:

void (void (*pfn)() );

这样,我们的Call函数可以直接这样写:

//针对没有参数的Call函数
template <typename RT>
int Call(RT (*Func) () , lua_State *L, int idx);
{
//调用Func
RT ret  = (*Func)();

//将返回值交给lua
Push(L, ret);

//告诉lua有多少个返回值
return 1;
}

//针对有一个参数的Call
template <typename T, typename P1>
int Call(RT (*Func)(P1), lua_State *L, int idx)
{
//从lua中取得参数
if (!Match(TypeWrapper<P1>(), L, -1)
return 0;

RT ret = (*Func) (Get(TypeWrapper<P1>(), L, -1));

Push(L, ret);
return 1;
}

按照上面的写法,我们可以提供任意参数个数的Call函数,现在回到最初的时候,我们的函数指针要通过lua_State *L来存储,这只要利用lua提供的api就可以了,还记得我们的lua_pushdirectclosure函数吗:

template <typename Func>
void lua_pushdirectclosure(Func fn, lua_State *L, int nUpvalue)
{
//伪代码,向L存储函数指针
save_pointer(L);

//向lua提供我们的register_proxy函数
lua_pushcclosure(L, register_proxy<Func>, nUpvalue + 1);
}

其中,save_pointer(L)可以这样实现:

void save_pointer(lua_State *L)
{
unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(func));
memcpy(buffer, &func, sizeof(func));
}

而在register_proxy函数中:

template <typename Func>
int register_proxy(lua_State *L)
{
//伪代码,通过L参数取得这个指针
unsigned char *buffer = get_pointer(L);
//对这个指针做强制类型转化,调用Call函数
return Call(*(Func*)buffer, L, 1);
}
get_pointer函数可以这样实现:

unsigned char* get_pointer(lua_State *L)
{
    return (unsigned char*) lua_touserdata(L, lua_upvalueindex(1));
}

这一点能够有效运作主要依赖于这样一个事实:

我们在lua栈中保存这个指针之后,在没有对栈做任何操作的情况下,又把它从栈中取了出来,所以不会弄乱lua栈中的信息,记住,lua栈中的数据是由用户保证来清空的。

到现在,我们已经可以向lua注册任意个参数的c函数了,只需简单的一行代码:

lua_register_directclosure(L, func)就可以啦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值