首页 > 解决方案 > 通过 Lua 包装器访问 C++ 类状态会产生垃圾值

问题描述

我正在尝试通过 Lua 的 C API 向 Lua 公开一个简单的 C++ 类。我知道有些库已经解决了这个问题,但我想了解如何仅使用 C API 来解决此问题。

这是我的程序:

#define LUA_LIB

#include <cassert>

extern "C"
{
  #include "lua.h"
  #include "lauxlib.h"
}

namespace
{

class Foo
{
public:
  Foo(int state)
  : _state(state)
  {}

  int get_state() const
  { return _state; }

private:
  int _state;
};

int foo_new(lua_State *L)
{
  assert(lua_gettop(L) == 2);

  luaL_checktype(L, 1, LUA_TTABLE);

  lua_newtable(L);
  lua_pushvalue(L, 1);
  lua_setmetatable(L, -2);
  lua_pushvalue(L, 1);
  lua_setfield(L, 1, "__index");

  int state = lua_tointeger(L, 2);

  auto **__obj = static_cast<Foo **>(lua_newuserdata(L, sizeof(Foo *)));
  *__obj = new Foo(state);

  lua_setfield(L, -2, "__self");

  return 1;
}

int foo_get_state(lua_State *L)
{
  assert(lua_gettop(L) == 1);

  luaL_checktype(L, 1, LUA_TTABLE);

  lua_getfield(L, 1, "__self");

  auto *__obj = static_cast<Foo *>(lua_touserdata(L, -1));
  lua_pushinteger(L, __obj->get_state());

  return 1;
}

luaL_Reg const foo_functions[] = {
  {"new", foo_new},
  {"get_state", foo_get_state},
  {nullptr, nullptr}
};

} // namespace

extern "C"
{

LUALIB_API int luaopen_foo(lua_State *L)
{
  luaL_newlib(L, foo_functions);
  lua_setglobal(L, "Foo");

  return 1;
}

} // extern "C"

foo_new创建一个 Lua 表,该表将 C++Foo对象作为用户数据存储在其__self字段中。我将 Lua 中的这个函数称为local foo = Foo:new(1). 但是,这似乎可行,随后调用print(foo:get_state())不会返回1一些垃圾值。为什么会这样,我该如何解决?

标签: c++lua

解决方案


有2个问题:

  • auto **__obj您在 lua 函数中分配内存new,但auto *__obj在 lua 函数中访问foo_get_state
  • 内存泄漏 - 您缺少收集垃圾的功能new Foo()

将元表与数据对象混合是不好的,但在你的情况下,它应该像这样工作:

int foo_new(lua_State *L)
{
  assert(lua_gettop(L) == 2);
  luaL_checktype(L, 1, LUA_TTABLE);

  lua_newtable(L);
  lua_pushvalue(L, 1);
  lua_setmetatable(L, -2);
  lua_pushvalue(L, 1);
  lua_setfield(L, 1, "__index");

  int state = lua_tointeger(L, 2);

  auto *__obj = static_cast<Foo *>(lua_newuserdata(L, sizeof(Foo)));
  new(__obj) Foo(state);
  lua_setfield(L, -2, "__self");

  return 1;
}

int foo_get_state(lua_State *L)
{
  assert(lua_gettop(L) == 1);
  luaL_checktype(L, 1, LUA_TTABLE);

  lua_getfield(L, 1, "__self");
  auto *__obj = static_cast<Foo *>(lua_touserdata(L, -1));
  assert(__obj != nullptr);
  lua_pushinteger(L, __obj->get_state());
  return 1;
}

int foo_gc(lua_State *L)
{
  assert(lua_gettop(L) == 1);
  luaL_checktype(L, 1, LUA_TTABLE);

  lua_getfield(L, 1, "__self");
  auto *__obj = static_cast<Foo *>(lua_touserdata(L, -1));
  if (__obj)
    __obj->~Foo();

  lua_pushnil(L);
  lua_setfield(L, -3, "__self");
  return 0;
}

luaL_Reg const foo_functions[] = {
  {"new", foo_new},
  {"get_state", foo_get_state},
  {"__gc", foo_gc},
  {nullptr, nullptr}
};

__gc如果您使用没有内存/处理分配的简单数据,因为 lua 垃圾收集释放分配的内存,则没有必要。


推荐阅读