Lua C API如何推送现有指针?

3
假设我有一个从C端拥有的指针,我希望以userdata的形式将其推送到Lua堆栈中。在不牺牲指定特定元表的能力的情况下,最好的方法是什么?
我的原始想法是使用轻量级userdata,但是根据我所读到的这里,所有轻量级userdata共享一个公共的元表。这并不理想,因为它会强制所有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);

或者,对于推送轻用户数据的情况
struct T object;
struct T** p = (struct T**)lua_newuserdata(L, sizeof(struct T*));
*p = &object;

是否推荐使用附加“this指针”,或者有没有更符合API的替代方案?我的最终目标仅仅是将现有指针推送到Lua,并以这样一种方式关联一个元表,即元表可以重复用于重型和轻型用户数据。


1
你不能使用void*进行指针算术运算。那么,你想让你的第一个代码示例做什么? - Nicol Bolas
那是我的失误。我忘记了指针算术在void*上是非标准的。我的意图是将this指针附加到userdata上,然后是结构体本身。接着,将this指针设置为刚好在自身之后的位置,在那里我期望分配结构体。我将编辑我的代码以标准方式反映这一点。 - TheCodeBroski
1个回答

5

一种常见(但不是唯一)的方法是使用一种称为“boxed pointer”的技术。其思想是分配一个足够大以容纳指针的userdata,然后将您的metatable等附加到其中。

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

    // setup metatable, etc
}

然后通过元表调用的函数将在堆栈上接收此userdata,您可以从中解包出原始指针。

// 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 端仍然有对其 userdate 的引用,则会出现悬空指针。如果可能发生这种情况,则需要一种处理方式。我自己解决了这个问题,通过维护一个“活动”的 C 对象注册表,在尝试使用指针之前可以检查该表。

  • userdata 相等性。由于每个指针都推送了一个新的 userdata,因此具有相同指针的两个 userdata 在 Lua 中不会相等。如果这很重要,则有几种方法可以处理。__eq 元方法是最简单的方法,但并非总是适用。我使用上面提到的相同对象注册表来提供指针 -> userdata 映射,然后在推送时,如果我已经有一个指向该指针的 userdata,则只需将其推送而不是创建一个新的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接