給lua_close實現回調函數


先講下為什么會需要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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM