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

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是脚本语言&#xff0c;优点是门槛低&#xff0c;可以热更新&#xff0c;缺点当然就是性能。C/C是编译型语言&#xff0c;有点是性能高&#xff0c;但是相对的&#xff0c;门槛高&#xff0c;技术不好的人写的代码可能还没有lua的性能高&#xff0c;容易出现c…...

简单聊一聊公平锁和非公平锁,parallel并行流

目录 一、降低锁的粒度&#xff0c;将synchronized关键字不放在方法上了&#xff0c;改为synchronized代码块。二、先区分一下公平锁和非公平锁1、公平锁2、非公平锁3、公平锁的优缺点&#xff1a;4、非公平锁的优缺点&#xff1a; 三、是否对症下药四、IntStream.rangeClosed是…...

【SpringCloud】微服务技术栈入门4 - RabbitMQ初探

目录 RabbitMQ安装 rabbitmqSpringAMQP 基础队列WorkQueue路由发布订阅 FanoutExchangeDirectExchangeTopicExchange RabbitMQ 安装 rabbitmq 首先确保自己已经安装好了 docker 是 docker 拉取镜像文件&#xff1a;docker pull rabbitmq:3-management 拉取完毕&#xff0c;打…...

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在上传图片在前端处理图片压缩

有的人会遇到需要在前端代码处理图片压缩的问题&#xff0c;下面给大家分享怎么处理。 // 上传图片 var image_src var IsImgDealfalse; layui.upload.render({ elem: "#{tag}{id}", url: sessionStorage.getItem(httpUrlPrefix) /upload/uploadImage, // dataT…...

js 事件参考

事件参考 事件介绍 触发事件是为了通知代码可能影响代码执行的“有趣变化”。这些可能来自用户交互&#xff0c;例如使用鼠标或调整窗口大小&#xff0c;底层环境状态的变化(例如&#xff0c;低电量或来自操作系统的媒体事件)以及其他原因。 每个事件都由一个基于Event接口的…...

卷积网络的发展历史-LeNet

简介 LeNet是CNN结构的开山鼻祖&#xff0c;第一次定义了卷积神经网络的结构。 LeNet模型包含了多个卷积层和池化层&#xff0c;以及最后的全连接层用于分类。其中&#xff0c;每个卷积层都包含了一个卷积操作和一个非线性激活函数&#xff0c;用于提取输入图像的特征。池化层…...

(2023,GPT-4V,LLM,LMM,功能和应用)大型多模态模型的黎明:GPT-4V(ision) 的初步探索

The Dawn of LMMs: Preliminary Explorations with GPT-4V(ision) 公众号&#xff1a;EDPJ&#xff08;添加 VX&#xff1a;CV_EDPJ 或直接进 Q 交流群&#xff1a;922230617 获取资料&#xff09; 目录 0. 摘要 1. 简介 1.1 动机和概述 1.2 我们探索 GPT-4V 的方法 1.3…...

【C++设计模式之装饰模式:结构型】分析及示例

装饰模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许在运行时动态地给一个对象添加额外的行为。 描述 装饰模式通过创建一个包装器&#xff08;Wrapper&#xff09;来包裹原始对象&#xff0c;并在原始对象的行为前后添加额外的功能。…...

绘制散点图、曲线图、折线图和环形图失败, 设置迭代次数和进度无法保存图片

错误❌ 分别input设置&#xff08;我想知道微积分的力量&#xff09; 设1个人&#xff0c;他有每天3种方案&#xff0c;每天进步千分之一&#xff0c;千分之一&#xff0c;十万分之一等到他们迭代 200,500,1000,2000,3000,5000,9000次 他们在图片什么位置画曲线图&#xff0…...

micro-ROS中对消息的内存管理

文章目录 1.背景2.答案2.1.基本类型及其数组&#xff0c;不需要2.1.序列类型&#xff08;复合类型、复合序列类型&#xff09;&#xff0c;需要 3.内存申请方法3.1.手动申请&#xff08;Manual allocation&#xff09;3.1.工具辅助&#xff08;micro-ROS utilities&#xff09;…...

Springboot中使用拦截器、过滤器、监听器

一、Servlet、Filter&#xff08;过滤器&#xff09;、 Listener&#xff08;监听器&#xff09;、Interceptor&#xff08;拦截器&#xff09; Javaweb三大组件&#xff1a;servlet、Filter&#xff08;过滤器&#xff09;、 Listener&#xff08;监听器&#xff09; Spring…...

代码随想录二刷day45

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣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&#xff1a;数据框架与使用 简介数据准备Qlib 格式数据Qlib 格式数据集自动更新日频率数据将 CSV 格式转换为 Qlib 格式股票池&#xff08;市场&#xff09;多股票模式 数据API数据检索特征过滤器 数据加载器QlibDataLoaderStaticDataLoaderInterfac…...

Kubernetes安装部署 1

本文主要描述kubernetes的安装部署&#xff0c;kubernetes的安装部署主要包括三个关键组件&#xff0c;其中&#xff0c;包括kubeadm、kubelet、kubectl&#xff0c;这三个组件的功能描述如下所示&#xff1a; Kubeadm 用于启动与管理kubernetes集群 Kubelet 运行在所有集群的…...

在VS Code中优雅地编辑csv文件

文章目录 Rainbow csv转表格CSV to Tablecsv2tableCSV to Markdown Table Edit csv 下面这些插件对csv/tsv/psv都有着不错的支持&#xff0c;这几种格式的主要区别是分隔符不同。 功能入口/使用方法Rainbow csv按列赋色右键菜单CSV to Table转为ASCII表格指令CSV to Markdown …...

LCR 128.库存管理 I

​题目来源&#xff1a; leetcode题目&#xff0c;网址&#xff1a;LCR 128. 库存管理 I - 力扣&#xff08;LeetCode&#xff09; 解题思路&#xff1a; 数组可以分割成两段的升序连续子数组&#xff0c;找到两个子数组的开始元素并返回较小者即可。 解题代码&#xff1a; …...

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…...

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

TCP/IP 网络编程 | 服务端 客户端的封装

设计模式 文章目录 设计模式一、socket.h 接口&#xff08;interface&#xff09;二、socket.cpp 实现&#xff08;implementation&#xff09;三、server.cpp 使用封装&#xff08;main 函数&#xff09;四、client.cpp 使用封装&#xff08;main 函数&#xff09;五、退出方法…...

2025年- H71-Lc179--39.组合总和(回溯,组合)--Java版

1.题目描述 2.思路 当前的元素可以重复使用。 &#xff08;1&#xff09;确定回溯算法函数的参数和返回值&#xff08;一般是void类型&#xff09; &#xff08;2&#xff09;因为是用递归实现的&#xff0c;所以我们要确定终止条件 &#xff08;3&#xff09;单层搜索逻辑 二…...

js 设置3秒后执行

如何在JavaScript中延迟3秒执行操作 在JavaScript中&#xff0c;要设置一个操作在指定延迟后&#xff08;例如3秒&#xff09;执行&#xff0c;可以使用 setTimeout 函数。setTimeout 是JavaScript的核心计时器方法&#xff0c;它接受两个参数&#xff1a; 要执行的函数&…...