2009-04-07 170 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");