2013-08-15 11 views
8

Ho una classe in C++ chiamato "Punto":Come estrarre C++ puntatore oggetto da Lua

class Point 
{ 
public: 
    int x, y; 

    //constructor 
    Point(int x, int y) 
    { 
    this->x = x; 
    this->y = y; 
    } 
}; 

Il mio obiettivo è quello di essere in grado di istanziare un oggetto Point con uno script Lua, ed estrarre il puntatore questo oggetto dalla pila Lua.

Ecco il mio (attualmente non funzionante) tentativo che si spera chiarisca cosa esattamente sto cercando di fare; notare che questo codice è essenzialmente modificato copia/incolla da this tutorial e che sto usando Lua 5.2:

static int newPoint(lua_State *L) 
{ 
    int n = lua_gettop(L); 
    if (n != 2) 
     return luaL_error(L, "expected 2 args for Point.new()", n); 

    // Allocate memory for a pointer to object 
    Point **p = (Point **)lua_newuserdata(L, sizeof(Point *)); 

    double x = luaL_checknumber (L, 1);  
    double y = luaL_checknumber (L, 2); 

    //I want to access this pointer in C++ outside this function 
    *p = new Point(x, y); 

    luaL_getmetatable(L, "Point"); // Use global table 'Point' as metatable 
    lua_setmetatable(L, -2); 

    return 1; 
} 

static const luaL_Reg pointFuncs[] = { 
    {"new", newPoint}, 
    {NULL, NULL} 
}; 

//register Point to Lua 
void registerPoint(lua_State *L) 
{ 
    lua_createtable(L, 0, 0); 
    // Register metatable for user data in registry 
    luaL_newmetatable(L, "Point"); 
    luaL_setfuncs(L, pointFuncs, 0);   
    lua_pushvalue(L,-1); 
    lua_setfield(L,-2, "__index"); 
    lua_setglobal(L, "Point"); 
} 

Point* checkPoint(lua_State* L, int index) 
{ 
    void* ud = 0; 
    luaL_checktype(L, index, LUA_TTABLE); 
    lua_getfield(L, index, "__index"); 
    ud = luaL_checkudata(L, index, "Point");; 

    return *((Point**)ud);  
} 

int main() 
{ 
    lua_State *L = luaL_newstate(); 
    luaL_openlibs(L); 
     registerPoint(L); 
    luaL_dofile(L, "testz.lua"); 
    lua_getglobal(L, "Point"); 
    Point *foo = checkPoint(L, lua_gettop(L)); 
     std::cout << "x: " << foo->x << " y: " << foo->y; 
    lua_close(L); 
    return 0; 
} 

E qui è lo script Lua:

local point = Point.new(10,20) 

L'esecuzione di questo codice, ottengo il seguente errore : "argomento non valido # 3 (punto previsto, ottenuto tabella)" alla riga: ud = luaL_checkudata (L, indice, "Punto") all'interno della funzione checkPoint().

Se qualcuno potesse guidarmi nella giusta direzione, sarebbe molto apprezzato.

risposta

8

Ci sono un paio di problemi nell'utilizzo e nella rilegatura di cui sopra.

All'interno del tuo script di test, local pointnon sarà visibile dal tuo programma C++ dell'host. Questo perché, beh, è ​​locale a quello script. Se lo si desidera accessibile da C++, restituire point come valore dallo script o renderlo globale e recuperarlo utilizzando lua_getglobal(L, "point").

La funzione checkPoint ha un codice non necessario. Se si guardano le altre funzioni luaL_check* fornite dall'API Lua C, controllano principalmente se il valore dato nell'indice stack è corretto. Se è convertito, digita su qualcosa che C++ può usare.

Quindi nel tuo funzione 'checkpoint' tutto quello che occorre fare è:

Point* checkPoint(lua_State* L, int index) 
{ 
    return *((Point **) luaL_checkudata(L, index, "Point")); 
} 

Se si modifica lo script a questo, ad esempio:

local point = Point.new(10,20) 
return point 

si dovrebbe essere in grado di accedere point da C++ nel seguente modo:

// ... 
luaL_dofile(L, "testz.lua"); 

Point *foo = checkPoint(L, -1); 
std::cout << "x: " << foo->x << " y: " << foo->y; 
// ... 

altro punto importante è regardin g Durata dell'oggetto C++ quando esposto a lua. Poiché stai assegnando e costruendo con il nuovo operatore C++ ogni volta che lo script chiama Point.new, stai dicendo indirettamente che lascia che lua gestisca la durata dell'oggetto dell'oggetto esposto C++. Ciò significa che dovrai anche implementare un metametodo __gc per fungere da "finalizzatore" o distruttore non deterministico. Senza questo non avresti modo di cancellare l'oggetto point e recuperare lo spazio quando lua garbage raccoglie i dati utente corrispondenti.

Per fare questo si può aumentare il vostro codice come segue:

  • Scrivete una funzione deletePoint che viene chiamato da Lua su GC userdata.

    static int deletePoint(lua_State *L) 
    { 
        Pointer **p = checkPoint(L, 1); 
        delete *p; 
        *p = NULL; // probably not needed but to be safe 
        return 0; 
    } 
    
  • aggiungere che, per il tuo modo pointFuncs lua lo sa.

    static const luaL_Reg pointFuncs[] = 
    { 
        {"new", newPoint}, 
        {"__gc", deletePoint}, 
        {NULL, NULL} 
    }; 
    
+1

Esattamente quello che dovevo sapere! 1000 volte grazie a voi signore. – user2687268

+0

@ user2687268 Ho aggiunto una nota sulla gestione di userdata clean. – greatwolf

+0

questa è una buona spiegazione, ma sembra presumere che il garbage collector non verrà eseguito prima che il puntatore del punto ottenga il punto dallo stato lua. Questo potrebbe non essere il caso con questo semplice esempio, ma in script più elaborati non saprai quando gc cancellerà il tuo punto. Quindi, tutta la confusione della gestione della memoria nella vita delle librerie vincolanti. –

5

io consiglierei di non scrivere il proprio legame, ma piuttosto uso un testato e documentato uno, come ad esempio luabridge o luabind

vostro legame si ridurrebbe a:

getGlobalNamespace (L) 
    .beginClass<Point>("Point") 
    .addConstructor<void (*) (int,int)>() 
    .addData("X", &Point::x) 
    .addData("Y", &Point::y) 
    .endClass() 
; 

e la vostra lua sarebbe assomigliare a

local point = Point(10,20) 

con luabridge.

Per l'estrazione di valori, vedere ad esempio LuaRef in luabridge. Se usi C++ 11 ci sono altre librerie di rilegatura lua piuttosto belle.

+1

sarò sicuramente check out luabind. Come esperienza di apprendimento, desidero farlo funzionare comunque. Sfortunatamente ho ancora lo stesso errore di prima anche dopo che sono passato all'operatore colon nel mio codice Lua e ho cambiato il controllo degli argomenti nella funzione newPoint(). Sapresti perché è così? – user2687268

+0

ha aggiornato la mia risposta in quanto l'ID non aveva una spiegazione reale, e greatwolf ha spiegato –