Lua用户数据数组访问和方法 [英] Lua userdata array access and methods

查看:307
本文介绍了Lua用户数据数组访问和方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