Lua调用c模块

一、lua调用c模块总结
1.c函数库成为lua的一个模块
主要做法:将包含c函数的代码生成库文件 linux下是.so  windows下是.dll 
          同时拷贝到lua运行环境的bin下(由于我用的是LuaTool运行环境 所以放在bin下 程序运行默认从bin下找)
          这样lua解析器才可以找到.dll文件 
2.编写c模块
  (1)先编写需要注册的c函数 这些c函数用统一的声明并且必须写为static 返回置为int
  (2)定义luaL_Reg结构体 这个结构体数组用来因为注册到Lua模块中
     luaL_Reg结构体的第一个字段为字符串 在注册时通知lua该函数的名字
    第二个字段为函数指针
    最后一个永远为NULL 用来标记结尾
  (3) __declspec(dllexport)将函数、类等声明为导出函数,供其它程序调用,作为动态库的对外接口函数、类等
      这个函数非常重要 如果没有 将来生成的.dll不能够被lua模块找到
  (4)向lua中注册上述的表格

(5)C相关的API

  1. typedef struct lua_State lua_State  lua解释器 
  2. typedef int (*lua_CFunction)(lua_State *L) 能够别Lua调用的C函数必须是这种规则 函数的返回值int值表示C函数返回值的个数
  3. void lua_pushcclosure(lua_State*L,lua_CFunction fn ,int n) 将一个C必包压栈 fn C函数指针 n 函数关联的upvalue的个数 首先将upvalues依次压栈 然后调用该函数 将C函数压栈 并将n个upvalues出栈
  4. void lua_pushcfunction(lua_State*L,lua_CFunction fn )将c函数压栈 接收一个c函数的指针 然后将Lua.function类型的对象压栈
  5. void lua_register(lua_State*L,const char *name, lua_CFunction f) 注册C函数为一个全局变量
  6. void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);
      注册函数到栈顶的表中;
      参数l:luaL_Reg列表,记录了要注册的函数信息,注意,该列表以{NULL, NULL}结尾;
      nup参数:upvalue的个数,如果不为0,则注册的所有函数都共享这些upvalues;
      先将table压栈,然后将upvalues依次压栈,然后调用该函数进行函数注册。注册完毕后upvalues会出栈。
      注意:luaL_register函数已经不再使用,取而代之的是luaL_setfuncs,因为该函数不会创建全局变量。

    举例如下 mycomplib.c

从C value ---> lua Value  并不是简单地将值通过栈传给lua 而是将其封装成一个table 其中会放生 栈顶新分配元素 绑定或者赋值

一个c value入栈就是进入了lua的世界, lua会生成一个对应的结构并管理起来, 从此就不再依赖这个c value

//static 内部  in类型返回值
static int compare(lua_State* L)
{
	const char *path1 = luaL_checkstring(L, 1); //检查参数是否错误
	const char *path2 = luaL_checkstring(L, 2);

	//实现函数功能
	if (path1 == NULL || path2 == NULL)
	{
		printf("these have empty file\n");
		return -1;
	}

	FILE* fp1 = fopen(path1, "r");
	if (fp1 == NULL)
	{
		printf("The file1 open fail\n");
		return -1;
	}

	FILE* fp2 = fopen(path2, "r");
	if (fp2 == NULL)
	{
		printf("The file2 open fail\n");
		return -1;
	}

	int c1;
	int c2;
	int flag = 0;
	while (1)
	{
		c1 = fgetc(fp1);
		c2 = fgetc(fp2);

		if (c1 != c2)
		{
			flag = 1;
			break;
		}

		if (c1 == EOF || c2 == EOF)
		{
			break;
		}
	}

	if (fclose(fp1) != 0)
		printf("close fp1 fail\n");
	else
		printf("close fp1 success\n");

	if (fclose(fp2) != 0)
		printf("close fp2 fail\n");
	else
		printf("close fp2 success\n");
	lua_pushnumber(L, flag);//压入结果
	return 1; //结果的数量
}

static int add(lua_State* L)
{
	double op1 = luaL_checknumber(L,1);
	double op2 = luaL_checknumber(L,2);

	lua_pushnumber(L,op1 + op2);
	return 1;
}


