背景:
这是之前那篇烂文章的一个扩展吧!在游戏领域,特别多的使用到lua,作为C++的补充,当然会用到lua与C++的交互。lua提供了与C++交互的API,但是这些API各种坑爹、各种坑,各种繁琐,有的API操作了lua栈,有的却没有。为了解决lua原生API的问题,就出现了一些框架、库来改善,比如lua++,luabind…,窃以为,luabind是史上最强大的lua与C++粘合层,无出其右者。但是,他依赖于boost,靠,这就是我不爽他的地方,所以,咱撸起袖子,自造了一个粘合层框架。
目标:
-
- 独立,无需第三方库依赖
- 小巧,仅提供大多数场景的功能需求
- 易用,接口简单明确
- 方便,提供完备的错误信息
目录结构:
state.hpp—对lua_state封装,支持对内存定制
reference.hpp—对lua的function、string、table引用,提高性能会用到
module.hpp--支持类似C++中namespace功能,以table方式实现
lua_reg.hpp--头文件包含
iterator.hpp--对不定参数的迭代
execute.hpp--执行lua文件,对lua_pcall封装,支持错误处理
error.hpp--错误处理,提供fatal_error与parameter_error,支持对堆栈内容的解析
converter.hpp--C++数据与lua数据的转换,默认支持C++原生类型、std::string\std::pair\std::tuple\std::map\std::vector
config.hpp--这个没什么好说的
class.hpp--对C++类的支持
call.hpp--lua_pcall封装,C++调用lua函数,支持错误处理
实现机制:
1. lua参数与C++参数转换
当需要对lua与C++参数进行转换时,请考虑片特化此类,如void*对应lightuserdata
2. C++函数与lua函数对应关系
当需要把C++函数注册到lua,如
在lua::def函数里,首先会推导test2的函数签名
根据模版参数,得到返回值类型,参数类型,构建一个free_function_t对象free_function_t对象保存注册名及当前函数指针,通过operator<<操作符把这个free_function_t匿名对象给module的临时匿名对象在module里,提供了operator<<操作符重载通过把free_function_t对象的function_\name_注册到lua,这里使用了lua_pushccloure这个API,利用upvalue保存了这个注册函数的指针。这里的lambda是Lua_CFunction的原型,一旦lua调用了这个函数,就会回到这个lambda函数体中,再把函数指针取出来进行调用即可。
再来看看这个details::call
返回值代表返回多少个数据到lua,通过convertion来完成。这里的enable_if来决断调用的C++函数返回值是否为void,如果为void则返回0个参数到lua。
解析就写到这儿吧,至于call_impl和make_obj请大家自己看源码吧,如果有什么不明白的,请加群探讨165666547
使用示例:
1. 对lua内存定制,只需要满足allocate、deallocate接口
2. 注册自由函数
当然,也可以注册类的成员函数,但是并不是由lua提供的userdata作为对象指针,而是由C++保存的指针
3. 注册类
5. 执行lua的一个函数执行lua的test_call函数,返回两个值,因为lua可以返回多值,所以在C++中可以采用tuple或者pair来接收。其中,错误均已throw异常来处理,当然,debug的时候会有assert及堆栈信息和参数信息。
-
-
对注册函数均已upvalue的方式来保存,限制了C++导出到lua函数个数(upvalue最大个数为255),不过,我认为这已足够,如果要导出很多接口道lua,那已经是不正常的了
-
与luabind比起,某些功能不支持(函数重载、导出变量等)
-
本框架大量使用C++11特性,使实现非常优雅的解决许多问题,比如lambda、variadic template、auto、decltype等等,所以,需要理解C++11,如果你有可能,请加入我们的C++11讨论群165666547。