Lua学习笔记:C/C++和Lua的相互调用

前言
本篇在讲什么

C/C++和Lua的相互调用
本篇适合什么

适合初学Lua的小白
适合需要C/C++和lua结合开发的人

本篇需要什么

Lua语法有简单认知
C/C++语法有简单认知
依赖Lua5.1的环境
依赖VS 2017编辑器

本篇的特色

具有全流程的图文教学
重实践,轻理论,快速上手
提供全流程的源码内容


★提高阅读体验★

👉 ♠ 一级标题 👈

👉 ♥ 二级标题 👈

👉 ♣ 三级标题 👈

👉 ♦ 四级标题 👈


目录

  • ♠ 为什么C/C++和Lua可以相互调用
  • ♠ 为什么要C/C++和Lua相互调用
  • ♠ Lua的C API
    • ♥ 加载标准库
  • ♠ C++和Lua的相互调用
    • ♥ 数据交换
    • ♥ 执行Lua脚本
      • ♣ 引入头文件
      • ♣ Lua的虚拟机
      • ♣ 注册库
    • ♥ C++和Lua的数据交互
      • ♣ C++调用Lua方法传递参数
      • ♣ Lua调用C++方法
  • ♠ C API一些常见函数
    • ♥ 压入数据
    • ♥ 查询数据
    • ♥ 其他栈操作
  • ♠ 推送
  • ♠ 结语


♠ 为什么C/C++和Lua可以相互调用

Lua运行的解释器是用Lua标准库实现的独立解释器,所以Lua是一种嵌入型语言,可以被当做库来拓展其他应用

Lua可以通过固定的C API实现和C语言的交互


♠ 为什么要C/C++和Lua相互调用

Lua作为脚本语言,不需要预先编译,加上其语法简单扩展性强的特点,尝尝作为胶水去完成其他应用的拓展,例如常见使用Lua作为热更的解决方案

Lua存在自身的局限性,通过调用C/C++编写的外部库,可以实现Lua不方便实现的功能,或实现效率更高的方式


♠ Lua的C API

C API是一个函数、常量和类型组成的集合,有了它,C语言代码就能与Lua语言交互
C API包括读写Lua全局变量的函数、调用Lua函数的函数、运行Lua代码段的函数,以及注册C函数(以便于其后可被Lua代码调用)的函数等
通过调用C API, C代码几乎可以做到Lua代码能够做的所有事情


♥ 加载标准库

如果你本地安装有Lua环境,可以很简单的在项目内加载Lua的标准库,下面文章可以了解如何在vs中引入lua的标准库

VIsual Studio内引用Lua解释器,编译Lua源码,执行Lua脚本


♠ C++和Lua的相互调用

我们尝试性的使用C++去加载一个lua文件,并一点点去解释和理解其中每个步骤的作用


♥ 数据交换

首先我们需要知道的是Lua和C之间的通信通过虚拟栈(stack)
当我们想要从Lua获取一个值(例如一个全局变量)时,会经过以下过程

  • C获取Lua的值

需要调用Lua将指定的值压入栈中,C再去栈中获取

  • Lua获取C的值

首先通过C将这个值压入栈,然后调用Lua将其从栈中弹出即可

在这里插入图片描述

如上图所示,交换数据都需要经过这个中间栈,数据交换的过程,就是入栈出栈的过程

栈的索引可以通过正数和负数表示,正数栈底索引1,负数栈顶为-1

C API提供了完善的接口让我们去操作对栈内数据的处理


♥ 执行Lua脚本

下面代码通过C++执行了一个名为Test.lua的Lua脚本

#include <iostream>

extern "C" {
	#include "lua.h"
	#include "lualib.h"
	#include "lauxlib.h"
}

int main()
{
	lua_State *L = luaL_newstate();
	luaL_openlibs(L);
	luaL_dofile(L, "Test.lua");   // 读取Lua文件,并压入栈内
	lua_close(L);                 // 关闭lua环境
	return 0;
}

♣ 引入头文件

lua.hlualib.hlauxlib.h三个头文件,是Lua标准库下调用Lua接口的文件

  • lua.h

头文件lua.h声明了Lua提供的基础函数
包括创建新Lua环境的函数、调用Lua函数的函数、读写环境中的全局变量的函数,以及注册供Lua语言调用的新函数的函数等
lua.h声明的所有内容都有一个前缀lua_(例如lua_pcall)

简单的理解,所有外部调用Lua和Lua调用外部都是使用lua.h内接口对应的功能

  • lualib.h

