Lua userdata数组访问及方法

2024-06-25

我正在用 C 编写一个用户数据类型,以便在 Lua 中使用。它有一些数组类型的属性和各种方法。现在如果你是这种类型,我使用u:set(k,v) resp. u:get(k)访问数据,例如u:sort()作为方法。为此我设置了__index到包含这些方法的表。现在如果我想使用访问数据u[k] = v or u[k],我需要设置__newindex and __index to set resp get。但是其他方法就无法访问了...

在 C 中处理这个问题的最佳方法是什么?我猜我需要用 C 编写一个函数来注册__index并以某种方式在那里处理它。也许检查 key 是否属于 Lua 方法表,如果是则调用它。

任何帮助/提示将不胜感激。我没有找到这样的例子,尽管(对我来说)这似乎是一件很自然的事情。

edit:在下面的答案中添加了我在 Lua 中发布的解决方案的 C 版本。这或多或少是直接翻译,因此所有功劳都归于@gilles-gregoire。

以下 C 函数被注册为 __index 元方法。

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;
};

where permL_methods is

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

and permL_functions is

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

这看起来像是一个可以通过嵌套元表来解决的问题。您需要一个元表用于方法(例如 sort() 方法),第二个元表用于索引操作。第二个元表实际上是方法元表的元表。

让我把它写成 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当你调用一个方法时。 如果没有找到,就会到__index字段指向的表中查找u的元表...并且 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

Usage:

> print(u)
table: 0x7fb1eb601c30
> u:sort()
sorting...
> = u[1]
this is a get on object    table: 0x7fb1eb601c30
nil
> 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Lua userdata数组访问及方法 的相关文章

随机推荐