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

在解决了传递函数指针的问题之后,让我们来看看调用函数时会有一些什么样的问题。

首先,当我们通过函数指针调用这个函数的时候,由于我们面对的是未知类型的函数,也就是说,我们并不知道参数的个数,参数的类型,还有返回值的类型,所以我们不能直接从lua栈中取得参数,当然,我们可以通过运行时测试栈中的信息来得到lua传递进来的参数的个数和类型,这意味着我们在稍后通过函数指针调用函数时也需要动态的根据参数的个数和类型来决议到正确的函数,这样,除了运行时的成本,cpp提供给我们的强类型检查机制的好处也剩不了多少了,我们需要的是一种静态的编译时的“多态”。

在cpp中,至少有两种方法可以实现这点。最直接简单的是使用函数重载,还有一种是利用模板特化机制。

简单的介绍一下模板特化:

在cpp中,可以针对一个模板函数或者模板类写出一些特化版本,编译器在匹配模板参数时会寻找最合适的一个版本。类似于这样:

templat <typename T>
T foo()
{
T tmp();
return tmp;
}

//提供特化版本
template <>
int foo()
{
return 100;
}

在main()函数中,我们可以显示指定使用哪个版本的foo:

int main(int argc, char **argv)
{
cout << foo<int>() << endl;
return 0;
}

程序将输出100,而不是0,以上代码在 g++中编译通过,由于vc6对于模板的支持不是很好,所以有一些模板的技术在vc6中可能不能编译通过。

所以最好使用重载来解决这个问题,在封装函数调用中,我们首先取得这个函数指针,然后,我们要提供一个Call函数来真正调用这个函数,类似于这样:
//伪代码
int Call(pfn, lua_State *L, int idx)

可是我们并不知道这个函数指针的类型,现在该怎么写呢?别忘记了,我们的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);
}

由重载函数Call调用真正的函数,这样,我们可以使用lua api注册相关的函数,下来我们提供一个注册的函数:

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);
}

再定义相关的注册宏:
#define lua_register_directclosure(L, func) /
lua_pushstring(L, #func);
lua_pushdirectclosure(func, L, 1);
lua_settable(L, LUA_GLOBALINDEX)

现在,假设我们有一个int add(int x, int y)这样的函数,我们可以直接向lua注册:

lua_register_directclosure(L, add);

看,最后使用起来很方便吧,我们再也不用手写那么多的封装调用的代码啦,不过问题还没有完,后面我们还得解决Call函数的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值