Как мне вставить экземпляр класса c ++, завернутый с swig в стек lua? - PullRequest
4 голосов
/ 05 марта 2009

У меня есть класс, завернутый в 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

На данный момент все, что я нашел, может работать, это показать некоторые функциональные возможности из сгенерированного swig файла cpp и вызвать его. Это плохо по нескольким причинам ... Это не будет работать, если у меня несколько модулей, и мне пришлось изменить спецификацию связывания по умолчанию в файле 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, чтобы зарегистрировать его. Это было неразумно - копаться в файле, который не должен быть читаемым человеком (так сказано в верхней части файла) и просто MESSY! (но это работает!)

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

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

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


Ответ на комментарий пользователя RBerteig

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

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

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

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

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

Я собираюсь об этом неправильно?

Спасибо.

Ответы [ 2 ]

3 голосов
/ 02 марта 2012

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

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 ++, из которого он определен.

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

1 голос
/ 05 марта 2009

Существует простой и прямой ответ, который может быть не самым эффективным ответом. 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. Было довольно много обсуждений пользовательских сценариев sandboxing в списке Lua и на lua-users wiki .

...