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

StackOverflow https://stackoverflow.com/questions/613282

  •  03-07-2019
  •  | 
  •  

Вопрос

У меня есть класс, завернутый в 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 для его регистрации.Было неразумно копаться в файле, который не должен быть удобочитаемым для человека (так написано в верхней части файла) и который просто БЕСПОРЯДЕН!(но это работает!)

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

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

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


Ответ на комментарий РБертейга

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

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

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

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

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

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

Спасибо.

Это было полезно?

Решение

Существует простой и прямой ответ, который, возможно, не самый эффективный.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-пользователи вики.

Другие советы

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

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(request, response) со стороны C++ запустит глобальную функцию lua "handle_web_request" и передаст ей два моих указателя с примененной магией SWIG.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top