如何在 package.preload 函数中破坏 C++ class

How to destruct C++ class inside package.preload function

我在使用 SWIG 包装的 Lua 中使用 C++ classes。

由于我使用的是单个 Lua_State,因此我希望能够在不调用 lua_close(L).[=29= 的情况下释放 Lua 脚本的特定块中的变量]

我决定使用 package.preload['name'] 以便在需要时可以使用 require 'name' 从其他块访问一个块。

有人告诉我,package.preload 函数中的变量在我执行以下操作后被释放:

package.preload['name'] = nil
package.loaded['name'] = nil

但是,似乎我的自定义 C++ classes 即使在这之后也没有被破坏。

这是我的完整示例代码:

Main.cpp

#include "Main.h"

int main()
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    luaopen_my(L);
    lua_settop(L, 0);
    luaL_dostring(L, "package.preload['test'] = function ()\n"
                         "local test = {}\n"
                         "local class = my.Class()\n"
                         "return test\n"
                     "end\n");
    luaL_dostring(L, "require 'test'");
    luaL_dostring(L, "package.preload['test'] = nil\n"
                     "package.loaded['test'] = nil\n");
}

Main.h

#include "lua.hpp"

extern "C"
{
    int luaopen_my(lua_State *L);
}
int main();

MyBindings.h

#include "Main.h"

class Class
{
public:
    Class()
    {
        std::cout << "Class Constructed" << std::endl;
    };
    ~Class()
    {
        std::cout << "Class Destructed" << std::endl;
    };
};

MyBindings.i(SWIG界面生成MyBindings.cpp

%module my
%{
    #include "MyBindings.h"
%}

%include <stl.i>
%include <typemaps.i>

%include "MyBindings.h"

结果:

Class Constructed

为什么 class 析构函数没有被调用以及如何正确地析构 package.preload 函数中的 classes 和变量?

您始终可以通过 obj.~class() 显式调用 d'tor。 如果您在 Lua 包装器中使用外部 classes,我不认为他们可以遵循 C++ 中存在的正确嵌套 d'tor 范例。

我无法重现您的问题,但您的代码中还有其他缺点,最明显的是缺少 header 守卫。文件 Main.h 不是必需的,在 MyBindings.h 文件中更是如此,因为它不使用它。我不知道您使用的是哪个编译器,但 void main() 不是有效的 C++,标准规定 int main().

Main.cpp

#include "lua.hpp"

extern "C" int luaopen_my(lua_State *L);

int main() {
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    luaopen_my(L);
    lua_settop(L, 0);
    luaL_dostring(L, "package.preload['test'] = function ()\n"
                         "local test = {}\n"
                         "local class = my.Class()\n"
                         "return test\n"
                     "end\n");
    luaL_dostring(L, "require 'test'");
    luaL_dostring(L, "package.preload['test'] = nil\n"
                     "package.loaded['test'] = nil\n");
    lua_close(L);
}

MyBindings.h

#pragma once
#include <iostream>

class Class
{
public:
    Class()
    {
        std::cout << "Class Constructed" << std::endl;
    };
    ~Class()
    {
        std::cout << "Class Destructed" << std::endl;
    };
};

MyBindings.i

%module my
%{
#include "MyBindings.h"
%}

%include "MyBindings.h"

调用示例:

$ swig -c++ -lua MyBindings.i
$ clang++ -Wall -Wextra -Wpedantic -I /usr/include/lua5.2 -fPIC -shared MyBindings_wrap.cxx -o my.so -llua5.2
$ clang++ -Wall -Wextra -Wpedantic -I /usr/include/lua5.2 -L . Main.cpp -l:my.so -llua5.2
$ LD_LIBRARY_PATH=. ./a.out 
Class Constructed
Class Destructed

您还应该注意 Lua 是一种垃圾收集语言,即当垃圾收集器认为有必要时,析构函数将 运行。您可以在 Lua 中使用 lua_gc in C or using collectgarbage 手动 运行 垃圾收集器,但 我强烈建议不要 运行 手动设置垃圾收集器 通常会对性能产生负面影响(即使您 运行 它手动希望提高性能)。只有当您 运行 在内存 非常 有限的环境中并且您刚刚 p运行ed 一个 table 或类似的东西。

无论如何,我已经在Lua中为您准备了使用上面编译的my.so模块的示例。

local my = require("my")
local x = my.Class()
print("Info: Deleting x")
x = nil
print("Info: Collecting garbage")
collectgarbage()
print("Info: Done :-)")
$ lua5.2 test.lua
Class Constructed
Info: Deleting x
Info: Collecting garbage
Class Destructed
Info: Done :-)