Lua和so
实际在应用开发过程中,会用到很多第三方的库;Lua由于其易嵌入的特性,不仅可以使用Lua编写的库,也可以将C++的库进行二次封装供Lua调用。这里我们以实现在Lua中解析xml文件格式场景,结合C++编写的“tinyxml2” 这个库为例进行讲解:
库的准备及编译
第一步,下载“tinyxml2”的源码
下载链接:leethomason/tinyxml2: TinyXML2 is a simple, small, efficient, C++ XML parser that can be easily integrated into other programs. (github.com);
下载解压之后,主要就是三个文件:tinyxml2.cpp、tinyxml2.h;非常的轻量,先编译一波,看是否能编译通过
第二步,如果对Lua版本有特殊要求的话,可以去Lua官网下载指定版本的lua源码
下载链接:Lua: download area
下载好源码之后,直接make就有liblua.a
库的封装及so的生成
有了“lua”和“tinyxml”的库之后,我们根据需求,基于“tinyxml”对其进行二次封装,这里我们想做的是把“xml”每个一级结构及其字段名和字段类型存入到全局表中;那么该怎么做呢?
假设我们的封装的so名字叫“libtinyxmllua.so”,我们得先定义接口“luaopen_libtinyxmllua”,并在“luaopen_libtinyxmllua”中将需要暴露的接口注册到全局表中,如下:
extern int luaopen_libtinyxmllua(lua_State *L) { lua_register(L,"LuaXML_ParseFile",LuaXML_ParseFile); return 1; }
然后实现接口“LuaXML_ParseFile”,这个接口的定义需要保证返回值是int且唯一形参为“lua_State *L”,如下:
extern int LuaXML_ParseFile (lua_State *L) { const char* sFileName = luaL_checkstring(L,1); XMLDocument doc; doc.LoadFile(sFileName); LuaXML_Ananysis(L,&doc); fclose(fp); return 1; }
进入“LuaXML_ParseFile”之后就是C++的流程,需要给“Lua”的部分,就通过“LuaState”进行结果的传递,如下:
//LuaXML_AnanysisOss执行XML的解析和数据组织工作 extern void LuaXML_Ananysis (lua_State *L,XMLNode* pNode){ XMLNode *pChild = pNode->FirstChild(); for(;pChild;pChild = pChild->NextSibling()) { if(strcmp(pChild->Value(),"table_list") == 0){ break; } } //建表,表中存有每个字段类型 lua_pushstring(L,"table_map"); lua_newtable(L); XMLNode* meta_table = pChild->FirstChild(); for(;meta_table;meta_table = meta_table->NextSibling()){ const XMLElement* meta_elem = meta_table->ToElement(); if(meta_elem){ const char* meta_name = LuaXML_GetNodeNameAttr(meta_table); type_struct_map item_map; table_map[node_name] = meta_map; lua_pushstring(L,node_name); LuaXML_ParseXMLNode(L,meta_table,false); lua_settable(L,-3); } } lua_setglobal(L,"table_map"); }
编译生成so文件
g++ tinyxml2.cpp tinyxml_lua.cpp -fPIC -O2 -shared -L ./liblua.a -I /usr/include/lua5_4/ -o libtinyxmllua.so
这里有一点需要注意,就是我们可能机器上安装有多个版本的lua,所以需要通过-L和-I指定链接的.a文件和头文件;若是静态库文件和头文件版本不对,会在运行中遇到段错误
在Lua中使用
准备好so之后,我们就可以在lua中使用了,如下:
require("libtinyxmllua") tb=LuaXML_ParseFile("test.xml") print(#tb) for key,val in pairs(table_map) do print(key,val) for key_1,val_1 in pairs(val) do print(key_1,val_1) end end
直接“require”对应的库之后,直接调用即可“LuaXML_ParseFile”
运行效果如下图所示:
meta_table table: 0x194ee40 meta_entry2 int meta_entry3 int meta_entry5 int meta_entry1 int meta_entry4 int