Как мне поместить экземпляр класса c ++, завернутый в swig, в стек lua?

У меня есть класс, завернутый в swig и зарегистрированный с помощью lua. Я могу создать экземпляр этого класса в lua-скрипте, и все работает нормально.

Но скажем, у меня есть экземпляр класса, созданный в моем коде на C ++ с вызовом нового X, и у меня есть la lua_state L с функцией в нем, которую я хочу вызвать, которая принимает один аргумент, экземпляр X ... Как мне вызвать эту функцию. Вот (часть) рассматриваемого кода (я пропустил обработку ошибок):

main.cpp

class GuiInst;
extern "C"
{
    int luaopen_engine (lua_State *L);
}

int main()
{
    GuiInst gui=new GuiInst;
    lua_State *L=luaL_newstate();
    luaopen_engine(L); //this is swigs module
    int error=luaL_loadfile(L,"mainmenu.lua")||
    lua_pcall(L, 0, 0, 0);
    lua_getglobal(L,"Init");
    //Somehow push gui onto lua stack...
    lua_pcall(L, 1, 0, 0));
    lua_close(L);
}

mainmenu.lua

function Init(gui)
    vregion=gui:CreateComponent("GuiRegionVertical");
end

На данный момент все, что я обнаружил, что может сработать, - это предоставить некоторые функции из файла cpp, сгенерированного swig, и вызвать это. Это плохо по нескольким причинам ... Это не сработает, если у меня несколько модулей, и мне пришлось бы изменить спецификацию привязки по умолчанию в файле swig (используя -DSWIGRUNTIME =).

Я добавляю в main.cpp следующее

extern "C"
{
    struct swig_module_info;
    struct swig_type_info;
    int luaopen_engine (lua_State *L);
    swig_module_info *SWIG_Lua_GetModule(lua_State* L);
    void SWIG_Lua_NewPointerObj(lua_State* L,void* ptr,swig_type_info *type, int own);
    swig_type_info *SWIG_TypeQueryModule(swig_module_info *start,swig_module_info *end,const char *name);
}
//and then to push the value...
SWIG_Lua_NewPointerObj(L,gui,SWIG_TypeQueryModule(SWIG_Lua_GetModule(L),SWIG_Lua_GetModule(L),"GuiInst *"),0);

Это получает указатель на модуль, затем указатель на тип, а затем вызывает функцию swigs для его регистрации. Было неразумно копаться в файле, который не должен быть удобочитаемым человеком (так написано в верхней части файла) и является просто НЕПРЕРЫВНЫМ! (но это работает!)

Конечно, есть лучший способ выполнить то, что я пытаюсь сделать.

PS с точки зрения высокого уровня, я хочу, чтобы lua не пересчитывал компоненты Gui, которые создаются Object Factory в GuiInst, на случай, если я ошибаюсь. Я впервые раскрываю функциональные возможности языка сценариев, за исключением некоторых очень простых (и не связанных с swig) модулей Python, поэтому я готов прислушаться к совету.

Спасибо за любой совет!


Ответ на комментарий RBerteig

Конструктор GuiInst # определен как закрытый при запуске swig, чтобы lua не создавал его экземпляры, так что для меня это не сработает. Я пытался предотвратить следующее (в lua):

r=engine.GuiRegionVertical()
r:Add(engine.GuiButton())

который вызовет "g = new GuiButton", затем зарегистрирует его в GuiRegionVertical (который по разным причинам должен хранить указатель), затем вызовет "delete g", и GuiRegionVertical останется с висящим указателем на g.

Я подозреваю, что на самом деле должно произойти то, что GuiRegionVertical :: Add (GuiButton *) должен увеличить счетчик ссылок GuiButton *, а затем деструктор GuiRegionVertical должен уменьшить количество ссылок всего его содержимого, хотя я не уверен, как это должно покончить с глотком.

Это устранило бы необходимость в частных конструкторах, фабрике объектов Gui и неприятных внешних модулях.

Я поступаю неправильно?

Спасибо.


person DaedalusFall    schedule 05.03.2009    source источник
comment
возможный дубликат SWIG: Lua - передача экземпляра C ++ как lua параметр функции   -  person Nicol Bolas    schedule 02.03.2012
comment
@NicolBolas: Как этот вопрос может быть дубликатом? Он старше этого на три года :). Спасибо за ссылку.   -  person DaedalusFall    schedule 10.04.2012


Ответы (2)


Есть простой и прямой ответ, который может быть не самым эффективным. SWIG создает оболочки для управления объектами со стороны языка сценариев. Для объектов он также синтезирует обернутый конструктор. Итак, прямое решение - просто позволить интерпретатору Lua вызвать конструктор SWIG для создания нового объекта.

Для обернутого класса engine.GuiInst вы почти наверняка можете сделать что-то вроде:

