从C查询Lua userdata类型

时间:2009-04-07 18:13:30

标签: types lua

我有一个具有特定元表类型的Lua userdata对象(例如"stackoverflow.test")。从C代码,我希望能够确切地检查它是什么类型,并根据结果表现不同。有没有一个很好用的函数(比如luaL_checkudata,但如果答案不是你想要的话没有错误)让我查询用户数据的元表类型名称?如果没有,我想我需要使用lua_getmetatable,但是我有点不清楚如何确定刚刚添加到堆栈中的metatable的名称。

只是为了澄清:我正在使用Lua 5.1,其中luaL_checkudata的行为已被更改。我明白在5.0中它并不习惯错误。

4 个答案:

答案 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_getmetatablelua_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调用以进行比较。