2009-04-07 19 views
6

Ich habe ein Lua Userdata-Objekt mit einem bestimmten Metatyp (z. B. "stackoverflow.test"). Aus dem C-Code möchte ich in der Lage sein, genau zu prüfen, um welchen Typ es sich handelt, und sich je nach den Ergebnissen anders zu verhalten. Gibt es eine nette handliche Funktion (eher wie luaL_checkudata, aber ohne Fehler, wenn die Antwort nicht das ist, was Sie wollen), lassen Sie mich den metatable Typnamen der Benutzerdaten abfragen? Wenn nicht, muss ich lua_getmetatable verwenden, aber dann bin ich etwas unklar, wie ich den Namen der Metatabelle ermitteln kann, die gerade dem Stapel hinzugefügt wurde.Abfrage Lua Benutzerdatentyp von C

Nur um zu verdeutlichen: Ich benutze Lua 5.1, wo das Verhalten von luaL_checkudata geändert wurde. Ich verstehe, dass in 5.0 es an Fehler nicht gewöhnt hat.

+0

Es scheint, dass lua 5.2 hat, was Sie suchen: [luaL_testudata] (http://www.lua.org/source/5.2/lauxlib.c.html#luaL_testudata) –

Antwort

3

Sie lua_getmetatable und lua_equal den Zweck zu testen, dass die Tabellen sind die gleichen verwenden werden.

Meiner Meinung nach sollte Lua mehr Unterstützung für diese Art von Typ erweitern. Ab sofort liegt es in der Verantwortung des Wrapper-Systems Lua/C (++), dies zu tun.

In einem Wrapper, den ich vor kurzem (als Teil eines kommerziellen Projekts) gemacht habe, mache ich class::instance(L,index), um Benutzerdatenzeiger eines bestimmten Typs zu erhalten. Mit anderen Worten, diese Methode überprüft, dass es userdata ist und dass die Metatabelle auch richtig ist. Wenn nicht, wird NULL zurückgegeben.

Die Art und Weise, wie Lua all dies helfen könnte, ist, wenn das Metatable ein Standardfeld für erweiterte Typinformationen hatte (s.a. __type). Dies könnte so verwendet werden, dass type() selbst "userdata", "xxx" (zwei Werte, die derzeit nur einen Wert zurückgeben) zurückgibt. Dies würde mit den meisten aktuellen Codes kompatibel bleiben. Aber das ist nur hypothetisch (es sei denn, Sie tun einen benutzerdefinierten Typ() und implementieren dies auf eigene Faust).

0

Ich habe gerade den Quellcode zu der luaL_checkudata Funktion betrachtet, und es ruft im Grunde die Metatable des Userdata-Objekts mit lua_getmetatable ab. Anschließend ruft es den angegebenen Typnamen aus der Registrierung unter Verwendung lua_getfield ab und führt einen Aufruf lua_rawequal, um sie zu vergleichen.

5

Sie könnten ein Markierungsfeld in der Metatabelle immer mit einem leichten Benutzerdatenwert speichern, der für Ihr Modul eindeutig ist.

static const char *green_flavor = "green"; 
... 
void my_setflavor(lua_State *L, void *flavor) { 
    lua_pushlightuserdata(L,flavor); 
    lua_pushlstring(L,"_flavor"); 
    lua_rawset(L,-3); 
} 

void my_isflavor(lua_State *L, void *flavor) { 
    void *p = NULL; 
    lua_pushlstring(L,"_flavor"); 
    lua_rawget(L,-2); 
    p = lua_touserdata(L,-1); 
    lua_pop(L,1); 
    return p == flavor; 
} 

Dann können Sie my_setflavor(L,&green_flavor) verwenden, um das _flavor Feld der Tabelle ganz oben auf dem Stapel zu setzen und my_isflavor(L,&red_flavor) an der Spitze des Stapels das _flavor Feld der Tabelle zu testen. Auf diese Weise kann das _flavor-Feld nur Werte annehmen, die durch Code in dem Modul mit dem Symbol green_flavor im Gültigkeitsbereich erzeugt werden können, und das Nachschlagen des Feldes und das Testen seines Werts erforderte nur eine Tabellensuche neben dem Abruf der Metatable selbst. Beachten Sie, dass der Wert der Variablen green_flavor keine Rolle spielt, da nur seine Adresse tatsächlich verwendet wird.

Da mehrere verschiedene Flavor-Variablen zur Verwendung als Sentinal-Werte zur Verfügung stehen, kann das Feld _flavor zur Unterscheidung mehrerer verwandter Metatabellen verwendet werden.

Alles gesagt, eine natürliche Frage ist "warum überhaupt?" Schließlich kann das Metatable leicht alle Informationen enthalten, die es benötigt, um das richtige Verhalten zu erzielen. Es kann sowohl Funktionen als auch Daten bereithalten, und diese Funktionen können sowohl von C als auch von Lua abgerufen und aufgerufen werden.

2

Die Userdata muss eine Metatable haben, also greifen Sie; dann suchen Sie den gewünschten Namen in der Registrierung. Wenn die beiden Objekte identisch sind, haben Sie den Typ gefunden, nach dem Sie suchen.

Sie könnten auf diese Art in C-Code versenden, aber erlauben Sie mir sanft, dass Sie stattdessen ein Feld des Metatable angeben. Eine Funktion, die in der Metatabelle gespeichert ist, sollte die Aufgabe erledigen, aber wenn nicht, müssen Sie unbedingt switch in C-Code, dann wählen Sie einen Namen, verwenden Sie diese, um in die Metatabelle zu indizieren und jeder Metatabelle eine kleine ganze Zahl zuzuweisen.

meta1.decision = 1 
meta2.decision = 2 
meta3.decision = 3 

und dann in Ihrem C-Code

if (lua_getmetatable(L, 1)) { 
    lua_getfield(L, -1, "decision"); 
    if (lua_isnumber(L, -1)) { 
    switch ((int) lua_tonumber(L, -1)) { 
     case 1: ... ; break; 
     case 2: ... ; break; 
     case 3: ... ; break; 
    } 
    return 0; 
    } 
} 
return luaL_error(L, "Userdata was not one of the expected types");