Lua用户数据数组访问和方法 [英] Lua userdata array access and methods
问题描述
我正在用C编写供Lua使用的userdata类型.它具有一些数组类型的属性以及各种方法.现在,如果u是这种类型,我将使用u:set(k,v)
resp. u:get(k)
访问数据,例如u:sort()
作为方法.为此,我将__index
设置为包含这些方法的表.现在,如果要使用u[k] = v
或u[k]
访问数据,则需要将__newindex
和__index
设置为set
或get
.但是随后其他方法不再可用...
I am writing in C a userdata type for use in Lua. It has some array-type properties and various methods aswell. Right now if u is of this type, I use u:set(k,v)
resp. u:get(k)
to access data and e.g. u:sort()
as method. For this I set __index
to a table containing these methods. Now if I want to access the data using u[k] = v
or u[k]
, I need to set __newindex
and __index
to set
resp get
. But then the other methods are no longer accessible...
在C中处理此问题的最佳方法是什么?我猜我需要在C中编写一个函数以注册为__index
并以某种方式在此进行处理.也许检查key是否属于方法的Lua表,如果是,则调用它.
What's the best way to deal with this in C? I am guessing I need to write a function in C to register as __index
and somehow deal with it there. Maybe check if key belongs to a Lua table of methods and if so call it.
任何帮助/提示将不胜感激.我没找到像这样的例子,尽管对我来说这似乎是很自然的事情.
Any help/hints would be appreciated. I did not find examples like this, although it seems a very natural thing to do (to me.)
编辑:在下面的答案中发布了我在Lua中发布的解决方案的C版本.这或多或少是直接翻译,所以所有的功劳都归@ gilles-gregoire.
edit: Added my C version of the solution in Lua posted in the answer below. This is more or less a direct translation, so all credit goes to @gilles-gregoire .
以下C函数已注册为__index元方法.
The following C function is registered as __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;
};
这是执行此操作的代码,
This is the code that does that,
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 }
};
推荐答案
这看起来像可以使用嵌套元表解决的问题.您需要一个用于该方法的元表(例如您的sort()方法),以及另一个用于索引操作的元表.第二个元表实际上是方法元表的元表.
This looks like a problem which can be solved with nested metatables. You need one metatable for the methods (like your sort() method), and a second one for index operations. That second metatable is actually the metatable of the methods metatable.
让我将其编写为lua代码.您需要3张桌子:
Let me write this as lua code. You need 3 tables:
-- 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将对该表重复该过程!
Now, the tricky part is here: lua will first lookup u
when you will call a method.
If it does not find it, it will lookup in the table pointed by the __index field of u
's metatable... And Lua will repeat the process for that table!
-- first level metatable
mt.__index = mt
setmetatable(u, mt)
-- second level metatable
setmetatable(mt, op_mt)
您现在可以像这样使用u
:
You can now use your u
like this:
> u:sort()
sorting...
> = u[1]
get
nil
使用__index元方法的功能更好的解决方案
a better solution by using a function for the __index metamethod
为__index元方法使用函数可能是正确的方法:
Using a function for the __index metamethod is probably the right way to this:
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
>
这篇关于Lua用户数据数组访问和方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!