Lua是一个由标准C编写的轻量级脚本语言,很容易被C++/C调
用,反过来也可以调用C++/C的函数。作用是可以作为扩展脚本与
配置脚本,优势是速度快,支持动态改变。
全文的代码参考:点击这里
准备工作
去Lua官网下载最新的Release版本,当然也可以根据具体需要下载特定的版本 。然后执行命令(这里下载的安装包是lua-5.2.3.tar.gz,目前官网已经出了最新的5.3):
$tar zxf lua-5.2.3.tar.gz $cd lua-5.2.3/ $sudo make install
这里需要注意的在Lua的Makefile中默认的安装路径是/usr/local,如需要修改可自行指定。另外Lua5.3参考手册也是参考必须的
Lua与C交互,准备阶段
我们首先得创建Lua在C中使用的环境,利用
lua_State *luaL_newstate (void);
Creates a new Lua state. It calls lua_newstate with an allocator based on the standard C realloc function and then sets a panic function that prints an error message to the standard error output in case of fatal errors.Returns the new state, or NULL if there is a memory allocation error.
大体上的意思是说luaL_newstate会调用lua_newstate创造一个Lua_State实例,使用C默认的内存分配释放接口。那么其实lua_newstate就是需要自己定义内存分配释放接口。
实例创建了,接下来需要加载Lua的库函数,利用
void luaL_openlibs (lua_State *L);
此接口是加载所有Lua库函数,如果你想选择加载可以参考luaL_requiref。
初始化函数
bool init_lua() { s_lua = luaL_newstate(); if (!s_lua) { printf("luaL_newstate failed!\n"); exit(-1); } luaL_openlibs(s_lua); return true; }
接下来是加载lua文件,使用接口
int luaL_dofile (lua_State *L, const char *filename);
bool load_lua_file(const char* lua_file) { if (luaL_dofile(s_lua, lua_file) != 0) { printf("LOAD LUA %s %s\n", lua_file, BOOT_FAIL); return false; } printf("LOAD LUA %s %s\n", lua_file, BOOT_OK); return true; }
Lua与C交互,开始阶段
这里以一个整数求和的例子来简单体现Lua与C交互,Lua文件内容
function test_func_add(a, b) return a + b end
那么在C中如何调用Lua实现的这个接口?这里需要介绍一点概念,lua_State会维护一个虚拟堆栈(遵循先进后出的原则),堆栈的序号可以从栈顶和栈底计数,从栈底计数,则栈底是1,向栈顶方向递增(1,2,3……)。从栈顶计数,则栈顶是-1,向栈底方向递减(-1,-2,-3)。一般默认从栈顶计数。
堆栈的默认大小是20,可以用lua_checkstack修改,用lua_gettop则可以获得栈里的元素数目。
这里有一个注意点是,如果你的lua_State是全局变量,那么每次对堆栈有新操作时务必使用lua_settop(lua_State, -1)将偏移重新置到栈顶(前提是确保上一个对栈的操作完成)。
接下来去Lua文件中取得test_func_add方法,利用
void lua_getglobal (lua_State *L, const char *name);
然后利用lua_pushnumber,把我们的参数压栈,最后通过
int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);
参数二:你有几个参数要传入Lua func(本段是传入test_func_add需要的参数,应该是2个)
参数三:Lua func处理完后有几个返回值(本段只有1个)
参数四:对错误的处理
具体详细的用法可以参考这里
取得Lua func的返回值也是通过栈的概念,利用lua_tonumber。多啰嗦一句,根据返回值的不同需要调用不同的接口。后文可以看见调用lua_toboolean。
整个代码
int proc_add_operation(int a, int b) { lua_settop(s_lua, -1); lua_getglobal(s_lua, "test_func_add"); lua_pushnumber(s_lua, a); lua_pushnumber(s_lua, b); int val = lua_pcall(s_lua, 2, 1, 0); if (val) { printf("lua_pcall_error %d\n", val); } return (int)lua_tonumber(s_lua, -1); }
Lua调用C接口,进行阶段
在复杂的需求业务环境中,Lua的处理接口不可能满足所有的数据需求,有时需要原始C程序的一些数据进行判定处理。这就需要在Lua环境调用C程序的接口来处理一些事情。为了满足这个需求,我们首先要做的是对C程序接口进行注册。
void lua_register (lua_State *L, const char *name, lua_CFunction f);
Sets the C function f as the new value of global name
譬如我们定义一个Lua的检查接口
function test_func_check(a) local val = test_check_value(a) return val end
其中test_check_value就是我们需要在C实现的接口,那么注册函数可以写为
lua_register(s_lua, "test_check_value", l_test_check_value);
l_test_check_value就是我们需要在C程序中实现的接口
#define target 300 static int l_test_check_value(lua_State * l) { int num = lua_tointeger(l, -1); bool check = (num == target); lua_pushboolean(l, check); return 1; }
该接口的返回值(本段程序是返回1)是需要返回值的个数,通过lua_pushboolean我们把需要返回的值压入栈。然后Lua func返回,我们再通过lua_toboolean取得Lua func返回值。
bool proc_check_value(int a) { lua_settop(s_lua, -1); lua_getglobal(s_lua, "test_func_check"); lua_pushnumber(s_lua, a); int val = lua_pcall(s_lua, 1, 1, 0); if (val) { printf("lua_pcall_error %d\n", val); } return (bool)lua_toboolean(s_lua, -1); }
Lua与C交互运行时改变,最后阶段
Lua与C交互的另外一个重要特点是,Lua脚本的改变不需要重启C程序即可完成,但是需要重新加载对应的Lua的文件,按一般网络服务器的做法,通过特殊的协议或者广播来触发Lua文件的重新加载。本段为了用例方便,调用处理函数前会重新加载。
Lua接口
function test_func_runtime_changes(num) local result = test_runtime_changes(num) result = result + 100 return result end
C相关
int proc_runtime_changes(int a) { lua_settop(s_lua, -1); lua_getglobal(s_lua, "test_func_runtime_changes"); lua_pushnumber(s_lua, a); int val = lua_pcall(s_lua, 1, 1, 0); if (val) { printf("lua_pcall_error %d\n", val); } return (int)lua_tonumber(s_lua, -1); } #define add 100 static int l_test_runtime_changes(lua_State * l) { int num = lua_tointeger(l, -1); num += add; lua_pushnumber(l, num); return 1; }
主函数处理片段
printf("== check runtime changes ==\n"); while(1) { sleep(1); load_lua_files(); int result = proc_runtime_changes(400); printf("runtime changes %d \n", result); }
这里可以看到我们在Lua func test_func_runtime_changes中加了100,在l_test_runtime_changes加了100,传入是400,最后结果是600。当我们重新标记Lua func test_func_runtime_changes中加200,最后结果会变成700。
最近项目中有关Lua应用大幅修改促使我完成了这边文章,想想WOW用Lua作为插件语言,剑网3部分功能用Lua实现,可以说Lua功能多强,多厉害,也可以说用其他哪个语言也可以达到类似或者更好的实现。这里我只想说合适的地方用合适的语言才是合适的选择。
(全文结束)
转载文章请注明出处:漫漫路 - lanindex.com
Submit