标签:path 接收 重复 函数 state val 转换 ons 工作
摘要:
在使用C++做服务器开发中,经常会使用到脚本技术,Lua是最优秀的嵌入式脚本之一。Lua的轻量、小巧、概念之简单,都使他变得越来越受欢迎。本人也使用过python做嵌入式脚本,二者各有特点,关于python之后会写相关的文章,python对于我而言更喜欢用来编写工具,我前边一些相关的算法也是用python来实现的。今天主要讲Lua相关的开发技术。Lua具有如下特点:
嵌入Lua:
嵌入lua脚本,必须要把lua脚本载入lua虚拟机,lua中的概念称之为dofile,FFLUA中封装了dofile的操作,由于lua文件可能集中放在某个目录,FFLUA中也提供了设置lua脚本目录的接口:
int add_package_path(const string& str_) int load_file(const string& file_name_) throw (lua_exception_t)
load_file就是执行dofile操作,若出错,则throw异常对象,可以使用exception引用目标对象使用what接口输出代码出错的traceback。
当嵌入lua时,最简单的情况是把lua脚本当成配置使用,那么需要获取lua脚本中的变量和设置lua变量,FFLUA封装了两个接口用于此操作。lua是动态语言,变量可以被赋值为任何lua支持的类型,但C++是强类型的,所以两个接口都是范型的:
template<typenameT> int get_global_variable(conststring& field_name_, T& ret_); template<typenameT> int get_global_variable(constchar* field_name_, T& ret_);
有时需要直接执行一些lua语句,lua中有dostring的概念,FFLUA中封装了单独的接口run_string:
void run_string(constchar* str_) throw (lua_exception_t)
嵌入lua时最一般的情况是调用lua中的函数,lua的函数比C++更灵活,可以支持任意多个参数,若未赋值,自动设置为nil,并且可以返回多个返回值。无论如何,从C++角度讲,当你嵌入lua调用lua函数时,你总希望lua的使用方式跟C++越像越好,你不希望繁复的处理调用函数的参数问题,比如C++数据转换成lua能处理的数据,即无趣又容易出错。正也正是FFLUA需要做到,封装调用lua函数的操作,把赋值参数,调用函数,接收返回值的细节做到透明,C++调用者就像调用普通的C++函数一样。使用FFLUA中调用lua函数使用call接口:
void call(constchar* func_name_) throw (lua_exception_t)
当调用出错时,异常信息记录了traceback。
实际上,FFLUA重载了9个call函数,以来自动适配调用9个参数的lua函数。
template<typename RET> RET call(const char* func_name_) throw (lua_exception_t); ...... template<typename RET, typename ARG1, typename ARG2, typename ARG3, typename ARG4, typename ARG5, typename ARG6, typename ARG7, typename ARG8, typename ARG9> RET call(const char* func_name_, ARG1 arg1_, ARG2 arg2_, ARG3 arg3_, ARG4 arg4_, ARG5 arg5_, ARG6 arg6_, ARG7 arg7_, ARG8 arg8_, ARG9 arg9_) throw (lua_exception_t);
需要注明的是:
扩展LUA:
这也是非常重要的操作,嵌入lua总是和扩展lua相伴相行。lua若要操作C++中的对象或函数,那么必须先把C++对应的接口注册都lua中。Lua CAPI提供了一系列的接口拥有完成此操作,但是关于堆栈的操作总是会稍显头疼,fflua极大的简化了注册C++对象和接口的操作,可以说是最简单的注册方式之一(如果不准说最的话)。首先我们整理一下需要哪些注册操作:
FFLUA中提供了一个范型接口,适配于注册C++相关数据:
template<typename T> void fflua_t::reg(T a) { a(this->get_lua_state()); }
这样,若要对lua进行注册操作,只需要提供一个仿函数即可,这样可以批量在注册所有的C++数据,当然FFLUA中提供了工具类用于生成仿函数中应该完成的注册操作:
template<typename CLASS_TYPE = op_tool_t, typename CTOR_TYPE = void()> class fflua_register_t { public: fflua_register_t(lua_State* ls_):m_ls(ls_){} fflua_register_t(lua_State* ls_, const string& class_name_, string inherit_name_ = ""); template<typename FUNC_TYPE> fflua_register_t& def(FUNC_TYPE func, const string& s_) { fflua_register_router_t<FUNC_TYPE>::call(this, func, s_); return *this; } };
刚才提到的像lua中的所有注册操作,都可以使用def操作完成。 示例如下:
//! 注册子类,ctor(int) 为构造函数, foo_t为类型名称, base_t为继承的基类名称 fflua_register_t<foo_t, ctor(int)>(ls, "foo_t", "base_t") .def(&foo_t::print, "print") //! 子类的函数 .def(&foo_t::a, "a"); //! 子类的字段
尤其特别的是,C++中的继承可以在注册到lua中被保持这样注册过基类的接口,子类就不需要重复注册。
高级特性:
通过以上的介绍,也许你已经了解了FFLUA的设计原则,即:当在编写C++代码时,希望使用LUA就像使用C++本地的代码一样,而在lua中操作C++的数据和接口的时候,又希望C++用起来完全跟table一个样。这样可以大大减轻程序开发的工作,从而把精力更多放大设计和逻辑上。那么做到如何lua才算像C++,C++做到如何才算像lua呢?我们知道二者毕竟相差甚远,我们只需要把常见的操作封装成一直即可,不常见操作则特殊处理。常见操作有:
以上前两者已经介绍了,而后三者FFLUA也是给予 完美支持。通过范型的C++封装,可以将C++ STL完美的转换成luatable,同时在lua返回table的时候,自动根据返回值类型将lua的table转换成C++ STL。FFLUA中只要被注册过的C++对象,都可以把其指针作为参数赋值给lua,甚至在lua中保存。当我讲述以上特性的时候,都是在保证类型安全的前提下。重要的类型检查有:
关于重载:
关于重载LUA 可以使用lua中内部自己的reload,也可以将fflua对象销毁后,重先创建一个,创建fflua对象的开销和创建lua虚拟机的开销一直,不会有附加开销。
总结:
完整的C++示例代码:
#include <iostream> #include <string> #include <assert.h> using namespace std; #include "lua/fflua.h" using namespace ff; class base_t { public: base_t():v(789){} void dump() { printf("in %s a:%d\n", __FUNCTION__, v); } int v; }; class foo_t: public base_t { public: foo_t(int b):a(b) { printf("in %s b:%d this=%p\n", __FUNCTION__, b, this); } ~foo_t() { printf("in %s\n", __FUNCTION__); } void print(int64_t a, base_t* p) const { printf("in foo_t::print a:%ld p:%p\n", (long)a, p); } static void dumy() { printf("in %s\n", __FUNCTION__); } int a; }; //! lua talbe 可以自动转换为stl 对象 void dumy(map<string, string> ret, vector<int> a, list<string> b, set<int64_t> c) { printf("in %s begin ------------\n", __FUNCTION__); for (map<string, string>::iterator it = ret.begin(); it != ret.end(); ++it) { printf("map:%s, val:%s:\n", it->first.c_str(), it->second.c_str()); } printf("in %s end ------------\n", __FUNCTION__); } static void lua_reg(lua_State* ls) { //! 注册基类函数, ctor() 为构造函数的类型 fflua_register_t<base_t, ctor()>(ls, "base_t") //! 注册构造函数 .def(&base_t::dump, "dump") //! 注册基类的函数 .def(&base_t::v, "v"); //! 注册基类的属性 //! 注册子类,ctor(int) 为构造函数, foo_t为类型名称, base_t为继承的基类名称 fflua_register_t<foo_t, ctor(int)>(ls, "foo_t", "base_t") .def(&foo_t::print, "print") //! 子类的函数 .def(&foo_t::a, "a"); //! 子类的字段 fflua_register_t<>(ls) .def(&dumy, "dumy"); //! 注册静态函数 } int main(int argc, char* argv[]) { fflua_t fflua; try { //! 注册C++ 对象到lua中 fflua.reg(lua_reg); //! 载入lua文件 fflua.add_package_path("./"); fflua.load_file("test.lua"); //! 获取全局变量 int var = 0; assert(0 == fflua.get_global_variable("test_var", var)); //! 设置全局变量 assert(0 == fflua.set_global_variable("test_var", ++var)); //! 执行lua 语句 fflua.run_string("print(\"exe run_string!!\")"); //! 调用lua函数, 基本类型作为参数 int32_t arg1 = 1; float arg2 = 2; double arg3 = 3; string arg4 = "4"; fflua.call<bool>("test_func", arg1, arg2, arg3, arg4); //! 调用lua函数,stl类型作为参数, 自动转换为lua talbe vector<int> vec; vec.push_back(100); list<float> lt; lt.push_back(99.99); set<string> st; st.insert("OhNIce"); map<string, int> mp; mp["key"] = 200; fflua.call<string>("test_stl", vec, lt, st, mp); //! 调用lua 函数返回 talbe,自动转换为stl结构 vec = fflua.call<vector<int> >("test_return_stl_vector"); lt = fflua.call<list<float> >("test_return_stl_list"); st = fflua.call<set<string> >("test_return_stl_set"); mp = fflua.call<map<string, int> >("test_return_stl_map"); //! 调用lua函数,c++ 对象作为参数, foo_t 必须被注册过 foo_t* foo_ptr = new foo_t(456); fflua.call<bool>("test_object", foo_ptr); //! 调用lua函数,c++ 对象作为返回值, foo_t 必须被注册过 assert(foo_ptr == fflua.call<foo_t*>("test_ret_object", foo_ptr)); //! 调用lua函数,c++ 对象作为返回值, 自动转换为基类 base_t* base_ptr = fflua.call<base_t*>("test_ret_base_object", foo_ptr); assert(base_ptr == foo_ptr); } catch (exception& e) { printf("exception:%s\n", e.what()); } return 0; }
test_var = 99 function dump_table(tb, str) if nil == str then str = "" end for k, v in pairs(tb) do print(str, k, v) end end -- 测试调用lua function test_func(arg1, arg2, arg3, arg4) print("in test_func:", arg1, arg2, arg3, arg4) mp = {["k"] = "v"} vc = {1,2,3} lt = {4,5,6} st = {7,8,9} dumy(mp, vc, lt, st) end -- 接受stl参数 function test_stl(vec, lt, st, mp) print("--------------dump_table begin ----------------") dump_table(vec, "vec") dump_table(lt, "lt") dump_table(st, "st") dump_table(mp, "mp") print("--------------dump_table end ----------------") return "ok" end -- 返回stl 参数 function test_return_stl_vector() return {1,2,3,4} end function test_return_stl_list() return {1,2,3,4} end function test_return_stl_set() return {1,2,3,4} end function test_return_stl_map() return { ["key"] = 124 } end -- 测试接受C++对象 function test_object(foo_obj) --测试构造 base = base_t:new() -- 每个对象都有一个get_pointer获取指针 print("base ptr:", base:get_pointer()) -- 测试C++对象函数 foo_obj:print(12333, base) base:delete() --基类的函数 foo_obj:dump() -- 测试C++ 对象属性 print("foo property", foo_obj.a) print("base property", foo_obj.v) end -- 测试返回C++对象 function test_ret_object(foo_obj) return foo_obj end -- 测试返回C++对象 function test_ret_base_object(foo_obj) return foo_obj end
FFLIB之FFLUA——C++嵌入Lua&扩展Lua利器[转]
标签:path 接收 重复 函数 state val 转换 ons 工作
原文地址:http://www.cnblogs.com/shuanger/p/6821263.html