【问题标题】:Accessing C++ class state via Lua wrapper yields garbage values通过 Lua 包装器访问 C++ 类状态会产生垃圾值
【发布时间】:2021-05-04 06:09:37
【问题描述】:

我正在尝试通过 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,而是返回一些垃圾值。为什么会这样,我该如何解决?

【问题讨论】:

  • 1.您在new 中分配内存auto **__obj,但在foo_get_state 中访问为auto *__obj。 2. 这样的分配在垃圾回收对象后导致内存泄漏 - new Foo() 永远不会被释放(检查stackoverflow.com/questions/519808/… 如何在不分配额外内存的情况下访问构造函数)
  • foo_get_state 中的代码必须是:auto **__obj = static_cast&lt;Foo **&gt;(lua_touserdata(L, -1)); lua_pushinteger(L, (*__obj)-&gt;get_state());,不要忘记检查指针是否有效。
  • @Darius:好的,除了内存泄漏之外,我还混淆了**__obj*__obj。我现在刚刚用auto *__obj = new (lua_newuserdata(L, sizeof(Foo))) Foo(state); 替换了分配步骤。 Lua 代码现在输出一个,但似乎仍然没有调用 ~Foo?除此之外,如果您将您的 cmets 组合成一个答案,我会接受它。

标签: c++ lua


【解决方案1】:

有2个问题:

  • 您在 lua 函数 new 中分配内存 auto **__obj,但在 lua 函数 foo_get_state 中访问为 auto *__obj
  • 内存泄漏 - 您缺少为 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 垃圾回收释放分配的内存,则不需要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多