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