- 什么是Lua中的userdata和lightuserdata?
- 我在哪里需要使用它们?
我一直在尝试理解这个问题,但似乎找不到任何我真正理解的教程/解释。
为什么需要它们,为什么不能直接将C函数绑定到Lua元表中?
我一直在尝试理解这个问题,但似乎找不到任何我真正理解的教程/解释。
为什么需要它们,为什么不能直接将C函数绑定到Lua元表中?
userdata是一个大小和内容任意的垃圾回收值。您可以使用C API创建一个userdata,用lua_newuserdata()
函数创建并将其推送到堆栈上,并提供一个指针以便您从C中初始化它。
它非常类似于调用malloc()
。与malloc()
的一个关键区别是,您永远不需要调用free()
,而是只需允许最后一个引用消失,垃圾回收器将最终回收其存储空间。
它们最有用的是用于保存从C有用但必须从Lua管理的数据。它们支持单独的元表,这是绑定C或C++对象到Lua的关键特性。您只需填充其元表,使用在C中编写的方法来访问、修改和/或使用userdata的内容,结果就是一个可以从Lua访问的对象。一个很好的例子是io
库,它在userdata中存储C的FILE *
指针,并提供实现熟悉的read
、write
等方法的绑定。通过实现__gc
元方法,io
库确保其中一个file
对象在收集时关闭关联的FILE *
。
轻量用户数据是在Lua中将指针表示为值的方式。您可以通过使用其值的指针调用lua_pushlightuserdata()
来创建一个轻量用户数据。它们由Lua来管理,就像数字一样。当您需要以某种方式命名C对象以便在Lua内部传递名称,但该对象的生命周期未由Lua管理时,它们非常有用。与数字相等当它们具有相同的值时,轻量用户数据在持有相同指针时相等。与数字类似,只要它们位于堆栈上或存储在变量中,它们就存在,并且它们没有单独的元表,也不会被垃圾收集。
首先,userdata指的是完整的userdata。以下是两种实现CharArray的解决方案,请参考以下内容:
//full userdata
extern "C" int newarray(lua_State* L)
{
int n = luaL_checkint(L, 1);
size_t nbytes = sizeof(CharArray) + (n - 1)*sizeof(char);
CharArray* a = (CharArray*)lua_newuserdata(L, nbytes);
a->size = n;
return 1;
}
//light userdata
extern "C" int newlarray(lua_State* L)
{
int n = luaL_checkint(L, 1);
size_t nbytes = sizeof(CharArray) + (n - 1)*sizeof(char);
CharArray* a = (CharArray*)(new char(nbytes));
lua_pushlightuserdata(L,a);
a->size = n;
return 1;
}
完整的 userdata 是一个没有预定义操作的原始内存区域,由 Lua 提供。因此,userdata 必须由垃圾回收器进行管理。 另一方面,轻量级 userdata 只是表示 C 指针(即 void * 值)的值。轻量级 userdata 不需要由垃圾收集器进行管理(也不会被管理)。
每当您有一些数据需要由Lua GC管理时,都可以使用userdata。例如,您可以将其用于C ++对象。以下是一些C ++对象与userdata的示例:您可以在userdata中保存一个C ++对象,然后在C ++中忘记它,因为它将由Lua管理。因此,您可以将其引用在Lua变量中,并将其传递给调用C ++对象成员函数的函数。 (当然有方法可以将其泛化,例如将通用函数对象放入userdata中,将其绑定为C闭包的upvalue,并将该C闭包注册到表示C ++对象的Lua对象上,其中也涉及userdata)。 如果您不希望Lua GC管理您的对象,只想从Lua引用您的C ++对象,则可以将指向它的指针存储为轻量级userdata。