2014-11-17 3 views
4

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

Каков наилучший способ справиться с этим в C? Я предполагаю, что мне нужно написать функцию в C, чтобы зарегистрироваться как __index и как-то с ней справиться. Возможно, проверьте, принадлежит ли ключ к таблице методов Lua и, если это так.

Любая помощь/подсказки будут оценены. Я не нашел таких примеров, хотя это кажется очень естественным делом (для меня.)

Редактировать: Добавил мою версию 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     } 
}; 
+0

Это кажется разумным подходом ко мне. –

+0

[Программирование в Lua] (http://www.lua.org/pil/28.2.html) содержит обширное учебное пособие по созданию типа массива в C, которое включает добавление мета-методов в C. Оно написано одним из дизайнеры Lua и свободно доступны. – ryanpattison

+1

Спасибо @rpattiso, я знаю. Однако это не касается моей проблемы AFAICT. – 1k5

ответ

5

Это выглядит как проблема, которая ча n быть разрешенными вложенными метатегами. Вам нужен один метатет для методов (например, метод 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 будет первый LookUp 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 

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 
> 
+0

Спасибо! Это тот ответ, на который я надеялся. Но: я только что реализовал это в C и проблема, с которой я столкнулся сейчас, заключается в том, что 'u [k]' теперь вызывает 'op_mt .__ index (mt, k)', а не 'op_mt .__ index (u, k)'. Таким образом, моя функция C, зарегистрированная как 'op_mt .__ index', не знает, какие пользовательские данные действуют ... – 1k5

+0

Вы правы: я не пытался получить доступ к исходной таблице в своем ответе, поэтому я не видел эту проблему. Я предполагаю, что первоначальное предложение - это путь. –

+2

Я добавил пример того, как это сделать, используя функцию метаданных __index. –

Смежные вопросы