Lua和C语言交互入门

文章目录

  • 目的
  • QuickStart
  • Virtual Stack
  • Lua调用C语言函数
  • C语言调用Lua函数
  • 总结

目的

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

QuickStart

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

首先从官网下载Lua源码:https://www.lua.org/
在这里插入图片描述

解压得到源码,将其中的除 lua.cluac.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语言交互入门_第1张图片

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和C语言交互入门_第2张图片

下面代码可以简单进行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和C语言交互入门_第3张图片

lua.hlauxlib.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;
}

Lua和C语言交互入门_第4张图片

注册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语言交互入门_第5张图片

在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;
}

Lua和C语言交互入门_第6张图片

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语言交互入门_第7张图片

总结

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

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

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

你可能感兴趣的:(编程相关,lua,c语言,交互,脚本,嵌入)