int main()
{
    lua_State *L=lua_open();
    luaopen_engine(L); //this is swigs module
    int error=luaL_loadfile(L,"mainmenu.lua")||
    lua_pcall(L, 0, 0, 0);

    luaL_dostring(L, "Init(engine.new_GuiInst())");

    lua_close(L);
}

Для одноразового случая, такого как запуск скрипта, штраф за выполнение строковой константы через luaL_dostring () совсем неплох. Однако я бы постарался избежать этого в обратном вызове события или во внутреннем цикле.

Кажется, что должен быть способ преобразовать указатель непосредственно в обернутый объект, я не замечаю его в своей собственной горстке сгенерированных SWIG оберток.

Изменить: Конечно, фрагмент Lua можно разложить на вызовы API, которые получают глобальную таблицу движка в стеке, извлекают из нее член new_GuiInst, вызывают его, затем вызывают глобальный Init, но маленький немного эффективности достигается за счет некоторой ясности.

Что касается работы с объектами, которые не должны создаваться случайно в пользовательском коде, как указывает проясненный вопрос, моим первым импульсом было бы позволить SWIG сгенерировать функцию конструктора, сохранить частную ссылку, если она понадобится позже, и удалить ее из таблицы . Даже модуль C (обычно) представляет собой просто таблицу, члены которой содержат значения функций. Реализация на C не делает их доступными только для чтения, если не предпринимаются дополнительные усилия.

Таким образом, вы всегда можете получить значение engine.new_GuiInst и сохранить его в реестре (см. luaL_ref() и обсуждение в разделе 3.5 Справочного руководства Lua псевдоиндекса LUA_REGISTRYINDEX для подробностей) для дальнейшего использования. Затем, прежде чем разрешить запуск любого пользовательского кода, просто выполните эквивалент engine.new_GuiInst = nil. Я должен отметить, что для типов данных C, с которыми я играл совсем недавно, SWIG создал два конструктора для каждого типа с именами new_TYPE и TYPE. Оба они были видны в таблице модуля, и вы хотите установить оба имени в nil. Если у вас гораздо меньше опыта работы с SWIG-оболочкой классов C ++, и результат может отличаться ...

Возможно, вы захотите проверить и просмотреть все содержимое таблицы engine, возвращаемой SWIG, и создать прокси-объект, содержащий только те методы, которые вы хотите использовать для своих пользователей. Вы также можете изменить среду, видимую пользовательским скриптом, чтобы она доступен только прокси-сервер, и он также называет прокси engine. Было довольно много обсуждений пользовательских скриптов песочницы на список Lua и в вики для пользователей lua.

person RBerteig    schedule 05.03.2009
comment
Спасибо за ответ (и его скорость), я обновил свой вопрос, так как в комментариях недостаточно места. Я бы проголосовал за ваш ответ, но мой рейтинг еще недостаточно высок. - person DaedalusFall; 05.03.2009
comment
Еще раз спасибо, есть несколько хороших советов, особенно WRT прокси-объектов. Некоторое время назад я возился с luaL_ref, но похоже, что он не разделяет объекты между lua_States (как подразумевается lua.org/pil/27.3.html), поэтому я двинулся дальше. - person DaedalusFall; 06.03.2009
comment
Нет, это не для обмена между штатами. Это способ удерживать ссылки на объекты со стороны C, чтобы их можно было снова найти, и чтобы они не собирались как мусор. Это также рекомендуемый способ поместить значение Lua в структуру C, обрабатывая int из luaL_ref () как указатель. - person RBerteig; 06.03.2009
comment
Справедливо. Это была библиотека, в которой используются такие переменные, которые нельзя использовать в нескольких состояниях Lua с этой страницы, которые вводят меня в заблуждение, но я думаю, что теперь понимаю, что это пытается передать. Спасибо. - person DaedalusFall; 07.03.2009

Лучше поздно, чем никогда, и это решение поможет другим людям.

void handle_web_request(WebRequest *request, WebResponse *response)
{
  lua_getfield(rackam->lua_state, LUA_GLOBALSINDEX, "handle_web_request");
  SWIG_Lua_NewPointerObj(rackam->lua_state, request, SWIGTYPE_p_WebRequest, 0);
  SWIG_Lua_NewPointerObj(rackam->lua_state, response, SWIGTYPE_p_WebResponse, 0);
  lua_call(rackam->lua_state, 2, 0);
}

этот код должен находиться внутри блоков% {}% в вашем файле .i, потому что SWIGTYPE_p_WebRequest является

#define SWIGTYPE_p_WebResponse swig_types[6]

а swig_types [6] - это

static swig_type_info *swig_types[12];

это означает, что swig_types доступен только из файла C ++, из которого он определен.

этот конкретный фрагмент отправляет два моих обернутых указателя, поэтому вызов handle_web_request (запрос, ответ) со стороны C ++ запустит глобальную функцию lua «handle_web_request» и передаст ей два моих указателя с примененной магией SWIG.

person Meleneth    schedule 02.03.2012
comment
Можно найти более полную версию этого ответа здесь. - person Nicol Bolas; 02.03.2012