Lua的用户数据数组访问和方法。

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

在C中处理这个问题的最佳方法是什么?我猜我需要编写一个函数在C中注册为__index,并在那里进行处理。如果键属于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                  }
};
点赞
用户1183484
用户1183484

这似乎是一个可以使用嵌套元表解决的问题。您需要一个用于方法(例如sort()方法)的元表,以及第二个用于索引操作的元表。第二个元表实际上是方法元表的元表。

让我写一下Lua代码。你需要3个表:

-- userdata对象。我在这里使用了一个表,
-- 但使用C userdata将会有着相同的效果
u = {}

-- "方法"元表:
mt = {sort = function() print('sorting...') end}

-- "运算符"元表:
op_mt = {__index = function() print('get') end}

现在,关键部分在这里:当您调用方法时,Lua将首先查找“u”。 如果它在此表中找不到,则会在“u”的元表的__index字段指向的表中查找...然后Lua会为该表重复此过程!

-- 第一层元表
mt.__index = mt
setmetatable(u, mt)

-- 第二层元表
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)
    -- 使用rawget避免递归
    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
>
2014-11-17 11:59:15