我在C中编写了一个用于Lua的userdata类型。它有一些数组类型属性和各种方法。现在,如果你是这种类型,我使用u:set(k,v)
resp。 u:get(k)
访问数据,例如u:sort()
作为方法。为此,我将__index
设置为包含这些方法的表。现在,如果我想使用u[k] = v
或u[k]
访问数据,我需要将__newindex
和__index
设置为set
resp get
。但是其他方法再也无法访问......
在C中处理此问题的最佳方法是什么?我猜我需要在C中编写一个函数来注册为__index
并以某种方式处理它。也许检查密钥是否属于Lua方法表,如果是,则调用它。
任何帮助/提示将不胜感激。我没有找到这样的例子,虽然这对我来说似乎是很自然的事情。(
)编辑:在下面的答案中添加了我在Lua中的C版解决方案。这或多或少是一种直接的翻译,所以所有的功劳都归功于@ gilles-gregoire。
以下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 :(得分:5)
这看起来像是一个可以用嵌套元表解决的问题。你需要一个metatable用于方法(比如你的sort()方法),第二个用于索引操作。第二个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元方法的函数可能是正确的方法:
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
>