Доступ и методы доступа к ресурсам пользователя Lua

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

    Это похоже на проблему, которая может быть решена с помощью вложенных метаданных. Вам нужен один метатет для методов (например, метод 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 

    EDIT: лучшее решение, используя функцию для метаданных __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 >