假设我有一个 C 端拥有的指针,我希望以用户数据的形式将其推送到 Lua 堆栈上。在不牺牲指定特定元表的能力的情况下,实现此目的的最佳方法是什么?
我最初的想法是使用轻量用户数据,但从我在这里读到的内容来看,所有轻量用户数据共享一个公共元表。这是不可取的,因为它会强制我的所有 c 面对象具有共同的行为。
考虑到我希望为 Lua 分配的对象重用元表,只需将不同的 this 指针传递给元方法,我的第二个想法涉及将人工 this 指针附加到用户数据对象。
struct ud
{
struct T* ptr;
struct T data;
};
struct ud* p = (struct ud*)lua_newuserdata(L, sizeof(struct ud));
p->ptr = &(p->data);
Run Code Online (Sandbox Code Playgroud)
或者,在推送少量用户数据的情况下
struct T object;
struct T** p = (struct T**)lua_newuserdata(L, sizeof(struct T*));
*p = &object;
Run Code Online (Sandbox Code Playgroud)
是否建议附加 this 指针,或者是否有更 API 友好的替代方案?我的最终目标只是将现有的指针推送到 Lua,并将元表与其关联起来,以便元表可以重用于重型和轻型用户数据。
执行此操作的常见(但不是唯一)方法是使用称为“装箱指针”的技术。这个想法是,您分配一个足够大的用户数据来容纳指针,然后将您的元表等附加到它。
void push_pointer(lua_State *L, void *p) {
void **bp = lua_newuserdata(L, sizeof(p));
*bp = p;
// setup metatable, etc
}
Run Code Online (Sandbox Code Playgroud)
然后,通过元表调用的函数将在堆栈上接收此用户数据,并从中解压原始指针。
// called via metatable
int some_function(lua_State *L) {
assert(lua_type(L, 1) == LUA_TUSERDATA);
void **bp = lua_touserdata(L, 1);
void *p = *bp;
// do stuff with p
}
Run Code Online (Sandbox Code Playgroud)
执行此操作时,您必须考虑以下几件事:
对象的生命周期。如果 C 端对象被释放,而 Lua 端有对其用户日期的引用,那么您将得到一个悬空指针。如果这种情况可能发生,那么您需要一种方法来处理它。我自己通过拥有一个“活动”C 对象注册表来处理这个问题,我可以在尝试使用指针之前对其进行检查。
用户数据平等。由于您为每个指针推送一个新的用户数据,因此具有相同指针的两个用户数据在 Lua 中不会比较相等。如果这对您很重要,您可以通过几种方法来处理它。元__eq方法是最简单的,但并不总是合适。我使用了上面提到的相同的对象注册表来提供指针->用户数据映射,然后,在推送时,如果我已经有指针的用户数据,我只需推送它而不是创建一个新的。