lua与c交互

Lua和c交互都是通过栈来实现,关于lua的栈,不在赘述,这不是本文重点。(其实只是大致知道,并不精通。)
要在程序中使用lua,首先得创建一个栈。如:lua_State* L = luaL_newstate();这里的lua_State就是一个栈的指针,所有的与c交互的操作都是通过这个L来实现的。程序中最好只用一个lua_State。
接下来要打开一些lua库,比如base库、table库、io库、string库、math库、coroutine库、debug库等等,可调用luaL_openlibs(L);就行了,这个函数会帮你打开这些库。其实打开库就是把c函数注册到lua里,使得我们可以在lua里调用。接下来就可以运行lua文件了。看下面的代码:
#include <stdio.h>
#include <stdlib.h>
#include <lua/lua.h>
#include <lua/lualib.h>
#include <lua/lauxlib.h>

int main(int argc, char *argv[]) {
	lua_State* L = luaL_newstate();
	luaL_openlibs(L);
	
	if (luaL_loadfile(L, "./main.lua") || lua_pcall(L, 0, 0, 0))
	{
		printf("LUA ERROR:%s", lua_tostring(L, -1));
		lua_close(L);
		return -1;
	}	
	return 0;
}


然后再main.lua里写lua代码就行了。

1.lua调用c函数
在lua中要想调用c代码的函数,需要先在c代码里注册一下这个函数,怎样注册呢?我们先来看下lua自己是怎么处理的。先看个简单的math库,在lmathlib.c文件里。该文件里前面都是一些定义的静态函数:math_abs,math_sin,blabla...先不管这些函数,在文件的下面,找到一个luaL_Reg型的数组,在lauxlib.h里,我们能找到luaL_Reg的原型:
typedef struct luaL_Reg {
	const char *name;
	lua_CFunction func;
} luaL_Reg;


从名字我们就能猜到,这个结构体存的就是名字和函数。这个name字段就是在lua里调的c的函数名字,func字段就是对应的c函数。在lua.h中,我们可以找到lua_CFunction的原型:
typedef int (*lua_CFunction) (lua_State *L);


它就是型如: int function_name(lua_State *L)的函数。然后再回来看lmathlib.c文件。在luaL_Reg数组上面的那些函数都是符合lua_CFunction原型的。实际上注册到lua里的函数都是必须写成lua_CFunction型的。参数lua_State *L就是用luaL_newstate创建创建出来的那个L。用来通过栈传递lua中的一些参数信息。返回值是int型,代表有几个返回值(lua中可以有多个返回值)。
lua中调用c函数的时候,先把要传递的参数按顺序压栈,所以我们在c函数中处理的时候要把参数一个一个取出来。比如用math库中很多函数都用luaL_checknumber(lua_State *L, int narg)函数。表示check函数第narg个参数是不是number型的,如果是就返回这个值。类似的函数还有luaL_checkinteger、luaL_checklstring、luaL_checkunsigned等。把这些值取出来进行一些操作之后在把结果压栈。压栈用lua_pushnumber、lua_pushinteger、lua_pushunsigned、lua_pushlstring、lua_pushstring、lua_pushvfstring、lua_pushfstring、lua_pushcclosure、lua_pushboolean、lua_pushlightuserdata、lua_pushthread等,比如,我们要在lua里注册一个两数相加的函数,需要这样写这个函数:
static int my_add(lua_State* L)
{
	int a = luaL_checknumber(L, 1);//取第一个参数
	int b = luaL_checknumber(L, 2);//取第一个参数
	lua_pushnumber(L, a+b);//结果压栈
	return 1;
}


然后在原来写的main函数的luaL_openlibs(L);函数下面注册一下这个函数,在lua里就可以直接调用了。
lua_register(L, "my_add", my_add);



lua_pushcfunction(L, my_add);
lua_setglobal(L, "my_add");
在lua里:
print(my_add(1, 2))

这样注册的话,每次注册一个全局函数。如果想分模块怎么办呢?其实看lua自带的一些库,我们已经知道答案了。在这里,我们简单介绍另一种方法。

首先创建一个luaL_Reg的结构体数组,把c函数和在lua里的函数名字一一对应起来存放在数组里。如:
static const luaL_Reg MyLib[] = {
	{"my_add", my_add},
	{NULL, NULL}
};

然后把刚才的注册函数部分改为:
luaL_newlib(L, MyLib);  //创建一个MyLib表
lua_setglobal(L, "MyLib");

这里lua_setglobal函数的意思是把栈顶的元素弹出,然后把它设为名为“MyLib”的全局表。这里栈顶的元素就是luaL_newlib创建的MyLib表。我们在lua里可以这样调用
print(MyLib.my_add(1, 100))

整理c代码,如下:
#include <stdio.h>
#include <stdlib.h>
#include <lua/lua.h>
#include <lua/lualib.h>
#include <lua/lauxlib.h>


static int my_add(lua_State* L)
{
	int a = luaL_checknumber(L, 1);
	int b = luaL_checknumber(L, 2);
	lua_pushnumber(L, a+b);
	return 1;
}


static const luaL_Reg MyLib[] = {
	{"my_add", my_add},
	{NULL, NULL}
};


static void luaopen_mylib(lua_State* L)
{
	luaL_newlib(L, MyLib);
    lua_setglobal(L, "MyLib");
}


int main(int argc, char *argv[]) {
	lua_State* L = luaL_newstate();		
	luaL_openlibs(L);
    luaopen_mylib(L);
    
	if (luaL_loadfile(L, "./main.lua") || lua_pcall(L, 0, 0, 0))
	{
		printf("LUA ERROR:%s", lua_tostring(L, -1));
		lua_close(L);
		return -1;
	}	
	return 0;
}

2.在c里调用lua函数
c代码调lua函数相对来说就简单多了。所调用的lua函数所在的文件必须被加载过,而且该函数不能为local。比如上文中我们的main.lua已经被执行了loadfile操作,因此在main.lua中的函数可以被c代码调用到。比如早main.lua中写个简单的函数:
function my_lua_fun()
	print("this is a lua function!");
end


则在main.c中可以写:

lua_getglobal(L, "my_lua_fun");//可以理解为找到这个函数
lua_pcall(L, 0, 0, 0);//调用函数

如果传参数并且获取返回值,可以这样:
lua函數:
function my_lua_add(param1, param2)	
	print("param1:"..param1, "param2:"..param2);
	return param1+param2;
end

c調用:
lua_getglobal(L, "my_lua_add");
lua_pushnumber(L, 10);
lua_pushnumber(L, 20);
lua_pcall(L, 2, 1, 0);
int param = lua_tonumber(L, -1);
printf("the result is %d", param);



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值