В lua есть ли способ привязать значение к значению userdata вместо функции?

В следующем примере userdata значение создается типа MyType и таблица создается с мета-функцией __tostring какие звонки LI_MyType__tostring, Код создает закрытие lua ООП. Мой пример с приведенным примером выглядит так, как будто есть только один способ связать userdata с помощью вызова метода, с помощью повышения значений. Само по себе это не проблема, если я не хочу делиться одним и тем же метатабором между экземплярами.

В идеальном мире — и что я надеюсь выяснить с этим вопросом — есть ли способ связать повышение стоимости со значением (например, userdata) не связывая это с вызовом функции через значение? Я надеюсь, что есть хитрость, которая позволит мне продолжать использовать lua OOP на основе замыканий а также разделить одну и ту же метатаблицу между экземплярами. Я не оптимистичен, но я решил спросить, есть ли у кого-то предложение или неочевидный трюк.

using FuncArray = std::vector<const ::luaL_Reg>;
static const FuncArray funcs = {
{ "__tostring", LI_MyType__tostring },
};

int LC_MyType_newInstance(lua_State* L) {
auto userdata = static_cast<MyType*>(lua_newuserdata(L, sizeof(MyType)));
new(userdata) MyType();

// Create the metatable
lua_createtable(L, 0, funcs.size());     // |userdata|table|
lua_pushvalue(L, -2);                    // |userdata|table|userdata|
luaL_setfuncs(L, funcs.data(), 1);       // |userdata|table|
lua_setmetatable(L, -2);                 // |userdata|
return 1;
}

int LI_MyType__tostring(lua_State* L) {
// NOTE: Blindly assume that upvalue 1 is my userdata
const auto n = lua_upvalueindex(1);
lua_pushvalue(L, n);                     // |userdata|
auto myTypeInst = static_cast<MyType*>(lua_touserdata(L, -1));
lua_pushstring(L, myTypeInst->str());    // |userdata|string|
return 1;                                // |userdata|string|
}

Я надеюсь, что есть способ выполнить что-то вроде (это псевдокод!):

// Assume that arg 1 is userdata
int LI_MyType__tostring(lua_State* L) {
const int stackPosition = -1;
const int upvalueIndex = 1;
const auto n = lua_get_USERDATA_upvalue(L, stackPosition, upvalueIndex);
lua_pushvalue(L, n);                     // |userdata|
auto myTypeInst = static_cast<MyType*>(lua_touserdata(L, -1));
lua_pushstring(L, myTypeInst->str());    // |userdata|string|
return 1;                                // |userdata|string|
}

Я знаю, что это похоже на то, как обстоят дела с «нормальным» метатабельным стилем ООП, но я хочу, чтобы все было основано на закрытии и не вводил синтаксис двоеточия.

Другой способ задать этот вопрос, есть ли способ поделиться метатаблицы между userdata экземпляры при использовании ООП на основе замыкания? Используя синтаксис lua со стороны сценариев, я не думаю, что это возможно, но я надеюсь, что есть кое-что, что можно сделать на стороне C.


ОБНОВИТЬ (2013-10-10): на основе ответ @ lhf для использования lua_setuservalue() а также lua_getuservalue() протокол, на котором я остановился, который позволяет мне повторно использовать метатаблицы, таков:

  1. Зарегистрировать один метатабельный объект, используя luaL_newmetatable(), Этим метатабелем теперь можно поделиться через userdata экземпляры, потому что при регистрации метатаблицы не используются никакие значения.
  2. Создать userdata значение (lua_newuserdata()).
  3. Назначьте правильный метатаблицу для userdata значение (lua_setmetatable()).
  4. Создайте и заполните таблицу вызовов / атрибутов метода экземпляра одним повышением, userdata,
  5. использование lua_setuservalue() на userdata хранить ссылку на таблицу атрибутов / методов для каждого экземпляра.
  6. Изменить различные метаметоды (например, __index) использовать userdataТаблица пользовательских значений.

Как следствие:

  • в метаметоде никогда не используются значения
  • upvalues ​​используются только в методах экземпляра значения
  • есть только одна дополнительная таблица на экземпляр данного класса

По-прежнему невозможно избежать создания таблицы методов / атрибутов для пользовательских данных, но эти издержки являются номинальными. Было бы хорошо, если obj.myMethod() пройдет obj в function myMethod() как-то без использования :, но это именно то, что : делает, потому что это невозможно другим способом (если вы не используете повышение стоимости).

5

Решение

lua_setuservalue кажется именно то, что вам нужно. Там тоже конечно lua_getuservalue.

(Я пропускаю код C ++ и отвечаю на вопрос в заголовке.)

1

Другие решения

Я не думаю, что вы должны пытаться сделать это точно по нескольким причинам.

  1. Если вы вызываете object.method () и пытаетесь вывести объект из созданного экземпляра, вы блокируете свою способность передавать указатели на функции, которые ведут себя на любом данном объекте.
  2. У вас есть циклические ссылки на объекты, которые никогда не будут собирать мусор (функция каждого экземпляра указывает обратно на экземпляр).

Просто возьмите объект из слота 1 и проверьте, соответствует ли его тип вашим пользовательским данным. (LuaL_checkudata)

Если это не объект, а tostring, например, вызывается, просто выведите его как класс имени объекта, а не детали экземпляра. Это имеет гораздо больше смысла и может значительно упростить отладку, если объект сообщает о том, чем он является на самом деле, вместо того, чтобы пытаться быть слишком умным и вводить вас в заблуждение.

0

По вопросам рекламы [email protected]