2009-03-05 6 views
4

J'ai une classe qui est enveloppée avec swig, et enregistré avec lua. Je peux créer une instance de cette classe dans un script lua, et tout fonctionne bien. Mais disons que j'ai une instance d'une classe faite dans mon code C++ avec un appel à new X, et 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 peu) du code en question (j'ai omis les trucs de gestion des erreurs):Comment puis-je pousser une instance d'une classe C++ encapsulée avec swig sur une pile lua?

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 

Au moment tout ce que je ont trouvé que cela peut fonctionner est d'exposer certaines fonctionnalités à partir du fichier cpp généré swig, et appeler cela. C'est mauvais pour quelques raisons ... Cela ne fonctionnera pas si j'ai plusieurs modules et que j'ai dû changer la spécification de liaison par défaut dans le fichier swig (en utilisant -DSWIGRUNTIME =).

j'ajouter ce qui suit à 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); 

qui obtient un pointeur sur le module, puis un pointeur vers le type, puis appelle rasades fonction pour l'enregistrer. C'était une chose déraisonnable d'avoir à creuser dans un fichier qui n'est pas supposé être lisible par l'homme (donc ça se trouve en haut du fichier) et c'est juste MESSY! (mais ça marche!)

Sûrement il y a une meilleure façon d'accomplir ce que j'essaie de faire. PS d'un pov de haut niveau ce que je veux, c'est que lua ne recompose pas les composants Gui qui sont créés par Object Factory dans GuiInst, au cas où je m'y prendrais mal. C'est la première fois que j'expose des fonctionnalités à un langage de script en dehors de quelques modules python très simples (et non swig), donc je suis prêt à prendre conseil.

Merci pour votre conseil!


Réponse au commentaire par RBerteig

de contructor de guiinst est #defined à privé lorsque rasade court pour empêcher la construction d'instances lua de celui-ci, de sorte que cela ne fonctionne pas pour moi. Ce que je voulais éviter était la suivante (en lua):

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

qui appellerait « g = new guibutton » puis enregistrez-le GuiRegionVertical (qui a besoin de stocker un pointeur pour diverses raisons), puis appelez "supprimer g", et le GuiRegionVertical est laissé avec un pointeur dangling à g. Je soupçonne que ce qui doit vraiment arriver est que GuiRegionVertical :: Add (GuiButton *) devrait incrémenter le nombre de ref de GuiButton *, puis le destructeur de GuiRegionVertical devrait décrémenter les refcounts de tout son contenu, bien que je ne sois pas sûr comment cela devrait être fait avec swig.

Cela supprimerait le besoin pour les constructeurs privés, la Gui Object Factory et les externs méchants.

Est-ce que je vais mal?

Merci.

+0

duplication possible de [SWIG: Lua - Passage d'une instance C++ comme paramètre de fonction lua] (http://stackoverflow.com/questions/9455552/swiglua-passing-ac-instance-as-a-lua-function- paramètre) –

+1

@NicolBolas: Comment cette question peut-elle être un doublon? C'est trois ans de plus que celui-là :). Merci pour le lien tho. – DaedalusFall

Répondre

1

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 enveloppé. Donc, la solution directe est de laisser l'interpréteur Lua appeler le constructeur de SWIG pour créer le nouvel objet.

Pour la enveloppées engine.GuiInst classe, vous pouvez 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 one-shot comme le démarrage du script, la peine de l'exécution d'une chaîne constante par luaL_dostring() est pas mal à tout. Cependant, j'aurais plus de mal à l'éviter dans un rappel d'événement ou une boucle interne.

Il semble qu'il devrait y avoir un moyen de convertir un pointeur directement en un objet encapsulé, je ne le trouve pas dans ma propre poignée de wrappers générés par SWIG.

Edit: Bien sûr, le fragment Lua peut être décomposé aux appels API qui obtiennent la table du moteur global sur la pile, extraire le membre new_GuiInst, appelez-le, appelez le Init global, mais le peu de l'efficacité vient au prix d'une certaine clarté. En ce qui concerne les objets qui ne devraient pas être construits accidentellement en code utilisateur, comme le montre la question clarifiée, ma première impulsion serait de laisser SWIG générer la fonction constructeur, garder une référence privée si nécessaire plus tard, et supprimer de la table. Même un module C est (habituellement) juste une table dont les membres contiennent des valeurs de fonction. La mise en œuvre en C ne les rend pas en lecture seule à moins qu'un effort supplémentaire ne soit fait.

Ainsi, vous pouvez toujours récupérer la valeur de engine.new_GuiInst et le parc dans le registre (voir luaL_ref() et la discussion en section 3.5 of the Lua Reference Manual du pseudo-indice LUA_REGISTRYINDEX pour les détails) pour une utilisation ultérieure. Ensuite, avant de laisser s'exécuter un code utilisateur, faites simplement l'équivalent de engine.new_GuiInst = nil. Je dois noter que pour les types de données C avec lesquels j'ai joué 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 souhaitez définir les deux noms sur nil. Si vous avez beaucoup moins d'expérience avec les classes C++ SWIG wrapping, et le résultat peut différer ...

Vous voudrez peut-être vérifier et revoir tout le contenu du tableau engine retourné par SWIG, et créer un objet proxy qui contient seulement le méthodes que vous souhaitez mettre à la disposition de vos utilisateurs. Vous pouvez également modifier le environment vu par le script utilisateur afin qu'il dispose uniquement du proxy et nomme également le proxy engine. Il y a eu beaucoup de discussions sur les scripts utilisateur sandboxing sur le Lua list et au lua-users wiki.

+0

Merci pour la réponse (et sa rapidité), j'ai mis à jour ma question car il n'y a pas assez d'espace dans les commentaires. Je voterais pour votre réponse mais ma note n'est pas encore assez élevée. – DaedalusFall

+0

Merci encore, quelques bons conseils là-bas, en particulier WRT les objets proxy. J'ai joué avec luaL_ref il y a peu de temps mais il ne semblait pas y avoir de partage d'objets entre lua_States (comme suggéré par http://www.lua.org/pil/27.3.html) donc je suis passé à autre chose. – DaedalusFall

+0

Non ce n'est pas pour le partage entre les états. C'est un moyen de conserver des références aux objets du côté C afin qu'ils puissent être trouvés à nouveau, et ainsi ils ne sont pas collectés comme des ordures. C'est aussi la manière recommandée de mettre une valeur Lua dans une structure C, en traitant l'int de luaL_ref() comme un pointeur. – RBerteig

3

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% {} 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]; 

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

cet extrait particulier envoie deux de mes pointeurs encapsulés, l'appel handle_web_request (demande, réponse) du côté C++ des choses exécutera la fonction lua globale "handle_web_request" et lui passer mes deux pointeurs, avec la magie SWIG appliqué.

+0

A [une version plus complète de cette réponse peut être trouvée ici.] (Http://stackoverflow.com/questions/9455552/swiglua-passing-a-c-instance-as-a-lua-function-parameter/9455942#9455942) –

Questions connexes