static luaL_Reg mylibs[] = {
	{"compare",compare},
	{"add",add},
	{NULL,NULL}
};
 __declspec(dllexport)
int luaopen_mycomplib(lua_State* L)
{
	const char *libName = "mycomplib";
	luaL_register(L, libName, mylibs);
	return 1;
}

ctest.lua

require "mycomplib"
require "analy" //我自己的解析命令行参数 

function help()
	print("you maybe input: lua [script] -d filename1 -f filename2")
end

function main()
	
	if (arg[4] == nil) then
		help()
		return
	end
	
	local table1 = analy.analyArgument("-d")
	local filename1 = table1[1]
	
	local table2 = analy.analyArgument("-f")
	local filename2 = table2[1]
	
	if (mycomplib.compare(filename1, filename2) == 0) then
		print("filename1 same as filename2")
	else
		print("filename1 not same as filename2")
	end
	
	--print(mycomplib.add(10,20))
end

main()

由于我是用LuaTool 工具环境运行lua代码得的 所以将生成的dll文件放在了其bin下 ,如下图所示

二、C++ C调用lua

lua_pushcclosure(L,func,0) --->创建并压入闭包

lua_createtable(L,0,0) ---->新建并压入一个表

lua_pushnumber(L,100)---->压入一个数字

lua_pushstring(L,"hello world")--->压入一个字符串

上面所有压入的不同类型的值其实在lua中都是用一个类型所集合的 就是TValue这样一个结构体 它对应lua中的所有数据类型 是一个{值,类型}结构 这就是lua的动态类型实现 将值和类型绑定在一起 用tt记录value的类型 value是一个联合体 联合体有四个域

p可以存放一个指针 十佳上市lua中的light userdata结构   n 所有的数值存在这里 不管是int 还是float

b boolean值存在这里 

gc 存放需要内存管理垃圾回收的类型 比如table thread closure string

lua中,number boolean nil light userdata 四种类型的值是直接存在栈上元素里的 和垃圾回收无关

lua中,string table closure userdata thread存在栈上元素里的只是指针 在生命周期结束后会被垃圾回收

C++ C调lua遵守的原则

1.所有lua中的值由lua自己管理 C++/C并不知道 并且他们其中的值Lua也不知道 如果C++/C要lua中的东西 由lua产生放到栈上 C++/C通过API接口获取这个值

2.凡是lua的变量 lua负责这些变量的生命周期和垃圾回收 

C调用lua函数举例

main.lua文件

function add(a,b)
	return a + b
end

str = "hello wolrd"
tb = {name = "apple", id = "1000020"}

test.c

#include <stdio.h>
#include <stdlib.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>


//调用lua中的函数
int call_lua_add(lua_State *L)
{
	lua_getglobal(L,"add");
	lua_pushnumber(L,200);
	lua_pushnumber(L,1);
	lua_pcall(L,2,1,0); //调用函数 2各参数 1个返回值
	int sum = (int)lua_tonumber(L,-1);//获取栈顶元素
	printf("%d\n",sum);
	lua_pop(L,1);//栈底元素出栈
	return sum;
}

//获取lua中的字符串
int call_lua_string(lua_State *L)
{
	lua_getglobal(L, "str");
	char *str = lua_tostring(L, -1);
	return str;
}

//获取lua中的table元素值
int call_lua_table(lua_State *L)
{
	lua_getglobal(L, "tb");
	lua_getfield(L, -1, "name");
	char *str = lua_tostring(L, -1);
	return str;
}

int main()
{
	lua_State *L = luaL_newstate();//新建lua解释器
	luaL_openlibs(L);//载入lua所有函数库
	luaL_dofile(L,"main.lua");
	printf("sum = %d\n", call_lua_add(L));
	printf("str = %s\n", call_lua_string(L));
	printf("name = %s\n", call_lua_table(L));
	lua_close(L);
	return 0;
}

需要注意的是 堆栈操作是基于栈顶的 他只会操作栈顶的值 lua中-1永远指向栈顶 1永远指向栈底

lua value –> c value时, 是通过 lua_to* 族api实现, 很简单, 取出对应的c中的域的值就行了

猜你喜欢

转载自blog.csdn.net/weixin_41966991/article/details/88847756