c++ - 通过 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
一些垃圾值。为什么会这样,我该如何解决?
解决方案
有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 垃圾收集释放分配的内存,则没有必要。
推荐阅读
- python - Django crontab 不在特定时间执行
- r - 奇怪的 R 问题:每当我使用“raster”包运行任何光栅计算时,互联网都会断开连接
- python - 如何使用 Cygwin64 为 Windows 构建 C Python 扩展?
- database - 脚本托管(API 到数据库)
- python - 如何停止树莓派机器人汽车上的模糊图像?
- python - 在不同设备上进行程序间通信的最简洁方法
- javascript - 显示 alert() 点击,相对于 .clone() 之后的 Id
- javascript - 如何从VueJS中的另一个输入更改输入值
- javascript - 限制 javascript 中并发异步 xhr 请求的数量 - 避免 net::ERR_INSUFFICIENT_RESOURCES
- python - python中的多线程?