Come faccio a spingere un'istanza di una classe c ++ avvolta con swig su uno stack lua?

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

  •  03-07-2019
  •  | 
  •  

Domanda

Ho una classe che è avvolta con un sorso e registrata con lua. Posso creare un'istanza di questa classe in uno script lua e tutto funziona bene.

Ma supponiamo che io abbia un'istanza di una classe creata nel mio codice c ++ con una chiamata alla nuova X, e ho la lua_state L con una funzione che voglio chiamare, che accetta un argomento, un'istanza di X ... Come posso chiamare quella funzione. Ecco (parte) del codice in questione (ho omesso la gestione degli errori):

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

Al momento tutto ciò che ho scoperto che può funzionare è quello di esporre alcune funzionalità dal file cpp generato dallo swig e chiamarlo. Questo è male per alcuni motivi ... Non funzionerà se ho più moduli e ho dovuto cambiare la specifica di collegamento predefinita nel file di swig (usando -DSWIGRUNTIME =).

Aggiungo quanto segue 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);

Questo ottiene un puntatore al modulo, quindi un puntatore al tipo, quindi chiama la funzione swigs per registrarlo. È stato irragionevole dover scavare in un file che non dovrebbe essere leggibile dall'uomo (come dice all'inizio del file) ed è MESSY! (ma funziona!)

Sicuramente esiste un modo migliore per realizzare ciò che sto cercando di fare.

PS da un punto di vista di alto livello quello che voglio è avere lua non ricontattare i componenti Gui che sono stati creati dalla Fabbrica degli oggetti in GuiInst, nel caso in cui sto sbagliando. Questa è la prima volta che espongo le funzionalità a un linguaggio di scripting oltre ad alcuni moduli python molto semplici (e non swig), quindi sono pronto a dare consigli.

Grazie per qualsiasi consiglio!


Risposta al commento di RBerteig

Il contrettore di GuiInst è #definito come privato quando si esegue il parrucca per impedire a Lua di costruirne istanze, quindi non funzionerà per me. Quello che stavo cercando di prevenire era il seguente (in lua):

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

che chiamerebbe " g = new GuiButton " quindi registralo con GuiRegionVertical (che deve memorizzare un puntatore per vari motivi), quindi chiama " elimina g " ;, e GuiRegionVertical viene lasciato con un puntatore pendente a g.

Ho il sospetto che ciò che deve realmente accadere è che GuiRegionVertical :: Add (GuiButton *) dovrebbe aumentare il conteggio dei ref di GuiButton *, e quindi il distruttore di GuiRegionVertical dovrebbe diminuire i conteggi di tutti i suoi contenuti, anche se non sono sicuro come dovrebbe essere fatto con il sorso.

Ciò eliminerebbe la necessità dei costruttori privati, della Gui Object Factory e dei cattivi esterni.

Ho intenzione di sbagliare?

Grazie.

È stato utile?

Soluzione

Esiste una risposta semplice e diretta, che potrebbe non essere la risposta più efficace. SWIG produce wrapper per manipolare oggetti dal lato del linguaggio di scripting. Per gli oggetti, sintetizza anche un costruttore avvolto. Quindi, la soluzione diretta è lasciare che l'interprete Lua chiami il costruttore di SWIG per creare il nuovo oggetto.

Per la classe engine.GuiInst , quasi sicuramente puoi fare qualcosa del tipo:

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

Per un caso one-shot come l'avvio dello script, la penalità di eseguire una costante di stringa attraverso luaL_dostring () non è affatto male. Guarderei di più per evitarlo in un callback di eventi o in un loop interno, tuttavia.

Sembra che ci dovrebbe essere un modo per convertire un puntatore direttamente in un oggetto avvolto, non lo sto individuando nella mia manciata di wrapper generati da SWIG.

Modifica: Naturalmente, il frammento Lua può essere scomposto in chiamate API che mettono la tabella del motore globale nello stack, estrae da essa il membro new_GuiInst , chiamalo , quindi chiama il Init globale, ma un po 'di efficienza ha un costo di chiarezza.

Per quanto riguarda la gestione di oggetti che non devono essere costruiti accidentalmente nel codice utente, come indica la domanda chiarita, il mio primo impulso sarebbe di consentire a SWIG di generare la funzione di costruzione, mantenere un riferimento privato se necessario in seguito e rimuoverlo dal tavolo. Anche un modulo C è (di solito) solo una tabella i cui membri contengono valori di funzione. L'implementazione in C non li rende di sola lettura a meno che non vengano presi ulteriori sforzi.

Quindi, puoi sempre recuperare il valore di engine.new_GuiInst e parcheggiarlo nel registro (vedi luaL_ref () e la discussione in sezione 3.5 del Manuale di riferimento di Lua dello pseudo-indice LUA_REGISTRYINDEX per i dettagli) per un uso successivo. Quindi, prima di consentire l'esecuzione di qualsiasi codice utente, è sufficiente fare l'equivalente di engine.new_GuiInst = nil . Devo notare che per i tipi di dati C con cui ho giocato di recente, SWIG ha creato due costruttori per ogni tipo, denominati new_TYPE e TYPE . Entrambi erano visibili nella tabella del modulo e si vorrebbe impostare entrambi i nomi su nil . Se hai molta meno esperienza con SWIG che avvolge le classi C ++ e il risultato potrebbe essere diverso ...

Potresti voler controllare e rivedere l'intero contenuto della tabella engine restituita da SWIG e creare un oggetto proxy che contenga solo i metodi che desideri siano disponibili per i tuoi utenti. Puoi anche modificare ambiente visto dallo script utente in modo che ha solo il proxy disponibile e nomina anche il proxy engine . C'è stata una buona dose di discussione sugli sandboxing sugli script utente Elenco Lua e nella lua-users wiki .

Altri suggerimenti

Meglio tardi che mai, e questa soluzione aiuterà altre persone.

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

questo codice deve essere all'interno dei blocchi% {}% nel tuo file .i, poiché SWIGTYPE_p_WebRequest è

#define SWIGTYPE_p_WebResponse swig_types[6]

e swig_types [6] è

static swig_type_info *swig_types[12];

che significa che swig_types è accessibile solo dal file C ++ da cui è definito.

questo particolare frammento sta inviando due dei miei puntatori a capo automatico, quindi la chiamata a handle_web_request (richiesta, risposta) dal lato C ++ delle cose eseguirà la funzione lua globale " handle_web_request " e passa i miei due puntatori, con la magia SWIG applicata.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top