Доступ к массиву пользовательских данных Lua и методы
Я пишу в C тип userdata для использования в Lua. Он также имеет некоторые свойства типа массива и различные методы. Прямо сейчас, если вы такого типа, я использую u:set(k,v)
соответственно u:get(k)
для доступа к данным и, например, u:sort()
как метод. Для этого я установил __index
к таблице, содержащей эти методы. Теперь, если я хочу получить доступ к данным с помощью u[k] = v
или же u[k]
Мне нужно установить __newindex
а также __index
в set
соответственно get
, Но тогда другие методы больше не доступны...
Какой лучший способ справиться с этим в C? Я предполагаю, что мне нужно написать функцию в C, чтобы зарегистрироваться как __index
и как-то с этим справиться. Может быть, проверить, принадлежит ли ключ таблице методов Lua, и если это так, вызвать его.
Любая помощь / советы будут оценены. Я не нашел таких примеров, хотя это кажется очень естественным (для меня).
edit: добавлена моя C версия решения в Lua, опубликованная в ответе ниже. Это более или менее прямой перевод, поэтому вся заслуга принадлежит @ 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 }
};
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}
Теперь сложная часть: Луа будет первым поиском u
когда вы будете вызывать метод. Если он не найдет его, он будет искать в таблице, указанной полем __index u
Это метатабельно... И Луа повторит процесс для этой таблицы!
-- 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
>