6 Stimmen

Abfrage des Lua-Benutzerdatentyps von C aus

Ich habe ein Lua-Userdata-Objekt mit einem bestimmten metatable Typ (z.B. "stackoverflow.test" ). Von C-Code, möchte ich in der Lage sein, genau zu überprüfen, welche Art es ist, und verhalten sich unterschiedlich je nach den Ergebnissen. Gibt es eine nette praktische Funktion (eher wie luaL_checkudata (aber ohne Fehlermeldung, wenn die Antwort nicht das ist, was Sie wollen), die mich den metatable Typnamen der Benutzerdaten abfragen lässt? Wenn nicht, muss ich wohl Folgendes verwenden lua_getmetatable aber dann ist mir nicht ganz klar, wie ich den Namen der Metadatei, die gerade zum Stapel hinzugefügt wurde, bestimmen soll.

Nur zur Klarstellung: Ich verwende Lua 5.1, wo das Verhalten von luaL_checkudata geändert wurde. Ich verstehe, dass es in 5.0 nicht verwendet, um Fehler.

5voto

RBerteig Punkte 39719

Sie können immer ein Markierungsfeld in der Metatabelle mit einem leichten Benutzerdatenwert speichern, der nur für Ihr Modul gilt.

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) um das Feld _flavor der Tabelle an die Spitze des Stapels zu setzen, und my_isflavor(L,&red_flavor) um das Feld _flavor der Tabelle am oberen Ende des Stapels zu testen.

Auf diese Weise kann das Feld _flavor nur Werte annehmen, die durch Code in dem Modul erstellt werden können, das das Symbol green_flavor im Anwendungsbereich hat, und das Aufsuchen des Feldes und das Testen seines Wertes erfordert nur einen Tabellenaufruf neben dem Abruf der Metatable selbst. Beachten Sie, dass der Wert der Variablen green_flavor keine Rolle spielt, da nur ihre Adresse tatsächlich verwendet wird.

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

Nach all dem stellt sich natürlich die Frage: "Warum machen wir das überhaupt? Schließlich könnte die Metatabelle problemlos alle Informationen enthalten, die für das entsprechende Verhalten erforderlich sind. Es kann sowohl Funktionen als auch Daten enthalten, und diese Funktionen können sowohl von C als auch von Lua abgerufen und aufgerufen werden.

3voto

akauppi Punkte 15498

Sie verwenden lua_getmetatable y lua_equal um zu prüfen, ob die Tabellen identisch sind.

Meiner Meinung nach sollte Lua mehr Unterstützung für diese Art von Typ-erweiternden Dingen bieten. Im Moment liegt es wirklich in der Verantwortung des Lua/C(++)-Wrapper-Systems, das zu tun.

In einem Wrapper, den ich kürzlich erstellt habe (als Teil eines kommerziellen Projekts), mache ich class::instance(L,index) um Userdata-Zeiger eines bestimmten Typs zu erhalten. Mit anderen Worten, diese Methode prüft, ob es sich um userdata handelt und ob die metatable auch richtig ist. Wenn nicht, gibt sie NULL zurück.

Lua könnte bei all dem helfen, wenn die Metatable ein Standardfeld für erweiterte Typinformationen hätte (s.a. __type ). Dies könnte so genutzt werden, dass type() selbst würde "userdata", "xxx" zurückgeben (zwei Werte, derzeit wird nur einer zurückgegeben). Dies wäre mit dem Großteil des aktuellen Codes kompatibel. Aber das ist nur hypothetisch (es sei denn, Sie machen einen benutzerdefinierten Typ() und implementieren dies auf eigene Faust).

2voto

Norman Ramsey Punkte 193087

Die Userdata müssen eine Metatabelle haben, also holen Sie sich diese; dann suchen Sie den gewünschten Namen in der Registrierung. Wenn die beiden Objekte identisch sind, haben Sie den Typ gefunden, den Sie suchen.

Sie könnten in C-Code auf diesen Typ zugreifen, aber erlauben Sie mir, vorsichtig vorzuschlagen, dass Sie stattdessen ein Feld des metatable bestimmen. Eine in der Metatable gespeicherte Funktion sollte die Aufgabe erfüllen, aber falls nicht, sollten Sie, wenn Sie unbedingt müssen switch in C-Code, wählen Sie dann einen Namen, verwenden Sie diesen, um in die Metatable zu indizieren, und weisen Sie jeder Metatable eine kleine Ganzzahl zu, die Sie einschalten können.

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

0voto

andygeers Punkte 6677

Ich habe mir gerade den Quellcode der luaL_checkudata Funktion und holt grundsätzlich die Metatable des userdata-Objekts mit lua_getmetatable . Es holt dann den angegebenen Typnamen aus der Registrierung mit lua_getfield und macht eine lua_rawequal anrufen, um sie zu vergleichen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X