我有一个具有特定元表类型的Lua userdata对象(例如"stackoverflow.test"
)。从C代码,我希望能够确切地检查它是什么类型,并根据结果表现不同。有没有一个很好用的函数(比如luaL_checkudata
,但如果答案不是你想要的话没有错误)让我查询用户数据的元表类型名称?如果没有,我想我需要使用lua_getmetatable
,但是我有点不清楚如何确定刚刚添加到堆栈中的metatable的名称。
只是为了澄清:我正在使用Lua 5.1,其中luaL_checkudata的行为已被更改。我明白在5.0中它并不习惯错误。
答案 0 :(得分:5)
您始终可以在metatable中存储标记字段,并为模块指定唯一的用户数据值。
static const char *green_flavor = "green";
...
void my_setflavor(lua_State *L, void *flavor) {
lua_pushlightuserdata(L,flavor);
lua_pushlstring(L,"_flavor");
lua_rawset(L,-3);
}
void my_isflavor(lua_State *L, void *flavor) {
void *p = NULL;
lua_pushlstring(L,"_flavor");
lua_rawget(L,-2);
p = lua_touserdata(L,-1);
lua_pop(L,1);
return p == flavor;
}
然后你可以使用my_setflavor(L,&green_flavor)
在堆栈顶部设置表格的_flavor字段,并my_isflavor(L,&red_flavor)
来测试堆栈顶部表格的_flavor字段。“ p>
以这种方式使用,_flavor字段只能接受可以由范围中具有符号green_flavor的模块中的代码创建的值,并且查找字段并测试其值仅需要除了检索之外的一个表查找metatable本身。请注意,变量green_flavor的值无关紧要,因为实际上只使用了它的地址。
有几个不同的味道变量可用作sentinal值,_flavor字段可用于区分几个相关的metatable。
所有这些都说,一个自然的问题是“为什么要这样做?”毕竟,metatable可以轻松地包含获得适当行为所需的所有信息。它可以很容易地保存函数和数据,并且可以从C和Lua中检索和调用这些函数。
答案 1 :(得分:3)
您将使用lua_getmetatable
和lua_equal
来测试表格是否相同。
在我看来,Lua应该给予这种类型扩展的东西更多的支持。截至目前,Lua / C(++)包装器系统确实负责这样做。
在我最近做过的一个包装器中(作为商业项目的一部分)我做class::instance(L,index)
来获取特定类型的userdata指针。换句话说,该方法检查它是userdata并且metatable也是正确的。如果不是,则返回NULL。
Lua可以帮助这一切的方式是,如果metatable有一个扩展类型信息的标准字段(s.a。__type
)。这可以用来使type()
本身返回“userdata”,“xxx”(两个值,当前只返回一个)。这将与大多数当前代码保持兼容。但这只是假设(除非你做一个自定义类型()并自己实现)。
答案 2 :(得分:2)
userdata必须有一个metatable,所以抓住它;然后在注册表中查找所需的名称。如果这两个对象相同,那么您找到了所需的类型。
您可以使用C代码调度此类型,但请允许我轻轻地建议您指定metatable的字段。存储在metatable中的函数应该完成这项工作,但如果没有,如果你必须在C代码中switch
,那么选择一个名称,用它来索引到metatable,并为每个metatable分配一个小整数你可以开启。
meta1.decision = 1
meta2.decision = 2
meta3.decision = 3
然后在你的C代码中
if (lua_getmetatable(L, 1)) {
lua_getfield(L, -1, "decision");
if (lua_isnumber(L, -1)) {
switch ((int) lua_tonumber(L, -1)) {
case 1: ... ; break;
case 2: ... ; break;
case 3: ... ; break;
}
return 0;
}
}
return luaL_error(L, "Userdata was not one of the expected types");
答案 3 :(得分:0)
我刚刚查看了luaL_checkudata
函数的源代码,它基本上使用lua_getmetatable
获取了userdata对象的元表。然后,它使用lua_getfield
从注册表中提取给定的类型名称,并进行lua_rawequal
调用以进行比较。