一、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
- typedef struct lua_State lua_State lua解释器
- typedef int (*lua_CFunction)(lua_State *L) 能够别Lua调用的C函数必须是这种规则 函数的返回值int值表示C函数返回值的个数
- void lua_pushcclosure(lua_State*L,lua_CFunction fn ,int n) 将一个C必包压栈 fn C函数指针 n 函数关联的upvalue的个数 首先将upvalues依次压栈 然后调用该函数 将C函数压栈 并将n个upvalues出栈
- void lua_pushcfunction(lua_State*L,lua_CFunction fn )将c函数压栈 接收一个c函数的指针 然后将Lua.function类型的对象压栈
- void lua_register(lua_State*L,const char *name, lua_CFunction f) 注册C函数为一个全局变量
- 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中的域的值就行了