lua如何调用C/C++
1 lua vs C/C++
lua是脚本语言,优点是门槛低,可以热更新,缺点当然就是性能。C/C++是编译型语言,有点是性能高,但是相对的,门槛高,技术不好的人写的代码可能还没有lua的性能高,容易出现core,不能热更新。
不过,lua语言本身就是用C实现的,而且,可以将很多能力封装成lua的接口供lua调用。
2 C/C++如何给lua提供接口
查看一个lua模块的源代码会发现,lua模块的实现中既包含lua代码,也包含C代码,其中,C代码的主要逻辑就是获取参数,调用系统调用,返回值,C代码会编译为so供lua调用,而lua代码就是将C代码提供的一些接口进行再封装,以便在lua中更好用,更简单,然后再通过lua代码对外提供接口。因此,这么看起来,通过lua调用C函数,重要的就是在C中如何获取参数以及如何返回值。
下面的说明以linotify项目进行说明。
2.1 lua模块的查找
当在lua里面通过require(“inotify”)时,lua怎么知道去哪里查找inotify模块呢?此时,inotify模块是个lua脚本还是个so呢?
跟其他脚本语言类似,lua中也是通过变量来控制模块的查找的,其中package.path是搜索lua模块的路径,package.cpath是搜索so模块的路径,先查找lua模块,再查找so模块。
通过上面这种方式,在当前目录找到了inotify.so。
2.2 so的入口
要调用inotify.so中的函数,肯定还是要用动态库的函数:dlopen、dlsym。例如,当调用require(“inotify”)时,如果没有导入inotify.so,则调用dlopen加载inotify.so库,
当在lua中调用local wd = handle:addwatch('/home/rob/', inotify.IN_CREATE, inotify.IN_MOVE)时,会调用handle_add_watch()函数。
2.3 参数获取
lua和C之间是通过栈进行交互的,当调用C函数时,C函数的第一个参数是lua_State的指针,可以将它理解为lua的一个状态机。
如果要调用函数,第一步就是参数的获取,lua会将参数放到栈中,因此,inotify.so中的函数可以获取栈中的数据得到参数:
fd = get_inotify_handle(L, 1);path = luaL_checkstring(L, 2);top = lua_gettop(L);for (i = 3; i <= top; i++) {mask |= (uint32_t)luaL_checkinteger(L, i);}
get_inotify_handle()获取栈中的第1个参数,luaL_checkstring(L, 2)获取栈中的第2个参数,且第2个参数是个字符串,然后通过lua_gettop(L)获取所有的参数的个数,再用for循环将剩余的参数通过位或放到mask变量。通过这种方式就分别得到了addwatch()的三个参数。
然后再调用inotify的inotify_add_watch()完成实际的逻辑。
2.5 返回值
当具体的业务逻辑完成后,就需要将返回值传给lua,依旧是通过入栈的方式。
在这里,调用完inotify_add_watch()就得到某个监听操作的描述符,也需要将这个描述符返回,如果操作成功,调用lua_pushinteger(L, wd)将wd返回,如果操作失败,则返回3个值:
static int handle_error(lua_State *L)
{lua_pushnil(L);lua_pushstring(L, strerror(errno));lua_pushinteger(L, errno);return 3;
}
第1个是nil,第2个是错误信息,第3个是错误码。
因此,在lua中可以这样来调用:
local wd, err_info, errno = handle:addwatch('/home/rob/', inotify.IN_CREATE, inotify.IN_MOVE)
if wd == nil thenprint("ERROR=", err_info)
end
同时还需要注意handle_add_watch()函数的返回值,返回值表明了lua中函数返回值的个数。例如这里,成功时,只返回描述符,因此,函数返回值是1,失败时,多了额外的错误信息,因此,函数返回值是3。
2.6 一个小的demo
有了上面的了解,可以实现我们的一个小小的demo。
假设我们要实现一个加法操作,实际的加法操作在C中完成,然后在lua中调用。
#include <lua.h>
#include <lauxlib.h>static int handle_add(lua_State *L) {int a, b, c;a = luaL_checkinteger(L, 1);b = luaL_checkinteger(L, 2);c = a + b;lua_pushinteger(L, c);return 1;
}static luaL_Reg funcs[] = {{"add", handle_add},{NULL, NULL}
};int luaopen_demo(lua_State *L) {lua_createtable(L, 0, sizeof(funcs)/sizeof(luaL_Reg) - 1);luaL_setfuncs(L, funcs, 0);return 1;
}
那这里的入口函数就是luaopen_demo(),里面就调用了两个函数,先调用lua_createtable创建
将上面的代码编译为so:
gcc demo.c -fPIC -shared -o demo.so
lua中调用:
local demo = require("demo")print(demo.add(2, 3))
3 lua FFI
lua C API实现lua的模块使用的是虚拟栈的方式,实现起来太过麻烦,用户需要使用一种新的接口(C API)和模式(虚拟栈)实现,而使用FFI机制,就可以在lua中直接调用C函数。
3.1 一个小例子
local ffi = require("ffi")
ffi.cdef[[
int printf(const char*fmt, ...);
]]ffi.C.printf("hello %s", "world");
首先加载ffi模块,然后使用cdef添加C函数的声明,有点类似于C语言中的头文件,然后就可以调用ffi.C中的printf函数。然后就可以使用luajit编译:luajit hell.lua。
3.2 调用so
上面的例子是调用C标准库中的函数,如果需要调用其他的so文件呢?
// libtest.c
#include <stdio.h>int show(char *str) {int ret = 0;if(str == NULL) {ret = -1;} else {printf("input: %s\n", str);}return ret;
}
# 将上述代码编译为so
gcc -shared -fPIC libtest.c -o libtest.so
然后就可以在lua中调用:
local ffi = require("ffi")-- 加载libtest.so
local myffi = ffi.load("test")-- 声明函数原型
ffi.cdef[[
int show(char *str);
]]local str1 = "hello"-- 将字符串类型转换为char*
local str2 = ffi.cast("char *",str1)-- 调用libtest.so中的show函数
print(myffi.show(str2))
4 C API vs FFI
FFI相比C API最大的优势就是比较好理解,性能高,但是使用FFI也存在一些兼容性的问题;而C API由于是官方提供的接口,在稳定性方面还是很好的。
5 参考文档
- FFI Tutorial
相关文章:
lua如何调用C/C++
1 lua vs C/C lua是脚本语言,优点是门槛低,可以热更新,缺点当然就是性能。C/C是编译型语言,有点是性能高,但是相对的,门槛高,技术不好的人写的代码可能还没有lua的性能高,容易出现c…...
简单聊一聊公平锁和非公平锁,parallel并行流
目录 一、降低锁的粒度,将synchronized关键字不放在方法上了,改为synchronized代码块。二、先区分一下公平锁和非公平锁1、公平锁2、非公平锁3、公平锁的优缺点:4、非公平锁的优缺点: 三、是否对症下药四、IntStream.rangeClosed是…...
【SpringCloud】微服务技术栈入门4 - RabbitMQ初探
目录 RabbitMQ安装 rabbitmqSpringAMQP 基础队列WorkQueue路由发布订阅 FanoutExchangeDirectExchangeTopicExchange RabbitMQ 安装 rabbitmq 首先确保自己已经安装好了 docker 是 docker 拉取镜像文件:docker pull rabbitmq:3-management 拉取完毕,打…...
cefsharp(117.2.20)cef117.2.2最新体验版
一、下载nupkg https://www.nuget.org/packages/CefSharp.WinForms/ https://www.nuget.org/packages/CefSharp.Common/ https://www.nuget.org/packages/cef.redist.x64/ https://www.nuget.org/packages/cef.redist.x86/ 此版本暂时不支持H264。上一版本支持H264 cefsharp…...
layui在上传图片在前端处理图片压缩
有的人会遇到需要在前端代码处理图片压缩的问题,下面给大家分享怎么处理。 // 上传图片 var image_src var IsImgDealfalse; layui.upload.render({ elem: "#{tag}{id}", url: sessionStorage.getItem(httpUrlPrefix) /upload/uploadImage, // dataT…...
js 事件参考
事件参考 事件介绍 触发事件是为了通知代码可能影响代码执行的“有趣变化”。这些可能来自用户交互,例如使用鼠标或调整窗口大小,底层环境状态的变化(例如,低电量或来自操作系统的媒体事件)以及其他原因。 每个事件都由一个基于Event接口的…...
卷积网络的发展历史-LeNet
简介 LeNet是CNN结构的开山鼻祖,第一次定义了卷积神经网络的结构。 LeNet模型包含了多个卷积层和池化层,以及最后的全连接层用于分类。其中,每个卷积层都包含了一个卷积操作和一个非线性激活函数,用于提取输入图像的特征。池化层…...
(2023,GPT-4V,LLM,LMM,功能和应用)大型多模态模型的黎明:GPT-4V(ision) 的初步探索
The Dawn of LMMs: Preliminary Explorations with GPT-4V(ision) 公众号:EDPJ(添加 VX:CV_EDPJ 或直接进 Q 交流群:922230617 获取资料) 目录 0. 摘要 1. 简介 1.1 动机和概述 1.2 我们探索 GPT-4V 的方法 1.3…...
【C++设计模式之装饰模式:结构型】分析及示例
装饰模式(Decorator Pattern)是一种结构型设计模式,它允许在运行时动态地给一个对象添加额外的行为。 描述 装饰模式通过创建一个包装器(Wrapper)来包裹原始对象,并在原始对象的行为前后添加额外的功能。…...
绘制散点图、曲线图、折线图和环形图失败, 设置迭代次数和进度无法保存图片
错误❌ 分别input设置(我想知道微积分的力量) 设1个人,他有每天3种方案,每天进步千分之一,千分之一,十万分之一等到他们迭代 200,500,1000,2000,3000,5000,9000次 他们在图片什么位置画曲线图࿰…...
micro-ROS中对消息的内存管理
文章目录 1.背景2.答案2.1.基本类型及其数组,不需要2.1.序列类型(复合类型、复合序列类型),需要 3.内存申请方法3.1.手动申请(Manual allocation)3.1.工具辅助(micro-ROS utilities)…...
Springboot中使用拦截器、过滤器、监听器
一、Servlet、Filter(过滤器)、 Listener(监听器)、Interceptor(拦截器) Javaweb三大组件:servlet、Filter(过滤器)、 Listener(监听器) Spring…...
代码随想录二刷day45
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、力扣70. 爬楼梯二、力扣322. 零钱兑换三、力扣279. 完全平方数 前言 一、力扣70. 爬楼梯 class Solution {public int climbStairs(int n) {int[] dp new…...
泊车功能专题介绍 ———— AVP系统基础数据交互内容
文章目录 系统架构系统功能描述云端子系统车辆子系统场端子系统用户APP 工作流程基础数据交互内容AVP 系统基础数据交互服务车/用户 - 云基础数据交互内容车位查询工作流程技术要求数据交互要求 车位预约工作流程技术要求数据交互要求 取消预约工作流程技术要求数据交互要求 泊…...
蓝桥杯每日一题2023.10.6
题目描述 门牌制作 - 蓝桥云课 (lanqiao.cn) 题目分析 #include<bits/stdc.h> using namespace std; int ans; int main() {for(int i 1; i < 2020; i ){int x i;while(x){int a x % 10;if(a 2)ans ;x / 10;}}cout << ans;return 0; } 题目描述 既约分数…...
7、【Qlib】【主要组件】Data Layer:数据框架与使用
7、【主要组件】Data Layer:数据框架与使用 简介数据准备Qlib 格式数据Qlib 格式数据集自动更新日频率数据将 CSV 格式转换为 Qlib 格式股票池(市场)多股票模式 数据API数据检索特征过滤器 数据加载器QlibDataLoaderStaticDataLoaderInterfac…...
Kubernetes安装部署 1
本文主要描述kubernetes的安装部署,kubernetes的安装部署主要包括三个关键组件,其中,包括kubeadm、kubelet、kubectl,这三个组件的功能描述如下所示: Kubeadm 用于启动与管理kubernetes集群 Kubelet 运行在所有集群的…...
在VS Code中优雅地编辑csv文件
文章目录 Rainbow csv转表格CSV to Tablecsv2tableCSV to Markdown Table Edit csv 下面这些插件对csv/tsv/psv都有着不错的支持,这几种格式的主要区别是分隔符不同。 功能入口/使用方法Rainbow csv按列赋色右键菜单CSV to Table转为ASCII表格指令CSV to Markdown …...
LCR 128.库存管理 I
题目来源: leetcode题目,网址:LCR 128. 库存管理 I - 力扣(LeetCode) 解题思路: 数组可以分割成两段的升序连续子数组,找到两个子数组的开始元素并返回较小者即可。 解题代码: …...
eigen::Affine3d 转换
平移eigen::vector3d和四元数Eigen::Quaterniond 转 eigen::Affine3d Eigen::Vector3d t Eigen::Vector3d::Zero(); Eigen::Quaterniond q Eigen::Quaterniond ::Identity();Eigen::Affine3d affine3d t * q.toRotationMatrix(); Eigen::Matrix4d 转 eigen::Affine3d Eige…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...
R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...
MLP实战二:MLP 实现图像数字多分类
任务 实战(二):MLP 实现图像多分类 基于 mnist 数据集,建立 mlp 模型,实现 0-9 数字的十分类 task: 1、实现 mnist 数据载入,可视化图形数字; 2、完成数据预处理:图像数据维度转换与…...
第14节 Node.js 全局对象
JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。 在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局…...
华为OD机考- 简单的自动曝光/平均像素
import java.util.Arrays; import java.util.Scanner;public class DemoTest4 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint[] arr Array…...
宠物车载安全座椅市场报告:解读行业趋势与投资前景
一、什么是宠物车载安全座椅? 宠物车载安全座椅是一种专为宠物设计的车内固定装置,旨在保障宠物在乘车过程中的安全性与舒适性。它通常由高强度材料制成,具备良好的缓冲性能,并可通过安全带或ISOFIX接口固定于车内。 近年来&…...
