假设我有一个从C端拥有的指针,我希望以userdata的形式推送到Lua堆栈。实现这一目标的最佳方法是什么,而不是牺牲指定特定元表的能力?
我最初的想法是使用一个轻量级用户数据,但从我读过的here开始,所有轻量级用户数据共享一个共同的元数据。这是不可取的,因为它会强制我所有c面对象的共同行为。
考虑到我希望为Lua分配的对象重用metatables,只需将不同的this指针传递给metamethods,我的第二个想法是将一个人工指针附加到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;
建议使用此指针,还是有更友好的API替代方案?我的最终目标是简单地将现有指针推送到Lua,并将metatable与它相关联,使得metatable可以重用于重度和轻量级用户数据。
答案 0 :(得分:2)
一种常见的(但不是唯一的)方法是使用一种称为“盒装指针”的技术。这个想法是你分配一个足够大的用户数据来保存指针,然后将你的元数据等附加到它上面。
void push_pointer(lua_State *L, void *p) {
void **bp = lua_newuserdata(L, sizeof(p));
*bp = p;
// setup metatable, etc
}
然后通过metatable调用的函数将在堆栈上接收此用户数据,并从中解压缩原始指针。
// 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
}
在执行此操作时,您需要考虑以下几点:
对象生命周期。如果在Lua端的某些东西对其userdate有引用时释放了C端对象,那么你将有一个悬空指针。如果这是可能发生的事情,那么你需要一种方法来处理它。我自己处理了这个问题,通过使用“live”C对象的注册表,我可以在尝试使用指针之前检查它。
用户数据相等。由于为每个指针推送一个新的userdata,因此具有相同指针的两个userdata在Lua中不会相等。如果这对你很重要,你有几种方法可以解决它。 __eq
metame方法是最简单的方法,但并不总是合适的。我已经使用上面提到的相同的对象注册表来提供指针 - >用户数据映射,然后,当推送时,如果我已经有指针的用户数据,我只需按下它而不是创建新的。