头文件lauxlib.h声明了辅助库所提供的函数
辅助库使用lua.h提供的基础API来提供更高层次的抽象,特别是对标准库用到的相关机制进行抽象
其中所有的声明均以luaL_开头(例如lual_loadstring)

简单的理解,lauxlib.h是对lua.h提供功能的封装

  • lauxlib.h

所有lua相关功能以单独的包存在(例如io、math)
lua运行环境创建后,不包含任何预定义的函数
需要用到哪些功能通过lauxlib.h内的接口注册

简单的理解,lua运行环境初始时没有任何功能的,想用啥功能,用lauxlib.h注册一下才能用


♣ Lua的虚拟机

我们通过luaL_newstate()方法创建了一个Lua虚拟机

lua_State *L = luaL_newstate();

lua_State是一个结构体,存储了一个Lua程序的执行状态信息,Lua和C程序通信的栈就存储在其中

注:这里先知道他是干嘛的就行了,后面有机会我们再去分析Lua的源码


♣ 注册库

上文我们已经提到了,Lua新创建的环境中是没有任何函数定义的,我们需要注册,luaL_openlibs方法就是打开所有的标准库以供使用

luaL_openlibs(L);

♥ C++和Lua的数据交互

上文我们已经说到,二者通信是通过栈,接下来我们具体分析一下都需要哪些操作


♣ C++调用Lua方法传递参数

我们先写一段简单的lua代码,一句输出,一个可以将参数输出的方法print_parm

在这里插入图片描述

我们C++加载逻辑如下图,在第一个例子的基础上稍作修改,运行后成功的输出了第二张图的效果

在这里插入图片描述
在这里插入图片描述

调用Lua一共经历了下面几个步骤

  • 第一步:加载脚本,接口luaL_dofile可以供我们加载lua脚本

参数1:创建好的lua_State
参数2:Lua脚本的路径

luaL_dofile(L, "Test.lua");
  • 第二步:获取函数,接口lua_getglobal可以供我们获取已经加载好的Lua脚本内的全局字段,获取后会压入栈

参数1:创建好的lua_State
参数2:Lua中的全局变量

lua_getglobal(L, "print_parm");	
  • 第三步:参数压入栈,接口lua_pushxxx可以供我们将Lua函数需要的参数压入到栈中

参数1:创建好的lua_State
参数2:具体对应类型的参数值

lua_pushnumber(L, 10);				
lua_pushstring(L, "我真帅");		
lua_pushboolean(L, true);	

经历了上述三个步骤之后,我们的栈会变成下图的样子

在这里插入图片描述

  • 第四步:调用Lua函数,接口lua_pcall调用我们之前压入栈中的Lua函数

参数1:创建好的lua_State
参数2:函数需要的参数数量
参数3:Lua函数的返回值数量
参数4:错误处理函数,成功返回0,失败返回错误码

lua_pcall(L, 3, 0, 0);	

♣ Lua调用C++方法

这一块我们看如何在Lua内调用已经写好的方法,这一次先写C++代码,如下图所示

在这里插入图片描述

然后再写一段Lua程序,程序内调用addNum方法,将参数相加后输出,执行后入下2图显示的输出效果

在这里插入图片描述
在这里插入图片描述

其中需要重点介绍的步骤如下

  • 第一步:准备函数,我们需要预先准备一个要给Lua使用的方法,参数必须是lua_State的实例
int AddNum(lua_State *L)
{
	int n1 = lua_tonumber(L, -1);
	int n2 = lua_tonumber(L, -2);

	lua_pushnumber(L, n1+n2);
	return 1;
}
  • 第二步:注册方法,一定要在加载Lua脚本前通过接口lua_register将函数进行注册
lua_register(L, "addNum", AddNum);

参数1:创建好的lua_State
参数2:给Lua映射的方法名
参数3:注册的C++方法名

  • 第三步:在Lua中执行方法,在Lua中要使用刚才lua_register方法第二个参数映射的名字来调用方法
num = addNum(10, 15)

♠ C API一些常见函数

上文中我们已经用到了很多去操作数据交换和注册方法的函数,下面我们分类列举一些常用的接口


♥ 压入数据

我们已经了解到了C/C++和Lua的相互调用都是通过栈进行的,其中必不可少的操作就是向栈中压入数据

下面我们列举向栈中压入不同数据的方法

函数名功能
lua_pushnil压入常量nil
lua_pushboolean压入bool值
lua_pushnumber压入双精度浮点值
lua_pushinteger压入整型
lua_pushlstring压入字符串非\0结尾
lua_pushstring压入字符串\0结尾
int lua_checkstack ( lua_State *L, int sz);

注:栈中至少会有20个空闲的位置,一般情况下够用,特殊情况使用接口lua_checkstack看是否有足够空间


