Lua进阶用法之Lua和C的接口设计

一:lua/c的接口编程

        首先skynet、openresty 都是深度使用 lua 语言的典范;学习 lua 不仅仅要学习基本用法,还要学会使用 c 与 lua 交互,这样才学会了 lua 作为胶水语言的精髓,下面看一下他们两个的调用过程。

虚拟栈:

1.栈中只能存放 lua 类型的值,如果想用 c 的类型存储在栈中,需要将 c 类型转换为 lua 类型;

2.lua 调用 c 的函数都得到一个新的栈,独立于之前的栈;
3.c 调用 lua,每一个协程都有一个栈;一个 lua 虚拟机中可以有多个协程,但同时只能有一个协程在运行;
4.c 创建虚拟机时,伴随创建了一个主协程,默认创建一个虚拟栈;
5.无论何时 Lua 调用 C , 它都只保证至少有 LUA_MINSTACK 这么多的堆栈空间可以使用。 LUA_MINSTACK 一般被定义为 20 ,因此,只要你不是不断的把数据压栈, 通常你不用关心堆栈大小。

        对于Lua调CC调Lua他俩的情况是不同的,我们在Lua调C的过程中,我们不需要去维护这个创建的栈,因为一旦调用 c 的函数都会得到一个新的栈,并且系统会自动释放栈的空间,所以不需要我们去维护。但是对于C调Lua来说的话,需要我们去维护这个栈的大小,避免过大。

闭包和上值:

对于闭包和上值对于Lua来说还是很重要的,大家可以看看这位博主的讲解,十分清楚。

Lua闭包和Upvalue上值-CSDN博客

二:Lua和C的具体代码实现

        我们上面说了我们会C调Lua,然后Lua再调C,因此我们将他们都拆开一个一个查看:

1:C调用Lua

        C和Lua的区别:我们一旦修改C的代码,那么我们就要重新进行编译,才能进行使用,而Lua不同,它不需要进行重新编译,只需要重新启动一下即可使用,因此对于一些场景来说,我们一些固定的场合使用C语言来实现,而经常改动的比如游戏代码,我们可以使用Lua来写,我们就不用每次都进行编译浪费时间了,这样极大的提高了写代码的效率。因此我们如果在代码中实现文件的重新启动,那么我们就实现了热更新操作。

#include <stdio.h>
#include <stdlib.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>//因为Lua是动态语言,我们可以实现热更新
//c调用Lua函数
static void
call_func_0(lua_State *L, const char* funcname)
{lua_getglobal(L, funcname);     //从Lua中的全局函数,找到这个名称的函数lua_pushinteger(L, 1);          // 压入一个整数到栈顶lua_call(L, 1, 0);              // 调用函数,参数为1,返回值为0
}//通过c调用LUa的函数
//c调用Lua的话,我们需要维护这个栈个数,因为c调用Lua是同时只有一个协程在工作,所以只有一个栈
//而Lua调c,就不需要维护,只需要注意别一直压栈就可以了,因为Lua每调用一次,就会重新生成一个新的栈。
int main(int argc, char** argv)
{lua_State *L = luaL_newstate();     // 初始化lua虚拟机luaL_openlibs(L);                   // 加载lua标准库if (argc > 1) {                     // 加载lua脚本lua_pushboolean(L, 1);          // 压入一个布尔值到栈顶lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");        // 把栈顶的值设置到注册表中,可以在多个C库中共享数据if ( LUA_OK != luaL_dofile(L, argv[1]) ) {              // 加载lua脚本到虚拟机中const char* err = lua_tostring(L, -1);              // 获取错误信息fprintf(stderr, "err:\t%s\n", err);                 return 1;}call_func_0(L, "Init");                //开始调用Lua的代码call_func_0(L, "Loop");call_func_0(L, "Release");lua_close(L);}return 0;
}

2:被C调用的Lua代码

