¿Cómo inserto una instancia de una clase c ++ envuelta con swig en una pila lua?

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

  •  03-07-2019
  •  | 
  •  

Pregunta

Tengo una clase que está envuelta con un trago y registrada con lua. Puedo crear una instancia de esta clase en un script lua, y todo funciona bien.

Pero supongamos que tengo una instancia de una clase hecha en mi código c ++ con una llamada a la nueva X, y tengo la lua_state L con una función que quiero llamar, que acepta un argumento, una instancia de X ... ¿Cómo llamo a esa función? Aquí está (algunos) del código en cuestión (he omitido las cosas de manejo de errores):

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

Por el momento, todo lo que he encontrado que puede funcionar es exponer alguna funcionalidad del archivo cpp generado por swig y llamar a eso. Esto es malo por varias razones ... No funcionará si tengo varios módulos y tuve que cambiar la especificación de enlace predeterminada en el archivo swig (usando -DSWIGRUNTIME =).

Agrego lo siguiente a 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);

Eso lleva un puntero al módulo, luego un puntero al tipo, luego llama a la función swigs para registrarlo. ¡Era irrazonable tener que buscar en un archivo que no se supone que sea legible para los humanos (eso dice en la parte superior del archivo) y solo es MESSY! (¡pero funciona!)

Seguramente hay una mejor manera de lograr lo que estoy tratando de hacer.

PS desde un punto de vista de alto nivel, lo que quiero es que no vuelva a contar los componentes de Gui creados por Object Factory en GuiInst, en caso de que esté haciendo esto mal. Esta es la primera vez que expongo la funcionalidad a un lenguaje de secuencias de comandos, aparte de algunos módulos de python muy simples (y no de swig), así que estoy preparado para tomar consejos.

Gracias por cualquier consejo!


Respuesta al comentario de RBerteig

El contructor de GuiInst está # definido para privado cuando se ejecuta swig para evitar que lua construya instancias de él, por lo que no funcionará para mí. Lo que intentaba evitar era lo siguiente (en lua):

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

que llamaría " g = nuevo GuiButton " luego regístrelo con el GuiRegionVertical (que necesita almacenar un puntero por varias razones), luego llame a "eliminar g", y el GuiRegionVertical se queda con un puntero colgante a g.

Sospecho que lo que realmente necesita suceder es que GuiRegionVertical :: Add (GuiButton *) debe incrementar el recuento de referencias del GuiButton *, y luego el destructor de GuiRegionVertical debe disminuir los recuentos de todos sus contenidos, aunque no estoy seguro Cómo se debe hacer con un trago.

Eso eliminaría la necesidad de los constructores privados, la Fábrica de Objetos Gui y los desagradables externos.

¿Me estoy equivocando?

Gracias.

¿Fue útil?

Solución

Hay una respuesta simple y directa, que puede no ser la respuesta más eficiente. SWIG produce envoltorios para manipular objetos desde el lado del lenguaje de scripting. Para los objetos, también sintetiza un constructor envuelto. Entonces, la solución directa es dejar que el intérprete de Lua llame al constructor de SWIG para crear el nuevo objeto.

Para la clase envuelta engine.GuiInst , es casi seguro que puede hacer algo como:

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);
}

Para un caso de un solo disparo como el inicio de un script, la penalización de ejecutar una constante de cadena a través de luaL_dostring () no es mala en absoluto. Sin embargo, buscaría más difícil evitarlo en una devolución de llamada de evento o un bucle interno.

Parece que debería haber una forma de convertir un puntero directamente en un objeto envuelto, no lo estoy viendo en mi propio puñado de envoltorios generados por SWIG.

Editar: Por supuesto, el fragmento Lua puede descomponerse en llamadas API que obtienen la tabla del motor global en la pila, extraer de ella el miembro new_GuiInst , llamarlo , luego llame al Init global, pero la pequeña eficiencia tiene el costo de cierta claridad.

En cuanto a tratar con objetos que no deberían construirse por accidente en el código de usuario, como lo indica la pregunta aclarada, mi primer impulso sería dejar que SWIG genere la función de constructor, mantener una referencia privada si es necesario más adelante y eliminarla de la mesa. Incluso un módulo C es (normalmente) solo una tabla cuyos miembros contienen valores de función. La implementación en C no los hace de solo lectura a menos que se haga un esfuerzo adicional.

Por lo tanto, siempre puede recuperar el valor de engine.new_GuiInst y estacionarlo en el registro (consulte luaL_ref () y la discusión en sección 3.5 del Manual de referencia de Lua del pseudo-índice LUA_REGISTRYINDEX para los detalles) para su uso posterior. Luego, antes de permitir que se ejecute cualquier código de usuario, simplemente haga el equivalente de engine.new_GuiInst = nil . Debo señalar que para los tipos de datos C con los que he estado jugando más recientemente, SWIG creó dos constructores para cada tipo, llamados new_TYPE y TYPE . Ambos estaban visibles en la tabla del módulo, y querría establecer ambos nombres en nil . Si tiene mucha menos experiencia con SWIG envolviendo clases de C ++, y el resultado puede diferir ...

Es posible que desee comprobar y revisar todo el contenido de la tabla engine que devuelve SWIG, y crear un objeto proxy que contenga solo los métodos que desea que estén disponibles para sus usuarios. También puede cambiar el entorno visto por el script del usuario para que solo tiene el proxy disponible, y también nombra al motor del proxy. Se ha discutido bastante sobre los scripts de usuario de sandboxing en Lua list y en el lua-users wiki .

Otros consejos

Mejor tarde que nunca, y esta solución ayudará a otras personas.

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);
}

este código debe estar dentro de% {}% bloques en tu archivo .i, porque SWIGTYPE_p_WebRequest es

#define SWIGTYPE_p_WebResponse swig_types[6]

y swig_types [6] es

static swig_type_info *swig_types[12];

lo que significa que swig_types solo es accesible desde el archivo C ++ desde el cual está definido.

este fragmento en particular está enviando dos de mis punteros envueltos, por lo que llamar a handle_web_request (solicitud, respuesta) desde el lado C ++ de las cosas ejecutará la función lua global " handle_web_request " y pásale mis dos punteros, con la magia SWIG aplicada.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top