当前位置: 首页 > article >正文

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 <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标准库打开接口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或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.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;
}

在这里插入图片描述

注册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脚本就不需要requirelua_close(L);                                 // 关闭Lua线程return 0;
}

在这里插入图片描述

在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和错误信息
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); //获取参数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!"); // 压入栈}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 <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,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提供了非常多的接口,可以根据需求参考官方文档和源码中的接口等。

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

http://www.lryc.cn/news/2415060.html

相关文章:

  • DCS控制系统概述
  • 电赛经验分享——一文看懂PID
  • ROS学习笔记(一)从0开始的ROS的安装以及初步使用
  • async 和 await(详解)
  • kubernetes(4)Pod的核心概念、Pod的调度
  • SSM框架(七):MyBatisPlus
  • 超详细的图解 Numpy,不收藏后悔!
  • Java中UUID的简单介绍
  • 国产操作系统环境下VNC的使用教程
  • 2024最新最全:【CISP系列考试大纲】零基础入门到精通
  • Snap: 高性能图处理框架详解与实践指南
  • Spring Boot注解汇总(详细)
  • 多功能计算机器在线,多功能数学计算器(RedCrab The Calculator)
  • k8s入门-k8s整体架构介绍
  • 如何关闭Microsoft Office-PowerPoint的OfficePlus
  • postman下载安装保姆级极简安装教程
  • 简单理解Hadoop(Hadoop是什么、如何工作)
  • Kaggle 新手入门必看,手把手教学
  • zookeeper原理篇-Zookeeper选举过程分析,面试教程视频讲解
  • Split文本分割
  • 【雕爷学编程】Arduino 手册之位操作 lowByte()
  • Git下载安装及基本配置
  • 【JDK的下载安装(小白级别)】
  • PHPStudy介绍、下载与安装
  • Verilog中case,casez,casex语句的用法
  • Swagger的简单介绍,集成,以及如何在生产环境中关闭swagger,在测试和开发环境中自动打开
  • tidb数据库的安装与部署_KOS操作系统
  • 全网最新详细学习SVN常用功能
  • ascii码_ASCII码一览表
  • SecureCRT 详细使用图文教程(按步骤)