package.cpath = "luaclib/?.so"
local so = require "uv.c"        --这里准备调用C语言中的库函数--这里又通过Lua调用C的函数
function Init(args)print("call [init] function", args)
endfunction Loop()print("call [loop] function")for k, v in ipairs({1,2,3,4,5}) doso.echo(v)        --这个echo函数就是通过调用C库里的函数,也就是Lua调用Cend
endfunction Release()print("call [release] function")
end

        在讲被Lua调用的C库的时候,我们先看一下Lua是怎么查找C库中的函数的。在下面的代码中,类似于C中的#include,用来加载头文件的,

local so = require "tbl.c"        --这里准备调用C语言中的库函数

 当Lua找到此代码后从C库中开始查找luaopen_tbl_c,因此他是有查找的规则的。在下面有讲到。

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdio.h>//我们C写的函数
static int
lecho (lua_State *L) {const char* str = lua_tostring(L, -1);  fprintf(stdout, "%s\n", str);return 0;
}static const luaL_Reg l[] = {// 导出给lua使用数组{"echo", lecho},        //K:函数名,V:函数指针,存放我们自己的函数名{NULL, NULL},
};//相当于创建一个表(或者叫栈),然后将变量或者函数压入栈中,后面的数字为索引
//命名规则:luaopen_模块名,当Lua加载C库时,会先搜索这个函数,然后调用这个函数,返回一个表,然后把这个表赋值给模块名,然后就可以使用了。
int
luaopen_tbl_c(lua_State *L) { // local tbl = require "tbl.c"// 创建一张新的表,并预分配足够保存下数组 l 内容的空间// luaL_newlibtable(L, l);// luaL_setfuncs(L, l, 0);      //注册函数到表中,0表示函数表的索引为0// luaL_newlib 函数可以创建一个新的表,然后把函数表 l 中的函数注册到表中,然后把表返回给LualuaL_newlib(L, l);      // 等价于上面两行return 1;
}

3:被Lua调用的C库

我们正常的函数,当使用完,他的内存会被回收,里面的变量的值也会消失,但是闭包不同,他还是保存变量的值。并且在这个函数中共享。

#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdio.h>
// 闭包实现:  函数 + 上值  luaL_setfuncs
// lua_upvalueindex(1)
// lua_upvalueindex(2)//闭包可以在函数内部进行数据共享,因为上值是大家都可以使用的
static int
lecho (lua_State *L) {lua_Integer n = lua_tointeger(L, lua_upvalueindex(1));      // 取出上值,然后把栈顶的元素替换掉上值n++;const char* str = lua_tostring(L, -1);     //-1 表示栈顶的元素     fprintf(stdout, "[n=%lld]---%s\n", n, str);     //输出到标准输出lua_pushinteger(L, n);                      // 把n压入栈顶lua_replace(L, lua_upvalueindex(1));        // 把栈顶的元素替换成上值return 0;
}static const luaL_Reg l[] = {{"echo", lecho},{NULL, NULL},
};//我们压入这个栈之后,变量是在栈中的,所以我们可以把这个栈中的值保存下来,然后在函数中使用,但是是看不到的,所以我们需要使用上值
//命名规则:luaopen_模块名,当Lua加载C库时,会先搜索这个函数,然后调用这个函数,返回一个表,然后把这个表赋值给模块名,然后就可以使用了。
int
luaopen_uv_c(lua_State *L) { // local tbl = require "tbl.c"// 1. 创建一张新的表,并预分配足够保存下数组 l 内容的空间luaL_newlibtable(L, l);// 1     创建一个新的表lua_pushinteger(L, 0);// 2       压入一个整数到栈顶luaL_setfuncs(L, l, 1);// 上值    把表和函数表关联起来,把栈顶的函数表和栈顶的表关联起来,然后返回Lua一个表// luaL_newlib(L, l);return 1;
}

我们在这里可以看到上值的作用:

package.cpath = "luaclib/?.so"
local so = require "uv.c"so.echo("hello world1")
so.echo("hello world2")
so.echo("hello world3")
so.echo("hello world4")
so.echo("hello world5")
so.echo("hello world6")
so.echo("hello world7")
so.echo("hello world8")
so.echo("hello world9")

        我们在这里明明没有传入任何数字,并且没有看到任何数组,只是在3中我们压入了一个上值,然后根据闭包,我们的数值会一直增长,并且在函数内这个上值是共享的。

