2013-08-15 8 views
8

Mam klasy w C++ o nazwie „Punkt”:Jak wyodrębnić wskaźnik C++ obiektu od Lua

class Point 
{ 
public: 
    int x, y; 

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

Moim celem jest, aby móc instancję obiektu Point ze skryptu Lua, i wyodrębnić wskaźnik ten obiekt ze stosu Lua.

Oto moja (obecnie nie działa) próba, która, mam nadzieję, wyjaśnia, co dokładnie próbuję zrobić; pamiętać, że ten kod jest zasadniczo zmodyfikowane kopie/pasty z this tutorial i że używam 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; 
} 

A oto skrypt Lua:

local point = Point.new(10,20) 

Uruchomienie tego kodu, pojawia się następujący błąd : "zły argument nr 3 (oczekiwany punkt, otrzymałem tabelę)" na linii: ud = luaL_checkudata (L, indeks, "Punkt") wewnątrz funkcji checkPoint().

Jeśli ktoś mógłby mnie skierować we właściwym kierunku, byłoby to bardzo cenne.

Odpowiedz

8

Istnieje kilka problemów związanych z powyższym użyciem i wiązaniem.

Wewnątrz skryptu testowego,nie będzie widoczny w programie C++ hosta. Jest tak, ponieważ, cóż, jest to lokalne dla tego skryptu. Jeśli chcesz, aby był dostępny z C++, zwróć wartość point jako wartość ze skryptu lub ustaw globalnie i pobierz ją, używając lua_getglobal(L, "point").

Funkcja checkPoint ma niepotrzebny kod. Jeśli spojrzysz na inne funkcje luaL_check* dostarczone przez API Lua C, głównie sprawdzają, czy podana wartość w indeksie stosu jest właściwa. Jeśli jest to następnie przekonwertuj ten typ do czegoś, z czego może korzystać C++.

Więc w funkcji 'checkpoint' wszystko co naprawdę trzeba zrobić, to:

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

Jeśli zmienisz skrypt do tego, na przykład:

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

powinny mieć możliwość dostępu point z C++ w następujący sposób:

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

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

Innym ważnym punktem jest regardin g Czas życia obiektu C++ po wystawieniu na działanie lua. Ponieważ przydzielasz i budujesz z "nowym" operatorem C++ za każdym razem, gdy wywoływane jest Point.new ze skryptu, pośrednio mówisz, że pozwala lua poradzić sobie z tym odsłoniętym obiektem C++ o czasie życia. Oznacza to, że będziesz także chciał wprowadzić metametod __gc, aby działał jako "finalizator" lub destruktor niedeterministyczny. Bez tego nie byłbyś w stanie "usunąć" obiektu point i odzyskać miejsca, gdy lua śmieci zbiera odpowiednie dane użytkownika.

Aby to zrobić, można poszerzyć swój kod w następujący sposób:

  • Napisz deletePoint funkcję, która jest wywoływana przez lua na userData GC.

    static int deletePoint(lua_State *L) 
    { 
        Pointer **p = checkPoint(L, 1); 
        delete *p; 
        *p = NULL; // probably not needed but to be safe 
        return 0; 
    } 
    
  • Dodaj do listy pointFuncs że tak lua o tym wie.

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

Dokładnie to, co musiałem wiedzieć! 1000x dzięki tobie, proszę pana. – user2687268

+0

@ user2687268 Dodałem notatkę o obsłudze danych użytkownika clean. – greatwolf

+0

to dobre wytłumaczenie, ale wydaje się zakładać, że garbage collector nie uruchomi się, zanim wskaźnik punktowy uzyska punkt od stanu lua. To może nie dotyczyć tego prostego przykładu, ale w bardziej rozbudowanych skryptach nie będziesz wiedział, kiedy gc usunie twój punkt. Stąd całe zamieszanie zarządzania czasem pamięci w bibliotekach wiążących. –

5

polecam nie pisać własne wiążące, ale raczej użyć przetestowane i udokumentowane jeden, takich jak luabridge lub luabind

Twój wiązaniami zostanie zmniejszona do:

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

a lua będzie Wygląda jak

local point = Point(10,20) 

z luabridge.

Aby uzyskać wartości wyodrębniające, patrz na przykład: LuaRef in luabridge. Jeśli używasz C++ 11 istnieją inne całkiem piękne biblioteki wiążące lua.

+1

I na pewno sprawdzić luabind. Jako doświadczenie edukacyjne chciałbym jednak, aby to działało. Niestety nadal mam ten sam błąd, co poprzednio, nawet po tym, jak przełączyłem się na operatora dwukropka w moim kodzie Lua i zmieniłem funkcję sprawdzania argumentów w funkcji newPoint(). Czy przypadkiem wiesz, dlaczego tak jest? – user2687268

+0

zaktualizowałem moją odpowiedź, ponieważ id nie posiadało prawdziwego wyjaśnienia, a superwolf wyjaśnił –