先讲下为什么会需要lua_close回调吧。

我用C++给lua写过不少库,其中有一些,是C++依赖堆内存,并且是每一个lua对象使用一块单独的内存来使用的。

在之前,我一直都是魔改lua源代码,给lua_State结构添加新的成员来进行快速引用,并修改了lua_close的源代码,添加了回调函数,使lua在对象关闭时顺便把C++分配的内存也回收掉。

然而随着有相同需求的库不断增多,我随时需要调整lua的源代码的次数也在不断增加,这反而成了一种负担。

最重要的是,通过修改lua源码的方式,在使用lua_newthread来作为调用栈对象时,我需要自行区分这些C++对象的引用管理。

所以我一直在寻求一种能够在lua对象被关闭时,把我在C++为它申请的内存也释放掉的机制。

随着对lua的不断深入理解,我发现可以有这种方式。

原理:

给一个table设置__gc回调,然后将其直接放到注册表,就这么简单。

这个table会在vm对象被lua_close中进行回收,回收的同时回调我们指定的回调函数。

这代码,简直不要太简单,要不是搜索不到,我真的都不好意思发:

#include <iostream>
#include "lua.hpp"
#pragma comment(lib, "lua54.lib") int Myref = 0; static int on_lua_close(lua_State *s) { printf("on_lua_close->top = %d\n", lua_gettop(s)); lua_rawgeti(s, LUA_REGISTRYINDEX, Myref);
char* p = (char*)lua_touserdata(s, -1);
printf("on_lua_close->p = %I64X\n", p);
delete p;
lua_pop(s, 1);
printf("on_lua_close->top = %d\n", lua_gettop(s)); return 0;
} int main()
{
lua_State* s = luaL_newstate();
luaL_openlibs(s); // 创建第一个table
lua_newtable(s); // 创建第二个table用于构建元表
lua_newtable(s);
lua_pushcfunction(s, on_lua_close);//回调函数压栈
lua_setfield(s, -2, "__gc");//key命名为"__gc",设置完之后会自己弹出栈 // 将第2个table设置为第一个table的元表,设置完之后第二个表就弹出了,之后栈里就只剩第一个table
lua_setmetatable(s, -2); // 然后将第一个表放到注册表引用,引用完弹栈
luaL_ref(s, LUA_REGISTRYINDEX); // 下面是简单的测试
char* p = new char[10000];
lua_pushlightuserdata(s, p);
Myref = luaL_ref(s, LUA_REGISTRYINDEX);
printf("top=%d\n", lua_gettop(s)); // 各种东西处理好之后,此时此处top应为0 printf("p = %I64X, Myref = %d\n", (__int64)p, Myref); lua_close(s);
return 0;
}

那么,利用C++11的lambda函数,再结合C++的萃取机制,我们可以将任意需要释放的堆内存,完美捆绑到lua_close:

#include <iostream>
#include "lua.hpp"
#pragma comment(lib, "lua54.lib") template<typename _Ty>
void lua_autofree(lua_State* s, _Ty *p) {
lua_newtable(s); //指针入lua栈
lua_pushlightuserdata(s, p); //然后将其设置为数字key 1,lua的线性数组比string key速度要快一些,所以推荐这么干
lua_rawseti(s, -2, 1); lua_newtable(s);
lua_pushcfunction(s, [](lua_State* s)->int{
lua_rawgeti(s, 1, 1);
_Ty *ptr = (_Ty*)lua_touserdata(s, -1);
lua_pop(s, 1); //这个printf仅针对下面的int*的测试例子
printf("%I64X,%d\n", ptr, *ptr); delete ptr;
return 0;
});
lua_setfield(s, -2, "__gc");
lua_setmetatable(s, -2); luaL_ref(s, LUA_REGISTRYINDEX);
} int main()
{
lua_State* s = luaL_newstate();
luaL_openlibs(s); int *n = new int;
*n = 123;
printf("%I64X\n", n); lua_autofree(s, n); lua_close(s);
return 0;
}

