文章目录

目的QuickStartVirtual StackLua调用C语言函数C语言调用Lua函数总结

目的

Lua本身的设计时就有为了可以方便的嵌入到别的语言中使用的功能,主要来说就是可以方便的嵌入到C语言中,并和C语言进行交互。这篇文章将对相关内容进行介绍。

QuickStart

交互可以有多方面意思,可以直接与现成的Lua解释器交互,可以将Lua编译成DLL交互,也可以将直接Lua嵌入代码中在进行函数相互调用等。这篇文章将以后者进行展开,下面就是个最基本的演示过程。

首先从官网下载Lua源码:https://www.lua.org/

解压得到源码,将其中的除 lua.c 和 luac.c (这两个一个是解释器、一个是编译器)外的代码导入到C语言项目中,然后使用下面代码就能编译测试:

#include

#include

#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标准库打开接口

int main()

{

lua_State* L = luaL_newstate(); // 创建Lua线程

luaL_openlibs(L); // 打开标准库

luaL_dostring(L, "print('Naisu, Lua!')"); // 解析并执行Lua脚本字符串

lua_close(L); // 关闭Lua线程

return 0;

}

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或NULL

printf("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)

下面是个最基础的演示:

static int sayhello (lua_State *L) {

printf("Hello Naisu\n");

return ;

}

int main() {

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线程

return 0;

}

注册C函数到Lua中有很多的方式,比如也可以学 luaL_openlibs(L) 操作内部注册标准库的方式

static int sayhello (lua_State *L) {

printf("Hello Naisu\n");

return 0;

}

static luaL_Reg Functions[] =

{

{"sayhello", sayhello}, // 函数名和函数指针

// 可以一次添加多个函数

{NULL, NULL} // 数组末尾必需有这个

};

LUAMOD_API int luaopen_hello (lua_State *L) {

luaL_newlib(L, Functions); // 将Functions注册为一个库

return 1;

}

int main() {

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脚本就不需要require

lua_close(L); // 关闭Lua线程

return 0;

}

在Lua中调用的C函数的传入参数和返回参数操作都是通过前面提到的栈来进行的:

#include

#include

#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和错误信息

static int add (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); //获取参数1

int var2 = lua_tointeger(L, 2); //获取参数2

int 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!"); // 压入栈

}

return 2; // 表示返回两个参数(栈顶的两个)

}

const char *lua_code = "\

local ret, err = add(22, 33)\

print(ret, err)\

ret, err = add(22, 33, 44)\

print(ret, err)\

";

int main() {

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);

return 0;

}

C语言调用Lua函数

Lua可以调用C函数,C也可以调用Lua函数,数据交互也是通过栈进行:

#include

#include

#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,33

const char *lua_code = "\

function lua_func(arg1, arg2)\

print(arg1, arg2)\

return 22, 33\

end\

";

int main() {

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);

return 0;

}

总结

Lua和C语言交互本身并不复杂,毕竟Lua也是由C编写的,这里整个源码都一起编译了。

相对内容比较多的就是数据交互了,总体来说都是通过抽象的栈进行的,但本文中只是展示了一些基础的内容,实际上为了应对各种场景,Lua提供了非常多的接口,可以根据需求参考官方文档和源码中的接口等。

另外本文还未涉及错误处理等内容,也可以参考官方文档和源码来处理。

好文推荐

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: