【问题标题】:Lua userdata array access and methodsLua 用户数据数组访问和方法
【发布时间】:2015-01-14 05:23:39
【问题描述】:

我正在用 C 语言编写一个用于 Lua 的用户数据类型。它还有一些数组类型的属性和各种方法。现在如果你是这种类型,我使用u:set(k,v) resp。 u:get(k) 访问数据,例如u:sort() 作为方法。为此,我将__index 设置为包含这些方法的表。现在,如果我想使用u[k] = vu[k] 访问数据,我需要将__newindex__index 设置为setget。但随后其他方法不再可用......

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

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

编辑:在 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;
};

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

【问题讨论】:

  • 这对我来说似乎是一个合理的方法。
  • Programming in Lua 有一个详尽的教程,用于在 C 中创建数组类型,其中包括在 C 中添加元方法。它是由 Lua 的一位设计者编写的,可免费获得。
  • 谢谢@rpattiso,我知道。但是,它不能解决我的问题 AFAICT。
  • 你确定吗? this page__index__newindex 方法添加到用户定义的 C 数组类型。他们将这些添加为a:get(10)a:set(10, 3.4) 的第二个选项,因此它将与a[10]a[10]=3.4 相同。我从你的问题中不明白什么?
  • @rpattiso:它们用__index__newindex 表示法替换了get()set() 方法。在他们完成此操作后,a:get(10) 会产生 mt.__index(a,"get")(10)。我想要两种方法(如a:sort())和索引符号。

标签: c lua lua-api lua-userdata


【解决方案1】:

这看起来像是一个可以通过嵌套元表解决的问题。您需要一个用于方法的元表(例如您的 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。 如果没有找到,它将在u 的元表的__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
> 

【讨论】:

  • 谢谢!这是我所希望的答案。但是:我刚刚在 C 中实现了这个,我现在遇到的问题是 u[k] 现在调用 op_mt.__index(mt, k) 而不是 op_mt.__index(u, k)。因此,我注册为op_mt.__index 的 C 函数不知道要对哪个用户数据进行操作...
  • 你是对的:我没有尝试在我的答案中访问原始表,所以我没有看到这个问题。我想最初的建议是要走的路。
  • 我添加了一个示例,说明如何使用 __index 元方法的函数来执行此操作。
  • @Leo:我找到了我的旧代码。我的解决方案实际上只是 Gilles Lua 代码 C 的直接解决方案。
  • @Leo:我添加了代码。 permL_methods 是一个 luaL_Reg 包含 __index 等等。然后其他功能在permL_functions 中,例如composition 并注册了两次。这样我就可以同时做composition(p, i, q)p:composition(i,q) IIRC(与问题无关)。重要的是包括__index 在内的所有函数都进入元表。然后__index 首先检查是否存在具有该名称的函数,如果没有则假定用户数据访问(在我的情况下是一些类似数组的对象)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-12-14
  • 1970-01-01
  • 1970-01-01
  • 2016-12-21
  • 2018-01-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多