文章目录
目的
Lua本身的设计时就有为了可以方便的嵌入到别的语言中使用的功能,主要来说就是可以方便的嵌入到C语言中,并和C语言进行交互。这篇文章将对相关内容进行介绍。
QuickStart
交互可以有多方面意思,可以直接与现成的Lua解释器交互,可以将Lua编译成DLL交互,也可以将直接Lua嵌入代码中在进行函数相互调用等。这篇文章将以后者进行展开,下面就是个最基本的演示过程。
首先从官网下载Lua源码:https://www.lua.org/
解压得到源码,将其中的除
lua.c
和
luac.c
(这两个一个是解释器、一个是编译器)外的代码导入到C语言项目中,然后使用下面代码就能编译测试:
#include<stdio.h>#include<stdlib.h>#include"lua-5.4.6/src/lua.h"// Lua数据类型与函数接口#include"lua-5.4.6/src/lauxlib.h"// Lua与C交互辅助函数接口#include"lua-5.4.6/src/lualib.h"// Lua标准库打开接口intmain(){
lua_State* L =luaL_newstate();// 创建Lua线程luaL_openlibs(L);// 打开标准库luaL_dostring(L,"print('Naisu, Lua!')");// 解析并执行Lua脚本字符串lua_close(L);// 关闭Lua线程return0;}
Lua与C交互所需的东西都在
lua.h
中有声明,相关内容可以参考官方文档的《The Application Program Interface》章节
另外为了简化交互操作,还提供了
lauxlib.h
,这个中的很多操作是对前者进一步封装,相关内容可以参考《The Auxiliary Library》章节。
Virtual Stack
Lua和C语言的数据类型是不同的,所以没法直接交互,需要通过Lua的虚拟的栈结构进行交互。栈中的每个元素代表一个Lua的值。
Lua提供了非常多的函数用于C语言操作栈,比如使用
void lua_pushXXXX (lua_State *L, XX)
可以向栈压入数据,使用
XX lua_isXXXX (lua_State *L, int index)
可以判断栈中某个索引的值是否为某个类型,使用
XX lua_toXXXX (lua_State *L, int index)
获取栈中某个索引的值为特定类型。
Lua的栈可以正索引也可以负索引。默认最小大小由
lua.h
中
#define LUA_MINSTACK 20
定义。
下面代码可以简单进行Lua栈的测试:
lua_State* L =luaL_newstate();luaL_openlibs(L);printf("Top index: %d\n",lua_gettop(L));// 返回当前栈顶索引(等于栈中元素个数)lua_pushnumber(L,233);// 压数据入栈lua_pushstring(L,"Naisu");// 压数据入栈printf("Top index: %d\n",lua_gettop(L));printf("Index 2: %s\n",lua_tostring(L,2));// 将栈中数据转换成C语言数据printf("Index 1: %d\n",lua_tointeger(L,1));// 将栈中数据转换成C语言数据// 这类转换如果失败则给出默认值0或NULLprintf("Top index: %d\n",lua_gettop(L));lua_pop(L,1);// 从栈中弹出一个值printf("Top index: %d\n",lua_gettop(L));lua_settop(L,0);// 清空栈printf("Top index: %d\n",lua_gettop(L));lua_close(L);
lua.h
和
lauxlib.h
中有非常多的函数可以用来操作Lua栈,这里不具体进行介绍,有兴趣的可以查看官方文档。
Lua调用C语言函数
C语言函数只有符合下面格式,并且注册到Lua中才能通过Lua脚本调用:
typedef int (*lua_CFunction) (lua_State *L)
下面是个最基础的演示:
staticintsayhello(lua_State *L){printf("Hello Naisu\n");return;}intmain(){
lua_State* L =luaL_newstate();// 创建Lua线程luaL_openlibs(L);lua_register(L,"cfn_sayhello", sayhello);// 将C语言的函数sayhello以名称cfn_sayhello注册为Lua的全局函数luaL_dostring(L,"cfn_sayhello()");// 解析并执行Lua脚本字符串lua_close(L);// 关闭Lua线程return0;}
注册C函数到Lua中有很多的方式,比如也可以学
luaL_openlibs(L)
操作内部注册标准库的方式
staticintsayhello(lua_State *L){printf("Hello Naisu\n");return0;}static luaL_Reg Functions[]={{"sayhello", sayhello},// 函数名和函数指针// 可以一次添加多个函数{NULL,NULL}// 数组末尾必需有这个};
LUAMOD_API intluaopen_hello(lua_State *L){luaL_newlib(L, Functions);// 将Functions注册为一个库return1;}intmain(){
lua_State* L =luaL_newstate();// 创建Lua线程luaL_openlibs(L);// 打开标准库luaL_requiref(L,"hello", luaopen_hello,0);// 通过luaopen_hello将Functions注册为hello库// 当最后的参数为1时,注册完成时会将库直接导入到Lua全局变量中lua_pop(L,1);// 清除堆栈luaL_dostring(L,"hello = require(\"hello\")\
hello.sayhello()");// 解析并执行Lua脚本字符串// 如果luaL_requiref最后参数为1,则这里的Lua脚本就不需要requirelua_close(L);// 关闭Lua线程return0;}
在Lua中调用的C函数的传入参数和返回参数操作都是通过前面提到的栈来进行的:
#include<stdio.h>#include<stdlib.h>#include"lua-5.4.6/src/lua.h"#include"lua-5.4.6/src/lauxlib.h"#include"lua-5.4.6/src/lualib.h"// 下面函数的作用是传入两个整数,如果参数正确则求和并返回结果和nil,错误则返回0和错误信息staticintadd(lua_State *L){printf("Call add:");printf("Top index: %d\n",lua_gettop(L));// 在Lua中调用C函数,每次调用函数都有自己的堆栈if((lua_gettop(L)==2)&&lua_isinteger(L,1)&&lua_isinteger(L,2))// 输入两个参数并且都是整数{int var1 =lua_tointeger(L,1);//获取参数1int var2 =lua_tointeger(L,2);//获取参数2int sum = var1 + var2;lua_settop(L,0);// 清空栈lua_pushinteger(L, sum);// 将返回值压入栈lua_pushnil(L);// 压入栈}else{lua_settop(L,0);// 清空栈lua_pushinteger(L,0);// 压入栈lua_pushstring(L,"Wrong arg!");// 压入栈}return2;// 表示返回两个参数(栈顶的两个)}constchar*lua_code ="\
local ret, err = add(22, 33)\
print(ret, err)\
ret, err = add(22, 33, 44)\
print(ret, err)\
";intmain(){
lua_State* L =luaL_newstate();// 创建Lua线程luaL_openlibs(L);printf("Top index: %d\n",lua_gettop(L));lua_register(L,"add", add);// 注册函数luaL_dostring(L, lua_code);// 调用add函数,传入两个参数lua_close(L);return0;}
C语言调用Lua函数
Lua可以调用C函数,C也可以调用Lua函数,数据交互也是通过栈进行:
#include<stdio.h>#include<stdlib.h>#include"lua-5.4.6/src/lua.h"#include"lua-5.4.6/src/lauxlib.h"#include"lua-5.4.6/src/lualib.h"// 下面定义了一个lua函数,传入两个参数并打印,返回22,33constchar*lua_code ="\
function lua_func(arg1, arg2)\
print(arg1, arg2)\
return 22, 33\
end\
";intmain(){
lua_State* L =luaL_newstate();// 创建Lua线程luaL_openlibs(L);luaL_dostring(L, lua_code);// 加载自定义的lua函数到全局变量printf("Top index: %d\n",lua_gettop(L));lua_getglobal(L,"lua_func");// 从全局变量中获取自定义lua函数压入栈中lua_pushinteger(L,666);// 向栈中压入参数lua_pushinteger(L,777);// 向栈中压入参数printf("Top index: %d\n",lua_gettop(L));lua_call(L,2, LUA_MULTRET);// 调用已压入栈中的lua函数,传入2个参数,并将所有返回值压入栈中// lua_call调用函数本身会清空当前栈,然后再将返回值压入栈printf("Top index: %d\n",lua_gettop(L));printf("Index 1: %s\n",lua_tostring(L,1));// 打印lua函数返回值printf("Index 2: %s\n",lua_tostring(L,2));// 打印lua函数返回值lua_close(L);return0;}
总结
Lua和C语言交互本身并不复杂,毕竟Lua也是由C编写的,这里整个源码都一起编译了。
相对内容比较多的就是数据交互了,总体来说都是通过抽象的栈进行的,但本文中只是展示了一些基础的内容,实际上为了应对各种场景,Lua提供了非常多的接口,可以根据需求参考官方文档和源码中的接口等。
另外本文还未涉及错误处理等内容,也可以参考官方文档和源码来处理。
版权归原作者 Naisu Xu 所有, 如有侵权,请联系我们删除。