Comment pousser une instance d'une classe c ++ encapsulée avec swig sur une pile lua?

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

  •  03-07-2019
  •  | 
  •  

Question

J'ai une classe qui est enveloppée de swig et enregistrée auprès de lua. Je peux créer une instance de cette classe dans un script Lua et tout fonctionne correctement.

Mais disons que j'ai une instance d'une classe faite dans mon code c ++ avec un appel à new X, et que j'ai la lua_state L avec une fonction que je veux appeler, qui accepte un argument, une instance de X ... Comment j'appelle cette fonction. Voici un extrait du code en question (j'ai omis le traitement des erreurs):

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

Pour le moment, tout ce que j'ai trouvé qui puisse fonctionner est d'exposer certaines fonctionnalités du fichier cpp généré par swig et de l'appeler. C’est mauvais pour plusieurs raisons ... Cela ne fonctionnera pas si j’ai plusieurs modules et que je dois changer la spécification de liaison par défaut dans le fichier swig (avec -DSWIGRUNTIME =).

J'ajoute ce qui suit au fichier 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);

Cela obtient un pointeur sur le module, puis un pointeur sur le type, puis appelle la fonction swigs pour l’enregistrer. C’était une chose déraisonnable d’avoir à fouiller dans un fichier qui n’est pas censé être lisible par l’homme (comme il est dit en haut du fichier) et qui n’est que MESSY! (mais ça marche!)

Il y a sûrement une meilleure façon d'accomplir ce que j'essaie de faire.

PS d’un haut niveau pov, ce que je veux, c’est que j’ai pas besoin de rappeler les composants Gui créés par Object Factory à GuiInst, au cas où j’allais mal. C’est la première fois que j’expose des fonctionnalités à un langage de script, mis à part certains modules python très simples (et non swig). Je suis donc prêt à prendre des conseils.

Merci pour tout conseil!

Réponse au commentaire de RBerteig

Le constructeur de GuiInst est # défini en privé lorsque swig est exécuté pour empêcher Lua d'en construire des instances, de sorte que cela ne fonctionnera pas pour moi. Ce que j’essayais d’empêcher était le suivant (en lua):

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

qui appellerait " g = new GuiButton " puis enregistrez-le auprès de GuiRegionVertical (qui doit stocker un pointeur pour diverses raisons), puis appelez "delete g", et GuiRegionVertical reste avec un pointeur suspendu à g.

Je suppose que ce qui doit vraiment arriver, c’est que GuiRegionVertical :: Add (GuiButton *) devrait incrémenter le nombre de références du GuiButton *, et que le destructeur de GuiRegionVertical devrait alors décrémenter tous les contenus de son contenu, bien que je ne sois pas sûr. comment cela devrait être fait avec swig.

Cela supprimerait la nécessité pour les constructeurs privés, la fabrique d'objets Gui et les méchants externes.

Est-ce que je vais à propos de ce mauvais?

Merci.

Était-ce utile?

La solution

Il existe une réponse simple et directe, qui peut ne pas être la réponse la plus efficace. SWIG produit des wrappers pour manipuler des objets du côté du langage de script. Pour les objets, il synthétise également un constructeur encapsulé. La solution directe consiste donc à laisser l’interprète Lua appeler le constructeur de SWIG pour créer le nouvel objet.

Pour la classe engine.GuiInst encapsulée, vous pouvez presque certainement faire quelque chose comme:

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

Pour un cas ponctuel comme le démarrage d'un script, l'exécution d'une constante de chaîne via luaL_dostring () n'est pas une mauvaise chose du tout. Je chercherais plus difficilement à l'éviter dans un rappel d'événement ou dans une boucle interne.

Il semble bien qu'il devrait exister un moyen de convertir un pointeur directement en objet enveloppé. Je ne le repère pas dans ma propre poignée d'emballages générés par SWIG.

Modifier: Bien entendu, le fragment Lua peut être décomposé en appels d'API qui obtiennent la table de moteur globale sur la pile, extrayez-en le membre new_GuiInst , appelez-le. , appelez ensuite le Init global , mais le peu d’efficacité se fait au détriment de la clarté.

En ce qui concerne les objets qui ne devraient pas être construits par accident dans le code utilisateur, comme le dit la question clarifiée, mon premier réflexe serait de laisser SWIG générer la fonction constructeur, conserver une référence privée si nécessaire ultérieurement, et la supprimer. de la table. Même un module C n'est (généralement) qu'une table dont les membres contiennent des valeurs de fonction. Leur mise en œuvre en C ne les rend pas en lecture seule, à moins que des efforts supplémentaires soient nécessaires.

Ainsi, vous pouvez toujours récupérer la valeur de engine.new_GuiInst et la stocker dans le registre (voir luaL_ref () et la discussion dans section 3.5 du Manuel de référence de Lua du pseudo-index LUA_REGISTRYINDEX pour plus de détails) pour une utilisation ultérieure. Ensuite, avant de laisser un code utilisateur s'exécuter, effectuez simplement l'équivalent de engine.new_GuiInst = nil . Il est à noter que pour les types de données C avec lesquels je jouais le plus récemment, SWIG a créé deux constructeurs pour chaque type, nommés new_TYPE et TYPE . Les deux étaient visibles dans la table du module et vous voudriez définir les deux noms sur nil . Si vous avez beaucoup moins d’expérience avec l’encapsulation SWIG dans les classes C ++ et que le résultat peut être différent ...

Vous pouvez vérifier et revoir l'intégralité du contenu de la table moteur renvoyée par SWIG et créer un objet proxy contenant uniquement les méthodes que vous souhaitez mettre à la disposition de vos utilisateurs. Vous pouvez également modifier l’environnement vu par le script utilisateur afin qu'il uniquement le proxy est disponible et nomme également le proxy moteur . Il y a eu beaucoup de discussions sur les la mise en sandbox des scripts utilisateur sur le Liste Lua et à la wiki lua-utilisateurs .

Autres conseils

Mieux vaut tard que jamais, et cette solution aidera d’autres personnes.

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

ce code doit être à l'intérieur de% {}% de blocs dans votre fichier .i, car SWIGTYPE_p_WebRequest est

.
#define SWIGTYPE_p_WebResponse swig_types[6]

et swig_types [6] est

static swig_type_info *swig_types[12];

ce qui signifie que swig_types est uniquement accessible à partir du fichier C ++ à partir duquel il est défini.

cet extrait de code envoie deux de mes pointeurs encapsulés. Par conséquent, l'appel de handle_web_request (requête, réponse) à partir du côté C ++ exécutera la fonction globale lua " handle_web_request " et passez mes deux pointeurs, avec la magie SWIG appliquée.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top