♥ 查询数据

我们一般以栈顶为参照,第一个元素(最后被压入栈的)索引-1,第二个索引-2,以此类推

C API提供了一系列名为lua_is*的函数用来判断栈内元素类型,下面列举了一些常见的

函数名功能
lua_isfunction是否为方法(c或lua)
lua_istable是否为lua的table
lua_isuserdata是否为lua的userdata
lua_isnil是否为nil
lua_isboolean是否为bool
lua_isthread是否为thread
lua_isnumber是否为数字
lua_isstring是否为字符串或数字

C API提供了一系列名为lua_to*的函数用来从栈中获取一个值,下面列举了一些常见的

函数名功能
lua_toboolean获取布尔值
lua_tonumber获取浮点值
lua_tothread获取thread
lua_tolstring获取字符串
lua_tointeger获取整型

注:即使对应栈获取的元素类型不正确,这些函数也不会报错,可能会返回Null


♥ 其他栈操作

除了上述在C语言和栈之间交换数据的函数外,C API还提供了下列用于通用栈操作的
函数

函数名功能
lua_gettop返回栈中元素的个数
lua_settop将栈顶设置为一个指定的值
lua_pushvalue将指定索引上的元素的副本压入栈
lua_rotate将指定索引元素向栈顶转动n个位置
lua_remove删除指定索引的元素
lua_insert将栈顶元素移动到指定位置
lua_replace弹出一个值,并将栈顶设置为指定索引上的值
lua_copy将一个索引上的值复制到另一个索引上

♠ 推送

  • Github
https://github.com/KingSun5

♠ 结语

若是觉得博主的文章写的不错,不妨关注一下博主,点赞一下博文,另博主能力有限,若文中有出现什么错误的地方,欢迎各位评论指摘。

👉 本文属于原创文章,转载请评论留言,并在转载文章头部著名作者出处👈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/24140.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【2023 · CANN训练营第一季】基于昇腾910的TF网络脚本训练(ModelArts平台)

准备工作: 1.注册华为云账号&#xff0c;获取AK/SAK&#xff0c;授权ModelArts&#xff0c;并申请华为云代金券 2.获取训练数据集&#xff0c;并进行数据预处理&#xff0c;比如离线制作成tfrecords(建议&#xff0c;可选) 3.将数据集(训练脚本)上传到OBS 4.安装PycharmIDE及To…

Word控件Spire.Doc 【其他】教程(4):在 Word 中插入上标和下标

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

注意力Transformer

注意力 注意力分为两步&#xff1a; 计算注意力分布 α \alpha α 其实就是&#xff0c;打分函数进行打分&#xff0c;然后softmax进行归一化 根据 α \alpha α来计算输入信息的加权平均&#xff08;软注意力&#xff09; 其选择的信息是所有输入向量在注意力下的分布 打…

Docker 设置国内镜像源

Docker 镜像加速 国内从 DockerHub 拉取镜像有时会遇到网络问题&#xff0c;此时可以配置国内的镜像加速来下载。Docker 官方和国内很多云服务商都提供了国内加速器服务&#xff0c;例如如下&#xff1a; 科大镜像&#xff1a;https://docker.mirrors.ustc.edu.cn/网易&#…

ATA-4014高压功率放大器驱动超声马达测试应用

ATA-4014 高压功率放大器简介 ATA-4014是一款理想的可放大交、直流信号的单通道高压功率放大器。最大输出160Vp-p&#xff08;80Vp&#xff09;电压&#xff0c;452Wp功率&#xff0c;可以驱动高压功率型负载。电压增益数控可调&#xff0c;一键保存常用设置&#xff0c;为您提…

抽象轻松js

重复声明 经过了这么久&#xff0c;对声明变量应该差不多了解了&#xff0c;再加上之前了解的作用域 我们现在开始如果科学的使用重复声明 先复习一边(遍)作用域 var的作用域是全局 let、const的作用域是花括号 了解这点&#xff0c;那么科学使用重复声明就是合理使用作用…

C++11之atomic原子操作

atomic介绍 多线程间是通过互斥锁与条件变量来保证共享数据的同步的&#xff0c;互斥锁主要是针对过程加锁来实现对共享资源的排他性访问。很多时候&#xff0c;对共享资源的访问主要是对某一数据结构的读写操作&#xff0c;如果数据结构本身就带有排他性访问的特性&#xff0c…

如何用chatGPT赚钱?

