2016-08-02 8 views
3

Wenn ich ein Userdata-Objekt erstellen und es in einer Tabelle speichern, dann erhalten Sie einen Verweis auf es in C/C++, für wie lange ist diese Referenz gültig? Ist der Verweis in C/C++ garantiert gültig, solange die Benutzerdaten in der Tabelle in Lua gespeichert sind? Oder besteht die Gefahr, dass die Lua-Laufzeitumgebung das Userdata-Objekt verschiebt, wodurch die C/C++ - Referenz ungültig wird?Lebensdauer von Lua userdata Zeigern

Hier ist, was ich tue:

// Initially, the stack contains a table 
class Foo { ... }; 
lua_pushstring(L, "my_userdata"); 
void* mem = lua_newuserdata(L, sizeof(Foo)); 
new (mem) Foo(); 
lua_settable(L, -3); 

// Later: 
lua_pushstring(L, "my_userdata"); 
lua_gettable(L, -2); 
Foo *foo = (Foo*)lua_touserdata(L, -1); 
lua_pop(L, 1); 
// How long will this pointer be valid? 

Bin ich besser dran operator new und ein Licht-Userdaten mit hier?

Antwort

2

die Referenz (oder ein Zeiger, da Lua ist in C geschrieben) wird für die gesamte Lebensdauer der Benutzerdaten gültig bleiben.

Lua Chefarchitekt adressiert diese auf dem Lua-l mailing list:

Zitat: 18. April 2006; Roberto Ierusalimschy

Die Vorsicht ist über Strings, nicht über Userdata (obwohl wir eigentlich nicht explizit im Handbuch gesagt haben). Wir haben nicht die Absicht zuzulassen, dass sich Benutzeradressen während GC ändern. Im Gegensatz zu Strings, die sind eine interne Daten in Lua, der einzige Zweck von Benutzerdaten ist zu von C-Code verwendet werden, die es vorziehen, dass die Dinge bleiben, wo sie :)

sind Sie die Lebensdauer eines Benutzerdaten steuern indem sie sie in dem Zustand zu verankern:

Es gibt mehrere Gründe, warum Sie eine vollständige Benutzerdaten über einen lightuserdata bevorzugen:

  • Vollbenutzerdaten kann seine eigene Uservalue und Metatabelle haben (alle lightuserdata Aktien die gleiche Metatabelle)
  • Finalisierung obwohl die __gc metamethod
  • Mehrere Bequemlichkeit API Funktionen für die Benutzerdaten mit Arbeits (luaL_newmetatable, luaL_setmetatable, etc.)

Ein üblicher Weg, Benutzerdaten von einer Klasse in C++ zu schaffen, ist die pointer-to-pointer idiom zu verwenden:

class Foo { ... }; 

static int new_Foo(lua_State *L) { 
    // begin userdata lifetime 
    Foo **ud = static_cast<Foo **>(lua_newuserdata(L, sizeof *ud)); 
    luaL_setmetatable(L, "Foo"); 

    // begin C++ object lifetime 
    *ud = new Foo(); 
    return 1; 
} 

// __gc metamethod 
static int delete_Foo(lua_State *L) { 
    Foo **ud = static_cast<Foo **>(luaL_checkudata(L, 1, "Foo")); 

    // end C++ object lifetime 
    delete *ud; 

    // end userdata lifetime 
    return 0; 
} 
+0

Danke, große Antwort.Gibt es ein Problem damit, stattdessen 'void * men = lua_newuserdata (L, sizeof (Foo)) zu sagen; Foo * f = neu (Männer) Foo(); '? – Tom

+1

@tom das Problem ist, wer wird 'löschen' anrufen? Es gibt zwei unterschiedliche Lebenszeiten; die userdata (verwaltet mit C's 'realloc') und das C++ Objekt (verwaltet mit' new'/'delete'). Bei einer Platzierung "neu" wird die Erinnerung geteilt, Lua wird sie glücklich sammeln und Sie werden ein undefiniertes Verhalten haben. Das Pointer-zu-Pointer-Idiom ermöglicht die Koexistenz beider Lebensdauern. Lua verwaltet seinen Speicher mit 'realloc' und in einem' __gc' Metamethode können Sie 'delete' auf Ihrem C++ Objekt aufrufen. – Adam

+2

Sie könnten den Destruktor manuell in der Metamethode '__gc' aufrufen. Das ist sowieso normal, wenn Sie Placement neu verwenden ... – siffiejoe