给lua_close实现回调函数的更多相关文章

  1. 小兔JS教程(三)-- 彻底攻略JS回调函数

    这一讲来谈谈回调函数. 其实一句话就能概括这个东西: 回调函数就是把一个函数当做参数,传入另一个函数中.传进去的目的仅仅是为了在某个时刻去执行它. 如果不执行,那么你传一个函数进去干嘛呢? 就比如说对 ...

  2. 嵌入式&iOS:回调函数(C)与block(OC)传 参/函数 对比

    C的回调函数: callBack.h 1).声明一个doSomeThingCount函数,参数为一个(无返回值,1个int参数的)函数. void DSTCount(void(*CallBack)(i ...

  3. 嵌入式&iOS:回调函数(C)与block(OC)回调对比

    学了OC的block,再写C的回调函数有点别扭,对比下区别,回忆记录下. C的回调函数: callBack.h 1).定义一个回调函数的参数数量.类型. typedef void (*CallBack ...

  4. 理解 JavaScript 回调函数并使用

    JavaScript中,函数是一等(first-class)对象:也就是说,函数是 Object 类型并且可以像其他一等对象(String,Array,Number等)一样使用.它们可以"保 ...

  5. 关于js的回调函数的一点看法

    算了一下又有好几个月没写博客了,最近在忙公司android的项目,所以也就很少抽时间来写些东西了.刚闲下来,我就翻了翻之前看的东西.做了android之后更加感觉到手机端开发的重要性,现在做nativ ...

  6. JS学习:第二周——NO.1回调函数

    [回调函数] 定义:把一个函数的定义阶段,作为参数,传给另一个函数: 回调函数调用次数,取决于条件: 回调函数可以传参: 回调函数可以给变this指向,默认是window: 回调函数没有返回值,for ...

  7. 【java回调】java两个类之间的回调函数传递

    背景交代:熟悉用js开发的cordovaAPP:对java一窍不通的我,老师让做一个监测用户拍照事件的功能,无奈没有找到现成的库,无奈自己动手开发java插件~~0基础java GreenHand,祝 ...

  8. Java|今天起,别再扯订阅和回调函数

    编程史上有两个令人匪夷所思的说辞,一个是订阅,一个是回调函数. 我想应该还有很多同学为“事件的订阅”和“回调函数”所困扰,因为事情本来就不应该按这个套路来解释. 多直白,所谓的“回调函数”你完全可以线 ...

  9. C++ 回调函数的定义与用法

    一回调函数 我们经常在C++设计时通过使用回调函数可以使有些应用(如定时器事件回调处理.用回调函数记录某操作进度等)变得非常方便和符合逻辑,那么它的内在机制如何呢,怎么定义呢?它和其它函数(比如钩子函 ...

随机推荐

  1. 微信小程序的实现原理

    一.背景 网页开发,渲染线程和脚本是互斥的,这也是为什么长时间的脚本运行可能会导致页面失去响应的原因,本质就是我们常说的 JS 是单线程的 而在小程序中,选择了 Hybrid 的渲染方式,将视图层和逻 ...

  2. Spring Cloud Alibaba 介绍及工程准备

    简介 SpringCloud Alibaba是阿里巴巴集团开源的一套微服务架构解决方案. 微服务架构是为了更好的分布式系统开发,将一个应用拆分成多个子应用,每一个服务都是可以独立运行的子工程.其中涵盖 ...

  3. 2021.9.12考试总结[NOIP模拟51]

    T1 茅山道术 仔细观察发现对于每个点只考虑它前面第一个与它颜色相同的点即可. 又仔细观察发现对一段区间染色后以这个区间内点为端点的区间不能染色. 于是对区间右端点而言,区间染色的贡献为遍历到区间左端 ...

  4. ubuntn 一直循环登录界面 (卸载nvidia驱动)

    由于在Ubuntu下安装了Nvidia显卡驱动后开机一直处于循环登录界面,密码输入正确也是进不去,然后就决定卸载Nvidia显卡驱动.首先是在能使用tty1登录的情况下,使用 $ sudo apt-g ...

  5. hdu 4288 Coder(单点操作,查询)

    题意: 三种操作: 1. add x – add the element x to the set;2. del x – remove the element x from the set;3. su ...

  6. cm1 逆向分析

    目录 cm1 逆向分析 前言 查壳分析 逆向分析 代码分析 qmemcpy分析 sub_401020函数分析 sub_401050函数分析 加密算法分析 POC代码编写 cm1 逆向分析 前言 还是先 ...

  7. JVM-内存区域与OOM

    本篇博客内容主要参考<深入理解Java虚拟机> 内存区域与内存溢出异常 运行时数据区 Java虚拟机运行时数据区: 程序计数器(Program Counter Register)是一块较小 ...

  8. swoole、swoft环境配置

    一.服务器环境 1.lnmp wget http://soft.vpser.net/lnmp/lnmp1.5.tar.gz -cO lnmp1.5.tar.gz && tar zxf ...

  9. 深入探索 Linux listen() 函数 backlog 的含义

    1:listen()回顾以及问题引入 2:正确的解释 3:实验验证 1:listen()回顾以及问题引入 listen()函数是网络编程中用来使服务器端开始监听端口的系统调用,首先来回顾下listen ...

  10. docker容器运行java后台程序,存到数据库的时间差一天的问题

    主要原因是docker容器中的时间用的是标准时间,不是用的宿主机的时间. 修改方法: docker run -e TZ="Asia/Shanghai" -d -p 80:80 -- ...