赚钱思路 1&#xff09;初级-账号 对于新事物的出现&#xff0c;很多人对此都是抱着一个看热闹的态度&#xff0c;大家对于这个东西的整体认知水平是很低的&#xff01; 所以这个时候的思路就是快速去抢占市场&#xff0c;去各个平台发一些和ChatGPT相关的视频和文章去抢占市…

css、js(vue)进行textarea自适应高度(超详细说明)

文章目录 需求——如下图一、纯css 的自适应高度&#xff08;有问题&#xff0c;不推荐&#xff09;1.css代码2. html代码3. 代码截图说明4. 效果和会出现的问题 二、js 的自适应高度0.思路1.代码1. css代码2. html代码3. js代码 2.代码说明3.注意点导致的问题&#xff08;0&am…

GP05丨多因子IC对冲

量化策略开发&#xff0c;高质量社群&#xff0c;交易思路分享等相关内容 大家好&#xff0c;今天我们分享股票社群第5期量化策略——多因子IC对冲。 在前几期中&#xff0c;我们分享了GP01多因子、ETF轮动策略及Plus版本、网格等等。本期我们继续分享多因子策略。 策略背景与…

【P32】JMeter While 控制器(While Controller)

文章目录 一、While 控制器&#xff08;While Controller&#xff09;参数说明二、测试计划设计2.1、变量2.2、函数2.2.1、groovy脚本2.2.2、jex13脚本2.2.3、js脚本 一、While 控制器&#xff08;While Controller&#xff09;参数说明 可以对部分逻辑按变量条件进行循环迭代…

CentOS7编译安装Python3.10(含OpenSSL1.1.1安装),创建虚拟环境,运行Django项目(含sqlite版本报错)

文章目录 1、CentOS安装OpenSSL1.1.1&#xff08;前置环境&#xff09;2、CentOS安装 Python 3.103、创建虚拟环境4、运行Django项目 1、CentOS安装OpenSSL1.1.1&#xff08;前置环境&#xff09; 编译安装Python3.10时需要openssl1.1.1 查看当前版本 & 删除openssl1.0 …

代码随想录算法训练营第三十九天 | 力扣 62.不同路径, 63. 不同路径 II

62.不同路径 题目 62. 不同路径 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多…

C++数据结构:二叉树之一(数组存储)

文章目录 前言一、二叉树的基本定义二、二叉树的基本性质三、二叉树的存储&#xff08;数组&#xff09;总结原创文章&#xff0c;未经许可&#xff0c;禁止转载 前言 树是一种非线性数据结构&#xff0c;它由若干个节点和边组成。每个节点都有一个值&#xff0c;而边则表示节…

day17 - 用形状包围图像

在进行图像轮廓提取时&#xff0c;有的情况下不需要我们提取出精确的轮廓&#xff0c;只要提取出一个接近于轮廓的近似多边形&#xff0c;就可以满足后续的操作。 本期我们来学习如何通过设置参数来找出图像的近似多边形。 完成本期内容&#xff0c;你可以&#xff1a; 了解…

算法基础学习笔记——⑨C++STL使用技巧

✨博主&#xff1a;命运之光 ✨专栏&#xff1a;算法基础学习 目录 ✨CSTL简介 ✨CSTL使用技巧 前言&#xff1a;算法学习笔记记录日常分享&#xff0c;需要的看哈O(∩_∩)O&#xff0c;感谢大家的支持&#xff01; ✨CSTL简介 vector变长数组&#xff0c;倍增的思想//系统为…

STM32单片机(三)第一节:GPIO输出

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…

驱动开发:内核读写内存浮点数

如前所述&#xff0c;在前几章内容中笔者简单介绍了内存读写的基本实现方式&#xff0c;这其中包括了CR3切换读写&#xff0c;MDL映射读写&#xff0c;内存拷贝读写&#xff0c;本章将在如前所述的读写函数进一步封装&#xff0c;并以此来实现驱动读写内存浮点数的目的。内存浮…

MyBatis操作数据库表和动态SQL的使用

目录 1.MyBatis开发环境的搭建和测试 2.MyBatis基本操作 2.0 准备工作 2.1 新增操作 2.2 删除、修改、查询操作 2.3 #{param} 和 ${param}的使用和区别 2.4 实体对象属性和数据库字段名称不同时如何映射&#xff1f; 3. MyBatis多表查询 3.0 准备工作 3.1 一对一的表…

ELK企业级日志分析系统

ELK概述 为什么要使用 ELK 日志主要包括系统日志、应用程序日志和安全日志。系统运维和开发人员可以通过日志了解服务器软硬件信息、检查配置过程中的错误及错误发生的原因。经常分析日志可以了解服务器的负荷&#xff0c;性能安全性&#xff0c;从而及时采取措施纠正错误。 往…