Lua与C交互简明教程

Lua是一个由标准C编写的轻量级脚本语言,很容易被C++/C调

用,反过来也可以调用C++/C的函数。作用是可以作为扩展脚本与

配置脚本,优势是速度快,支持动态改变。

全文的代码参考: 点击这里

准备工作

Lua官网下载最新的Release版本 ,当然也可以根据具体需要下载特定的版本 。然后执行命令(这里下载的安装包是lua-5.2.3.tar.gz):

$tar zxf lua-5.2.3.tar.gz
$cd lua-5.2.3/
$sudo make install

这里需要注意的在Lua的Makefile中 默认的安装路径是/usr/local ,如需要修改可自行指定。另外 Lua5.2参考手册 也是参考必须的

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 %sn", lua_file, BOOT_FAIL);
		return false;
	}
	printf("LOAD LUA %s %sn", 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 %dn", 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 %dn", val);
	}
	return (bool)lua_toboolean(s_lua, -1);
}

Lua与C交互运行时改变,没刀:D

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 %dn", 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功能多强,多厉害,也可以说用其他哪个语言也可以达到类似或者更好的实现。这里我只想说合适的地方用合适的语言才是合适的选择。

(全文结束)

稿源:漫漫路 (源链) | 关于 | 阅读提示

本站遵循[CC BY-NC-SA 4.0]。如您有版权、意见投诉等问题,请通过eMail联系我们处理。
酷辣虫 » 综合编程 » Lua与C交互简明教程

喜欢 (0)or分享给?

专业 x 专注 x 聚合 x 分享 CC BY-NC-SA 4.0

使用声明 | 英豪名录