2009-04-07 169 views
6

我有一个具有某种元类型的Lua用户数据对象(例如"stackoverflow.test")。从C代码中,我希望能够检查它是哪种类型,并根据结果采取不同的行为。有没有一个很好用的函数(相当于luaL_checkudata,但没有错误,如果答案不是你想要的)让我来查询用户数据的metatable类型名称?如果没有,我想我需要使用lua_getmetatable,但是我有点不清楚我如何确定刚添加到堆栈的metatable的名称。查询Lua用户数据类型C

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

+0

看来,LUA 5.2已经得到了你正在寻找:[luaL_testudata(http://www.lua.org/source/5.2/lauxlib.c.html#luaL_testudata) – 2014-02-13 10:10:05

回答

3

您将使用lua_getmetatablelua_equal用于测试表是相同的。

在我看来,Lua应该给这种类型扩展的东西更多的支持。到目前为止,Lua/C(++)包装系统的责任确实在于此。在我最近做的一个包装器(作为商业项目的一部分)中,我做了class::instance(L,index)来获取特定类型的userdata指针。换句话说,该方法检查它是用户数据,并且metatable也是正确的。如果不是,则返回NULL。

方式的Lua可以帮助这一切都是如果有元表的标准字段用于扩展类型信息(股份公司__type)。这可用于使type()本身将返回“用户数据”,“XXX”(两个值,目前只返回一个)。这将与大多数当前代码保持兼容。但这只是假设的(除非你自定义类型()并且自己实现)。

0

我刚刚查看了luaL_checkudata函数的源代码,它基本上使用lua_getmetatable来获取userdata对象的metatable。然后它使用lua_getfield从注册表中获取给定的类型名称,并调用lua_rawequal来比较它们。

5

您可以随时在metatable中存储标记字段,并为您的模块指定唯一的light userdata值。

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领域。

使用这种方式,_flavor场仅承担,可以通过代码,在范围内的符号green_flavor模块中创建,并查找领域和测试它的值值仅需要一个查找表除了检索的metatable本身。请注意,变量green_flavor的值并不重要,因为实际上只使用其地址。

有几个不同的风味变量可用作sentinal值,_flavor字段可用于区分几个相关的metatables。

所有这一切说,一个自然的问题是“为什么要这样做?”毕竟,metatable可以很容易地包含获得适当行为所需的所有信息。它可以容易地保存函数和数据,这些函数可以从C和Lua中检索和调用。

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");