又一个lua与C++粘合层框架
2013-09-19 11:51
423 查看
背景:
这是之前那篇烂文章的一个扩展吧!在游戏领域,特别多的使用到lua,作为C++的补充,当然会用到lua与C++的交互。lua提供了与C++交互的API,但是这些API各种坑爹、各种坑,各种繁琐,有的API操作了lua栈,有的却没有。为了解决lua原生API的问题,就出现了一些框架、库来改善,比如lua++,luabind…,窃以为,luabind是史上最强大的lua与C++粘合层,无出其右者。但是,他依赖于boost,靠,这就是我不爽他的地方,所以,咱撸起袖子,自造了一个粘合层框架。
2.支持lua5.2及以后版本
目标:
独立,无需第三方库依赖
小巧,仅提供大多数场景的功能需求
易用,接口简单明确
方便,提供完备的错误信息
目录结构:
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.可定制内存分配器,满足allocate、deallocate即可,默认使用std::allocator
2.错误信息丰富,堆栈、参数列表、详情
3.参数转换可扩展,采用模板偏特化方式实现,默认有std::vector\std::pair\std::tuple\std::map\std::string等STL容器类型
4.支持lua中namespace(即module),采用table实现
实现机制:
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、variadictemplate、auto、decltype等等,所以,需要理解C++11,如果你有可能,请加入我们的C++11讨论群165666547。
github:https://github.com/chenyu2202863/lua_reg
这是之前那篇烂文章的一个扩展吧!在游戏领域,特别多的使用到lua,作为C++的补充,当然会用到lua与C++的交互。lua提供了与C++交互的API,但是这些API各种坑爹、各种坑,各种繁琐,有的API操作了lua栈,有的却没有。为了解决lua原生API的问题,就出现了一些框架、库来改善,比如lua++,luabind…,窃以为,luabind是史上最强大的lua与C++粘合层,无出其右者。但是,他依赖于boost,靠,这就是我不爽他的地方,所以,咱撸起袖子,自造了一个粘合层框架。
更新:
1.支持gcc4.7及以后版本2.支持lua5.2及以后版本
目标:
独立,无需第三方库依赖
小巧,仅提供大多数场景的功能需求
易用,接口简单明确
方便,提供完备的错误信息
目录结构:
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.可定制内存分配器,满足allocate、deallocate即可,默认使用std::allocator
2.错误信息丰富,堆栈、参数列表、详情
3.参数转换可扩展,采用模板偏特化方式实现,默认有std::vector\std::pair\std::tuple\std::map\std::string等STL容器类型
4.支持lua中namespace(即module),采用table实现
实现机制:
1.lua参数与C++参数转换
1:template<typenameT,typenameEnableT=void>
2:structconvertion_t;
当需要对lua与C++参数进行转换时,请考虑片特化此类,如void*对应lightuserdata
1:template<>
2:structconvertion_t<void*>
3:{
4:staticvoid*from(state_t&state,intindex)
5:{
6:LUAREG_ERROR(lua_islightuserdata(state,index)!=0,LUA_TLIGHTUSERDATA,index);
7:
8:return::lua_touserdata(state,index);
9:}
10:
11:staticstd::uint32_tto(state_t&state,void*val)
12:{
13:if(val!=nullptr)
14:::lua_pushlightuserdata(state,val);
15:else
16:::lua_pushnil(state);
17:
18:return1;
19:}
20:};
2.C++函数与lua函数对应关系
当需要把C++函数注册到lua,如
1:inttest2(intn,doubled,conststd::string&msg)
2:{
3:return10;
4:}
1:luareg::module(state,"cpp")
2:<<lua::def("test2",&test2);
在lua::def函数里,首先会推导test2的函数签名
1:template<typenameR,typename...Args>
2:inlinedetails::free_function_t<R,Args...>def(constchar*name,R(*func)(Args...))
3:{
4:returndetails::free_function_t<R,Args...>(name,func);
5:}
根据模版参数,得到返回值类型,参数类型,构建一个free_function_t对象
1:template<typenameR,typename...Args>
2:structfree_function_t
3:{
4:constchar*name_;
5:
6:typedefR(*function_t)(Args...);
7:function_tfunction_;
8:
9:free_function_t(constchar*name,function_tfunc)
10::name_(name)
11:,function_(func)
12:{}
13:};
free_function_t对象保存注册名及当前函数指针,通过operator<<操作符把这个free_function_t匿名对象给module的临时匿名对象
1:inlinemodule_tmodule(state_t&state,constchar*name=nullptr)
2:{
3:if(name)
4:assert(std::strlen(name)!=0);
5:returnmodule_t(state,name);
6:}
在module里,提供了operator<<操作符重载
1:template<typenameR,typename...Args>
2:module_t&operator<<(constdetails::free_function_t<R,Args...>&func)
3:{
4:autolambda=[](lua_State*l)->int
5:{
6:state_tstate(l);
7:typedeftypenamedetails::free_function_t<R,Args...>::function_tfunction_t;
8:autofunc=static_cast<function_t>(::lua_touserdata(state,lua_upvalueindex(1)));
9:
10:returndetails::call(state,func);
11:};
12:
13:::lua_pushlightuserdata(state_,func.function_);
14:::lua_pushcclosure(state_,lambda,1);
15:::lua_setfield(state_,-2,func.name_);
16:
17:return*this;
18:}
通过把free_function_t对象的function_\name_注册到lua,这里使用了lua_pushccloure这个API,利用upvalue保存了这个注册函数的指针。这里的lambda是Lua_CFunction的原型,一旦lua调用了这个函数,就会回到这个lambda函数体中,再把函数指针取出来进行调用即可。
再来看看这个details::call
1:template<typenameR,typename...Args>
2:std::int32_tcall(state_t&state,R(*handler)(Args...),
3:typenamestd::enable_if<!std::is_same<R,void>::value>::type*=nullptr)
4:{
5:returnconvertion_t<R>::to(state,call_impl(state,make_obj(handler),0));
6:}
1:template<typenameR,typename...Args>
2:std::int32_tcall(state_t&state,R(*handler)(Args...),
3:typenamestd::enable_if<std::is_same<R,void>::value>::type*=nullptr)
4:{
5:call_impl(state,make_obj(handler),0);
6:
7:return0;
8:}
返回值代表返回多少个数据到lua,通过convertion来完成。这里的enable_if来决断调用的C++函数返回值是否为void,如果为void则返回0个参数到lua。
解析就写到这儿吧,至于call_impl和make_obj请大家自己看源码吧,如果有什么不明白的,请加群探讨165666547
使用示例:
1.对lua内存定制,只需要满足allocate、deallocate接口
1:std::allocator<char>std_allocator;
2:luareg::state_tstate(std_allocator);
2.注册自由函数
1:luareg::module(state,"cpp")
2:<<lua::def("test0",&test0)
3:<<lua::def("test1",&test1)
4:<<lua::def("test2",&test2)
5:<<lua::def("test3",&test3)
6:<<lua::def("test4",&test4)
7:<<lua::def("test5",&test5)
当然,也可以注册类的成员函数,但是并不是由lua提供的userdata作为对象指针,而是由C++保存的指针
1:lua::def("test6",&t,&test_t::test6);
3.注册类
1:luareg::module(state,"cpp")
2:[
3:luareg::class_t<foo_t>(state,"foo_t")
4:<<luareg::constructor<int>()
5:<<luareg::destructor()
6:<<luareg::def("add",&foo_t::add)
7:<<luareg::def("get",&foo_t::get)
8:<<luareg::def("get_pointer",&foo_t::get_pointer)
9:<<luareg::def("get_base",&foo_t::get_base)
10:]
需要注意的是constructor与destructor都不是必须的,如果没有,则采用默认。是不是很像luabind的语法呢?
4.执行lua文件
1:lua::execute(state,"test.lua");
5.执行lua的一个函数
1:try
2:{
3:lua::execute(state,"test2.lua");
4:std::pair<int,std::string>n=lua::call(state,"test_call",1,"haha",10.2,false);
5:
6:autoval=std::make_pair("testabc",10.2);
7:lua::call(state,"test_call2",1,"haha",val);
8:}
9:catch(constluareg::fatal_error_t&e)
10:{
11:std::cout<<e.what()<<std::endl;
12:e.dump(std::cout);
13:}
执行lua的test_call函数,返回两个值,因为lua可以返回多值,所以在C++中可以采用tuple或者pair来接收。其中,错误均已throw异常来处理,当然,debug的时候会有assert及堆栈信息和参数信息。
局限性:
对注册函数均已upvalue的方式来保存,限制了C++导出到lua函数个数(upvalue最大个数为255),不过,我认为这已足够,如果要导出很多接口道lua,那已经是不正常的了
与luabind比起,某些功能不支持(函数重载、导出变量等)
后记:
本框架大量使用C++11特性,使实现非常优雅的解决许多问题,比如lambda、variadictemplate、auto、decltype等等,所以,需要理解C++11,如果你有可能,请加入我们的C++11讨论群165666547。
github:
相关文章推荐
- 又一个lua与C++粘合层框架
- lua与C++粘合层框架
- 介绍一个轻量级的C++ Unit test 框架: TUT
- 一个使用c++在lua中创建自定义数据类型的简易方法
- 函数编译lua笔记 --- 一个C++调用lua函数的类的实现
- VS2010 C++ MFC框架学习笔记2 - 创建一个简单的加法计算器(2)
- 一个轻量级Actor并发框架的c++实现, libgsc(Game Server Communication Library)(一)
- 转载和积累系列 - luci框架-LUA的一个web框架使用
- C++多线程框架(一)--------- new一下就启动一个线程
- 一个无聊男人的疯狂《数据结构与算法分析-C++描述》学习笔记 用C++/lua/python/bash的四重实现(3) 最大子序列和问题
- 基于ACE设计一个c++网络游戏服务器框架引擎
- 初学lua --lua嵌入c++的一个问题(初始化lua出错,版本问题)
- lua笔记 --- 一个C++调用lua函数的类的实现
- cinatra--一个高效易用的c++ http框架
- 推荐:一个写的相当好的介绍C++单元测试框架Google Test (gtest) 教程
- qLibc 对于C C++都是一个很好的框架,提供Tree Hash Stack String I/O File Time等功能
- C++怎么传递一个数组到LUA
- 一个简单的游戏框架:Lua相关
- 一个简单的Unity游戏开发框架(lua自更新)
- Google C++测试框架系列入门篇:第二章 开始一个新项目