[n=1]---hello world1
[n=2]---hello world2
[n=3]---hello world3
[n=4]---hello world4
[n=5]---hello world5
...

本篇主要讲解了Lua和C之间的接口设计,加入了Lua是动态语言的作用。0voice · GitHub

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

macOS 下的 ARM 裸机嵌入式开发入门- 第二部分:实现第一个裸机应用并且调试

1、准备二进制运行程序镜像 利用 QEMU 仿真一个完整的系统&#xff0c;并创建最简单的“Hello world!”示例。 QEMU 模拟器支持 VersatilePB 平台&#xff0c;该平台包含一个 ARM926EJ-S 核心&#xff0c;以及其他外设&#xff0c;四个 UART 串行端口&#xff1b;特别是第一个…

【网络面试篇】其他面试题——Cookie、Session、DNS、CDN、SSL/TLS、加密概念

目录 一、HTTP 相关问题 1. Cookie 和 Session 是什么&#xff1f; &#xff08;1&#xff09;Cookie &#xff08;2&#xff09;Session 2. Cookie 的工作原理&#xff1f; 3. Session 的工作原理&#xff1f; 4. Cookie 和 Session 有什么区别&#xff1f; 二、其他问…

【数值分析】复习1---牛顿迭代法

首先&#xff0c;我们先来回顾一下牛顿迭代法的概念。 这里注意的是&#xff0c;牛顿迭代法是一种线性方法&#xff0c;它在点 x k x_k xk​处进行线性展开&#xff0c;而且展开成一阶泰勒公式&#xff01;注意是一阶&#xff0c;不是二阶&#xff0c;不是更高阶&#xff0c;所…

文本语义分块、RAG 系统的分块难题:小型语言模型如何找到最佳断点

文本语义分块、RAG 系统的分块难题&#xff1a;小型语言模型如何找到最佳断点&#xff1f; 转自jina最新的关于文本语义分块的分享和模型 之前我们聊过RAG 里文档分块 (Chunking) 的挑战&#xff0c;也介绍了 迟分 (Late Chunking) 的概念&#xff0c;它可以在向量化的时候减…

PostgreSQL中如果有Left Join的时候索引怎么加

在PostgreSQL中&#xff0c;当你的查询包含多个LEFT JOIN和WHERE条件时&#xff0c;合理地添加索引可以显著提高查询性能。以下是一些具体的优化步骤和建议&#xff1a; 1. 分析查询 使用 EXPLAIN ANALYZE 命令分析你的查询&#xff0c;了解查询的执行计划&#xff0c;识别出连…

温度虽寒,其道犹变:OpenAI接口之温度参数设置为0,为何每次回复仍有不确定性?

问题描述 调用openai API&#xff0c;使用templature 0&#xff0c;每次返回的内容仍有一些不同 >>> client OpenAI( ... api_keyapi_key, ... base_urlapi_base) #第一次尝试 >>> response client.chat.completions.create(mo…

vue-h5:在h5中实现相机拍照加上身份证人相框和国徽框

参考&#xff1a; https://blog.csdn.net/weixin_45148022/article/details/135696629 https://juejin.cn/post/7327353533618978842?searchId20241101133433B2BB37A081FD6A02DA60 https://www.freesion.com/article/67641324321/ https://github.com/AlexKratky/vue-camer…

国标GB28181视频平台EasyCVR私有化部署视频平台对接监控录像机NVR时,录像机“资源不足”是什么原因?

EasyCVR视频融合云平台&#xff0c;是TSINGSEE青犀视频“云边端”架构体系中的“云平台”系列之一&#xff0c;是一款针对大中型项目设计的跨区域、网络化、视频监控综合管理系统平台&#xff0c;通过接入视频监控设备及视频平台&#xff0c;实现视频数据的集中汇聚、融合管理、…

【Android、IOS、Flutter、鸿蒙、ReactNative 】标题栏

Android 标题栏 参考 Android Studio版本 配置gradle镜像 阿里云 Android使用 android:theme 显示标题栏 添加依赖 dependencies {implementation("androidx.appcompat:appcompat:1.6.1")implementation("com.google.android.material:material:1.9.0")…

歌尔微拟赴港IPO,揭示AI+终端升级的供给革命

1959年&#xff0c;美国物理学家理查德费曼在他著名的演讲“底部有足够的空间”中&#xff0c;首次提出了将机器小型化到原子和分子尺度的想法。这个充满想象力的观点&#xff0c;为世界科技发展开启了一扇新的窗口。 时至今日&#xff0c;应这一理念而生的MEMS产品已经成为各…

ROS第七梯:ROS+VSCode+Python环境配置

第一步:Python版本的ROS项目和C++版本的ROS项目前期创建功能包的步骤基本一致,具体可参考第二章。 第二步:在功能包的目录下创建一个与src目录平级的文件夹,名称写作scripts: 第三步:在scripts文件夹下创建python的节点代码文件,此处以一个订阅节点代码文件为例:

洛谷解题日记||基础篇3

#include <iostream> #include <iomanip> // 用于设置输出格式 using namespace std;double a, b, c, d;// 定义方程 f(x) ax^3 bx^2 cx d double fc(double x) {return a * x * x * x b * x * x c * x d; }int main() {double l, r, m, x1, x2;int s 0;/…

软件测试学习记录 Day1

根据黑马程序员最新版的软件测试课程所做的笔记&#xff0c;需要原件后台私信&#xff1a; 练习提取测试点&#xff1a; 博主的答案&#xff0c;有不一样看法的可评论区讨论&#xff1a;

代码随想录刷题记录(二十七)——55. 右旋字符串

&#xff08;一&#xff09;问题描述 55. 右旋字符串&#xff08;第八期模拟笔试&#xff09;https://kamacoder.com/problempage.php?pid1065字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k&#xff0c;请编写一个函数&…

FreeRTOS 24:事件组EventGroup等待、清零、获取操作

等待事件标志位xEventGroupWaitBits() 既然标记了事件的发生&#xff0c;那么我怎么知道他到底有没有发生&#xff0c;这也是需要一个函数来获 取 事 件 是 否 已 经 发 生 &#xff0c; FreeRTOS 提 供 了 一 个 等 待 指 定 事 件 的 函 数 — — xEventGroupWaitBits()&…

信息安全数学基础(47)域的结构

一、域的定义 设F为一个非空集合&#xff0c;在其上定义两种运算&#xff1a;加法和乘法。如果这两种运算在集合上封闭&#xff0c;且满足以下条件&#xff0c;则称F对于规定的乘法和加法构成一个域&#xff1a; F中所有元素对于加法形成加法交换群&#xff0c;即加法满足交换律…

#渗透测试#SRC漏洞挖掘#CSRF漏洞的防御

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…

HarmonyOS 沉浸式状态实现的多种方式

1. HarmonyOS 沉浸式状态实现的多种方式 HarmonyOS 沉浸式状态实现的多种方式 1.1. 方法一 1.1.1. 实现讲解 &#xff08;1&#xff09;首先设置setWindowLayoutFullScreen(true)&#xff08;设置全屏布局&#xff09;。   布局将从屏幕最顶部开始到最底部结束&#xff0c…

在API接口数据获取过程中,如何确保数据的安全性和隐私性?

在API接口数据获取过程中&#xff0c;确保数据的安全性和隐私性至关重要。以下是一些关键措施&#xff0c;可以帮助开发者和管理者保护API接口的数据安全和隐私性&#xff1a; 身份认证和授权 身份认证&#xff1a;确认用户身份的过程&#xff0c;常用的身份认证方式包括用户…

C++常用的特性-->day05

友元的拓展语法 声明一个类为另外一个类的友元时&#xff0c;不再需要使用class关键字&#xff0c;并且还可以使用类的别名&#xff08;使用 typedef 或者 using 定义&#xff09;。 #include <iostream> using namespace std;// 类声明 class Tom; // 定义别名 using …