商务网站开发工具,字节跳动公司简介,推广app违法吗,一个网站有哪几种漏洞作者#xff1a;查志旺 #xff0c;向日葵远程控制软件前端开发工程师。 最近公司需要做向日葵远程控制软件跨平台项目#xff0c;为了代码的可复用性#xff0c;需嵌入跨平台脚本语言#xff0c;我们选择了Lua#xff0c;理由是Lua由标准C编写而成#xff0c;几乎在所有… 作者查志旺 向日葵远程控制软件前端开发工程师。 最近公司需要做向日葵远程控制软件跨平台项目为了代码的可复用性需嵌入跨平台脚本语言我们选择了Lua理由是Lua由标准C编写而成几乎在所有操作系统和平台上都可以编译Lua脚本可以很容易的被C/C 代码调用也可以反过来调用C/C的函数今天就跟大家分享下c与Lua交互的一些问题。
为了方便c和lua的交互我引进了LuaBridge。因为它源码简单易用只有头文件没有.cpp文件不需要编译只需要引用头文件即可方便快捷。
下面我用vs2008工具写了一个win32控制台项目例子来讲解下c和Lua简单的互调。
一、准备工作
从lua官网上下载最新的http://www.lua.org/download.html下个最新的lua-5.3.4.tar.gz解压出来后可以直接拿源码编译成lib或者直接加入到项目中.我是直接把它放到项目中编译的时候会出现多个main函数入口的错误移除对应的文件。
下载LuaBridge下载地址https://github.com/vinniefalco/LuaBridge列表下的Source/LuaBridge是源码Manual.html是帮助文档。
创建win32控制台项目test_lua把lua源码和LuaBridge头文件放入到项目目录下并添加到项目工程中。
二、编写代码
1、先引用Lua和LuaBridge头文件如下
//引用c文件的头文件所以需要加上extern C
extern C
{#include lua.h#include lauxlib.h#include lualib.h
}
#include LuaBridge\LuaBridge.h
2、然后创建test_lua类和一个子类代码如下
class test_lua
{
public:test_lua(){m_test_string c test string;}~test_lua(){}
//test方法void test(int a,int b){printf(c test function %d%d%d\n, a, b, ab);}//属性set方法void SetName(std::string name){m_name name;}
//属性get方法注意需要后面加conststd::string GetName() const{return m_name;}
//供lua调用方法返回多个参数方法int cFunc(lua_State* L){
//返回参数1lua_pushstring(L,str1);
//返回参数1lua_pushstring(L,str2);
//返回参数个数return 2;}std::string m_test_string;std::string m_name;static int m_static_data;
};
//test_lua静态变量定义静态变量在类内只是声明
int test_lua::m_static_data;
//test_lua子类
class test_lua_child :public test_lua
{public:test_lua_child(std::string test):m_test_child_string(test){printf(call test_lua_child constructor\n);}~test_lua_child(){}std::string m_test_child_string;
};
3、创建一个lua脚本文件a.lua内容为
--lua 打印lua script
print(lua script)
--调用成员变量m_test_string(test_str为注册的名字)
print(test_lua.test_str)
--调用c静态变量需要加上test命名空间
test.test_lua.static_data12
print(static_data: ..test.test_lua.static_data)
--调用c类test_lua属性name
test_lua.namename_property;
print(name: ..test_lua.name);
--lua调用c方法test_lua为c类在lua的注册名调用test方法
test_lua:test(3,4)--调用c调用方法返回多个值
local ret1,ret2 test_lua:cFunc()
print(ret1..ret1.. ret2..ret2)--创建test_lua_child对象
local test_lua_child test.test_lua_child(test_string)
--调用其变量
print(child string:..test_lua_child.test_child_string);
--调用父类的name属性
test_lua_child.namechild_name_property;
print(name:..test_lua_child.name);--lua 方法加法
function lua_add_function(a,b)print(lua_add_function) return ab;
end--lua 方法字符串加法..是相加语法
function lua_add_str_function(a,b)print(lua_add_str_function) return a..b;
end
4、主函数编写
4.1、Lua的初始化和加载Lua的基本库
//初始化Lua 最后记得调用lua_close(lua_state)释放lua_State* lua_state luaL_newstate(); //加载Lua基本库luaL_openlibs(lua_state);
4.2、用luabridge注册到lua中
在这里要注意的是多个类注册需要加一个namespacetest且.endClass()后面不加分号 luabridge::getGlobalNamespace(lua_state).beginNamespace(test).beginClasstest_lua(test_lua).addConstructorvoid (*) (void) ()//无参构造函数的注册.addData(test_str,test_lua::m_test_string)//注册变量到lua.addStaticData(static_data, test_lua::m_static_data)//注册静态变量到lua.addFunction(test, test_lua::test)//注册test、方法到luaaddStaticFunction静态函数注册也类似.addProperty(name,test_lua::GetName,test_lua::SetName)//属性方法的注册addStaticProperty静态属性方法也类似.addCFunction(cFunc,test_lua::cFunc)//注册返回多个参数给lua的方法.endClass().deriveClasstest_lua_child, test_lua (test_lua_child)//子类的注册.addConstructorvoid (*) (std::string) ()//有参构造函数的注册.addData(test_child_string, test_lua_child::m_test_child_string)//注册变量到lua.endClass().endNamespace();//创建test_lua对象
test_lua test;luabridge::setGlobal(lua_state, test, test_lua);//注册test_lua对象到lua
注test_lua也可以在lua创建因为构造函数也注册到lua如test.test_lua(),上面的a.lua脚本有子类test_lua_child的创建和调用其父类的属性方法
4.3、注册完成后再返回看上面写的a.lua脚本就知道每个调用的意义添加运行Lua脚本 代码然后执行代码如下 //运行lua脚本luaL_dofile(lua_state, a.lua);
//关闭Lua
lua_close(lua_state);
编译执行结果为
lua script
c test string
static_data: 12
name: name_property
c test function 347
ret1str1 ret2str2
call test_lua_child constructor
child string:test_string
name:child_name_property4.4、c调用lua方法因为lua方法函数参数一样而且都是一个返回值为了方便采用模板形式以两个参数为例第一个参数lua对象和第二个参数方法名类型固定后面参数用模板
templatetypename R, typename T1, typename T2
R call(lua_State* lua_state,const char* name, T1 arg1, T2 arg2)
{
//读取方法名lua_getglobal(lua_state, name);
//判断是不是方法if (lua_isfunction(lua_state, -1)){
//压入参数luabridge::StackT1::push(lua_state, arg1);luabridge::StackT2::push(lua_state, arg2);
//执行函数参数为lua对象、参数个数返回值个数出错返回lua_pcall(lua_state, 2, 1, 0);}
//获取返回值return luabridge::StackR::get(lua_state, -1);
}
在运行lua脚本后面再加上如下调用代码
//调用lua方法lua_add_functionint ret callint(lua_state,lua_add_function, 5, 6);//调用lua方法lua_add_str_functionstd::string value callconst char*(lua_state,lua_add_str_function,5, 6);printf(lua_add_function result:%d\n, ret);printf(lua_add_str_function result:%s\n, value.c_str());
编译执行结果为
lua script
c test string
static_data: 12
name: name_property
c test function 347
ret1str1 ret2str2
call test_lua_child constructor
child string:test_string
name:child_name_property
lua_add_function
lua_add_str_function
lua_add_function result:11
lua_add_str_function result:564.5、最后讲一下luaL_dostring
luaL_dostring跟luaL_dofile是一个作用都是加载并运行lua脚本只是对象不一样看方法名就知道是一个是加载文件另外一个是加载string最后运行里面的lua脚本luaL_dostring在lua嵌入到其他的脚本语言中经常用到现在沿用上面的例子在lua_close之前加段代码简单说明下
//定义lua脚本调用test_lua类里的属性name并打印出来
std::string lua_string print(\run lua string test_lua name:\..test_lua.name);
//加载string并运行
luaL_dostring(lua_state, lua_string.c_str());
编译运行得到的结果为
run lua string test_lua name:name_property
说到嵌入问题现在做的向日葵远程控制软件的界面用的是xml这里就涉及到lua嵌入到xml中由于lua特性在其他的系统这些xml都可以用所以以后再也不用担心加个新界面每个系统还得重新再搞一套lua嵌入xml中原理就是把lua脚本加入到一个节点中如
script![CDATA[
--lua代码
print(run lua script)]]/script
解析xml对应的script节点内容然后用luaL_dostring去加载运行就可以了。
最后配上现在的向日葵界面图
向日葵客户端
向日葵控制端
三、小结
希望通过上面的简单例子可以帮助大家快速上手c嵌入lua脚本从上面代码也可以看出lua和c很容易互相调用lua与c是通过操作虚拟栈来交互的例如上面调用lua方法就是c先把方法放入到栈顶然后lua从栈顶取值操作然后把结果又放回到栈顶c再从栈顶取值lua调用c也类似就是c把需要调用的先注册到lua中lua就可以调用想了解更多lua基本语法和原理的可以具体查看lua中的manual.htmlLuaBridge的其它用法也可查看LuaBridge的manual.html。