先讲下为什么会需要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. 计算机网络之网络层IP组播(IGMP、组播路由选择协议、组播地址)

    文章转自:https://blog.csdn.net/weixin_43914604/article/details/105318560 学习课程:<2019王道考研计算机网络> 学习目的 ...

  2. Tenable Nessus 10.0.0 (Unix, Linux) -- #1 漏洞评估解决方案

    请访问原文链接:https://sysin.org/blog/nessus-10/,查看最新版.原创作品,转载请保留出处. 作者:gc(at)sysin.org,主页:www.sysin.org 了解 ...

  3. 性能工具之代码级性能测试工具ContiPerf

    前言 做性能的同学一定遇到过这样的场景:应用级别的性能测试发现一个操作的响应时间很长,然后要花费很多时间去逐级排查,最后却发现罪魁祸首是代码中某个实现低效的底层算法.这种自上而下的逐级排查定位的方法, ...

  4. centos yum更换阿里镜像

    #1.如果没有wget命令,则需要执行下面命令进行安装.为保险期间,先执行下面命令. yum install wget #2.备份原镜像源,以免出错后可以恢复. mv /etc/yum.repos.d ...

  5. node.js中模块和包

    node.js中模块和包 什么是模块 如何创建并加载模块 1. 创建模块 2. 单次加载 3. 覆盖 exports 如何创建一个包 1. 作为文件夹的模块 2. package.json 如何使用包 ...

  6. 论文翻译:Fullsubnet: A Full-Band And Sub-Band Fusion Model For Real-Time Single-Channel Speech Enhancement

    论文作者:Xiang Hao, Xiangdong Su, Radu Horaud, and Xiaofei Li 翻译作者:凌逆战 论文地址:Fullsubnet:实时单通道语音增强的全频带和子频带 ...

  7. python Max retries exceeded with URL in requests

    使用requests进行重试 import requests from requests.adapters import HTTPAdapter from requests.packages.urll ...

  8. 要web开发精品教程吗?免费无广告一百期连讲的那种-逐浪CMS前端开发100期入门教程全面开放

    要web开发精品教程吗?免费无广告一百期连讲的那种-逐浪CMS前端开发100期入门教程全面开放 大师主讲 经验难得 由逐浪CMS首席架构师发哥老师,亲自主理讲解. 历时一年精心打造, 汇聚了互联网诞生 ...

  9. Java核心技术--Java程序设计

    Java术语 术语名 缩写 解释 Java Development Kit(Java开发工具包) JDK 编写Java程序的程序员使用的软件 Java Runtime Environment(Java ...

  10. Django笔记&教程 5-2 进阶查询——Queryset

    Django 自学笔记兼学习教程第5章第2节--进阶查询--Queryset 点击查看教程总目录 Queryset相关内容其实蛮多的,本文只介绍一些常用的,详细的推荐查询官方文档:queryset-a ...