2014-11-17 132 views
4

我在C中寫了一個用於Lua的userdata類型。它有一些數組類型的屬性和各種方法。現在,如果你是這種類型,我使用u:set(k,v) resp。 u:get(k)訪問數據和例如作爲方法使用u:sort()。爲此,我將__index設置爲包含這些方法的表格。現在如果我想使用u[k] = vu[k]訪問數據,我需要設置__newindex__indexsetget。但其他方法不再可訪問...Lua userdata數組訪問和方法

在C中處理此問題的最佳方法是什麼?我猜我需要在C中編寫一個函數來註冊爲__index,並以某種方式在那裏處理它。也許檢查鍵是否屬於Lua表的方法,如果是的話,調用它。

任何幫助/提示將不勝感激。我沒有找到像這樣的例子,雖然這似乎是很自然的事情(對我來說)。

編輯:在Lua中添加了我的C版本的解決方案,發佈在下面的答案中。這或多或少是一種直接的翻譯,所以所有功勞都歸於@吉列爾 - 格雷戈裏。

以下C函數被註冊爲__index metamethod。

static int permL_index(lua_State *L) { 
    struct perm **pp = luaL_checkudata(L, 1, PERM_MT); 
    int i; 

    luaL_getmetatable(L, PERM_MT); 
    lua_pushvalue(L, 2); 
    lua_rawget(L, -2); 

    if (lua_isnil(L, -1)) { 
    /* found no method, so get value from userdata. */ 
    i = luaL_checkint(L, 2); 
    luaL_argcheck(L, 1 <= i && i <= (*pp)->n, 2, "index out of range"); 

    lua_pushinteger(L, (*pp)->v[i-1]); 
    }; 

    return 1; 
}; 

這是做的是,

int luaopen_perm(lua_State *L) { 

    luaL_newmetatable(L, PERM_MT); 
    luaL_setfuncs(L, permL_methods, 0); 
    luaL_setfuncs(L, permL_functions, 0); 
    lua_pop(L, 1); 

    luaL_newlib(L, permL_functions); 

    return 1; 
}; 

其中permL_methods

static const struct luaL_Reg permL_methods[] = { 
    { "__index",  permL_index   }, 
    { "__eq",   permL_equal   }, 
    { "__tostring", permL_tostring  }, 
    { "__gc",   permL_destroy   }, 
    [...] 
    { NULL,   NULL     } 
}; 

permL_functions

static const struct luaL_Reg permL_functions[] = { 
    { "inverse",  permL_new_inverse  }, 
    { "product",  permL_new_product  }, 
    { "composition", permL_new_composition }, 
    [...] 
    { NULL,   NULL     } 
}; 
+0

這似乎是一個合理的方法給我。 –

+0

[Lua編程](http://www.lua.org/pil/28.2.html)有一個關於在C中創建數組類型的廣泛教程,包括在C中添加元方法。它由一個Lua的設計師可以免費獲得。 – ryanpattison

+1

感謝@ rpattiso,我知道。但是,它並沒有處理我的問題AFAICT。 – 1k5

回答

5

這看起來像它的CA問題的代碼用嵌套metatables解決。你需要一個metatable的方法(如你的sort()方法),第二個用於索引操作。第二個metatable實際上是metatable的方法metatable。

讓我把它寫成lua代碼。您需要3個表:現在

-- the userdata object. I'm using a table here, 
-- but it will work the same with a C userdata 
u = {} 

-- the "methods" metatable: 
mt = {sort = function() print('sorting...') end} 

-- the "operators" metatable: 
op_mt = {__index = function() print('get') end} 

,棘手的部分是在這裏:LUA會首先尋找u時,你會調用一個方法。 如果找不到它,它會在u的metatable的__index字段指向的表中查找...並且Lua會重複該表的過程!

-- first level metatable 
mt.__index = mt 
setmetatable(u, mt) 

-- second level metatable 
setmetatable(mt, op_mt) 

您現在可以使用您的u這樣的:

> u:sort() 
sorting... 
> = u[1] 
get 
nil 

編輯:使用功能的__index一個更好的解決方案元方法

使用功能的__index metamethod可能是正確的方法:

u = {} 
mt = {sort = function() print('sorting...') end} 
setmetatable(u, mt) 
mt.__index = function(t, key) 
    -- use rawget to avoid recursion 
    local mt_val = rawget(mt, key) 
    if mt_val ~=nil then 
     return mt_val 
    else 
     print('this is a get on object', t) 
    end 
end 

用法:

> print(u) 
table: 0x7fb1eb601c30 
> u:sort() 
sorting... 
> = u[1] 
this is a get on object table: 0x7fb1eb601c30 
nil 
> 
+0

謝謝!這是我期待的答案。但是:我剛剛在C中實現了這個,我現在遇到的問題是'u [k]'現在調用'op_mt .__ index(mt,k)'而不是'op_mt .__ index(u,k)'。因此,我的C函數註冊爲'op_mt .__ index'不知道要對哪個用戶數據執行... – 1k5

+0

你是對的:我沒有嘗試訪問我的答案中的原始表,所以我沒有看到這個問題。我想最初的建議是當時的路要走。 –

+2

我添加了一個如何使用__index metamethod的函數來執行此操作的示例。 –