将现有的指针推送到 Lua C API。

假设我有一个来自 C 端的指针,我希望将其以 userdata 的形式推送到 Lua 栈上。在不放弃指定特定元表的能力的情况下,最好的方法是什么?

我的原始想法是使用一个轻量级 userdata,但根据我在这里阅读到的内容,所有轻量级 userdatas 共享一个公共元表。这不是理想的,因为它会强制所有 c 端对象都有相同的行为。

考虑到我希望为 Lua 分配的对象重用元表,只需将不同的 this 指针传递给元方法,我的第二个想法涉及将人工 this 指针附加到 userdata 对象上。

struct ud
{
    struct T* ptr;
    struct T data;
};

struct ud* p = (struct ud*)lua_newuserdata(L, sizeof(struct ud));
p->ptr = &(p->data);

或者,在推送轻量级 userdata 的情况下

struct T object;
struct T** p = (struct T**)lua_newuserdata(L, sizeof(struct T*));
*p = &object;

附加 this 指针是否推荐,还是有更友好的 API 的替代方法?我的最终目标只是将现有指针推送到 Lua 并以这样的方式将元表与之关联,以便元表可以重用重型和轻量级 userdatas。

点赞
用户12421
用户12421

一种常见(但不是唯一)的方法是使用一种称为“封装指针”的技术。其思想是,您分配一个足够大的用户数据来容纳指针,然后将元表等附加到它上面。

void push_pointer(lua_State *L, void *p) {
    void **bp = lua_newuserdata(L, sizeof(p));
    *bp = p;

    // setup metatable, etc
}

然后通过元表调用的函数将在堆栈上接收到此用户数据,然后您可以从其中拆包原始指针。

// 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
}

做这个事情时需要考虑几件事情:

  • 对象生命周期。如果 C 端对象在 Lua 端有一个用户数据的引用时被释放,那么您将拥有一个悬空指针。如果这是可能发生的情况,那么您需要一种处理它的方法。我通过自己拥有一个“活动” C 对象的注册表来处理这个问题,可以在尝试使用指针之前进行检查。

  • 用户数据等价性。由于您为每个指针推送一个新的用户数据,因此具有相同指针的两个用户数据在 Lua 中不会相等。如果这对您很重要,那么您可以通过几种方式来解决它。一个 __eq 元方法是最容易的方法,但并不总是适合。我使用上面提到的同一对象注册表来提供指针->用户数据映射,然后在推送时,如果我已经有指针的用户数据,我只是推送它而不是创建一个新的。

2013-02-24 01:18:14