2013-02-17 10 views
5

HintergrundLua, C++ und verschwinden Metatables

ich mit Watusimoto Bitfighter auf dem Spiel arbeiten. Wir verwenden eine Variation von LuaWrapper, um unsere C++ - Objekte mit Lua-Objekten im Spiel zu verbinden. Wir verwenden auch eine Variante von Lua, genannt lua-vec, um Vektoroperationen zu beschleunigen.

Wir haben seit einiger Zeit daran gearbeitet, einen Fehler zu beheben, der uns entgangen ist. Zufällige Abstürze werden auftreten, die korrupte Metatables vorschlagen. Siehe here für Watusimotos Beitrag zu diesem Problem. Ich bin nicht sicher, dass es wegen einer korrupten Metatabelle ist und habe ein wirklich merkwürdiges Verhalten gesehen, über das ich hier fragen möchte.

Das Problem Äusserung

Als Beispiel erstellen wir ein Objekt, und fügen Sie es wie folgt auf ein Niveau:

t = TextItem.new() 
t:setText("hello") 
levelgen:addItem(t) 

jedoch wird das Spiel manchmal (nicht immer) zum Absturz bringen. Mit einem Fehler:

attempt to call missing or unknown method 'addItem' (a nil value) 

einen Vorschlag in Antwort gegeben Verwendung zu Watusimoto der Post oben erwähnt, habe ich die letzte Zeile wie folgt geändert:

local ok, res = pcall(function() levelgen:addItem(t) end) 

if not ok then 
    local s = "Invalid levelgen value: "..tostring(levelgen).." "..type(levelgen).."\n" 

    for k, v in pairs(getmetatable(levelgen)) do 
     s = s.."meta "..tostring(k).." "..tostring(v).."\n" 
    end 

    error(res..s) 
end 

Dies gibt die Metatabelle für levelgen wenn etwas, wenn falsch, eine Methode von ihm aufzurufen.

Allerdings, und das ist verrückt, wenn es fehlschlägt und die Metatabelle ausdruckt, die Metatabelle ist genau, wie es (mit dem richtigen addItem Anruf und alles) sein sollte. Wenn ich die Metatabelle für levelgen beim Laden von Skript drucken, und wenn es oben pcall nicht funktioniert, sind sie identisch, jeder Aufruf und Zeiger auf Benutzerdaten ist identisch und wie es sein sollte.

Es ist, als ob das Metatable für levelgen spontan zufällig verschwindet.

Würde jemand eine Idee haben, was los ist?

Danke

Hinweis: Dies nicht nur mit dem levelgen Objekt geschieht. Zum Beispiel ist es auch auf dem oben erwähnten Objekt passiert. In der Tat, stürzt das gleiche Code auf meinem Computer an der Linie levelgen:addItem(t) aber stürzt auf einen anderen Computer des Entwicklers mit der Linie t:setText("hello") mit der gleichen Fehlermeldung missing or unknown method 'setText' (a nil value)

+0

Haben Sie versucht, Ihren Code unter Valgrind auszuführen? Sie haben möglicherweise ein Speicherbeschädigungsproblem von C++. – nneonneo

+0

Hallo, ja, wir haben es jetzt mehrere Male durch valgrind laufen lassen, und nachdem wir die Speicherfehler behoben haben, die dort waren (in nicht verwandtem Code), hat das Problem immer noch bestanden – raptor

+0

Sehr neugierig. Können Sie das Problem auf einen kurzen Testfall reduzieren? – nneonneo

Antwort

1

Nach dem Zusammenführen mit dem Upstream (Commit 3c54015) LuaWrapper ist dieses Problem verschwunden. Es scheint sich um einen Fehler in LuaWrapper gehandelt zu haben.

Danke Alex!

2

Wie bei jedem Geheimnis, müssen Sie es Schicht für Schicht abzulösen. Ich empfehle, die gleichen Schritte zu durchlaufen, die Lua macht und herauszufinden, wo der Weg von Ihren Erwartungen abweicht:

Was gibt getmetatable(levelgen).__index zurück? Wenn es sich um eine Tabelle handelt, überprüfen Sie den Inhalt auf addItem. Wenn es eine Funktion ist, dann versuchen Sie es mit (table, "addItem") aufzurufen und zu sehen, was es zurückgibt.

Überprüfen Sie, ob getmetatable Referenz auf das gleiche Objekt vor und nach dem Aufruf (oder wenn es fehlschlägt) zurückgibt.

Gibt es mehrere Ebenen der metatablen Indirektion, die der Anruf durchläuft? Wenn ja, versuchen Sie, den gleichen Pfad mit expliziten Aufrufen zu verfolgen und zu sehen, wo die Unterschiede liegen.

Verwenden Sie Tasten weak, die dazu führen können, dass Werte verschwinden, wenn keine anderen Referenzen vorhanden sind?

Können Sie einen "Standard" -Wert angeben, wenn Sie feststellen, dass es fehlschlägt, und weiterhin sehen, ob diese Methode später erneut gefunden wird? Oder wenn es kaputt ist, ist es danach bei jedem Anruf kaputt?

Was passiert, wenn Sie einen geeigneten Wert für addItem speichern und ihn "reparieren", wenn Sie feststellen, dass er defekt ist?

Was passiert, wenn Sie einfach den Fehler behandeln (wie Sie) und es 10 Mal aufrufen? Würde es mindestens einmal gültige Ergebnisse anzeigen (nachdem es fehlgeschlagen ist)? 100 mal? Wenn Sie die gleiche Methode aufrufen, wenn sie funktioniert, wird sie fehlschlagen? Dies kann Ihnen helfen, einen reproduzierbareren Fehler zu finden.

Ich bin nicht vertraut mit LuaWrapper, um spezifischere Fragen zu stellen, aber das sind die Schritte, die ich nehmen würde, wenn ich Sie wäre.

2

ich stark das Problem vermuten, dass Sie eine Klasse oder Struktur ähnlich wie diese haben:

struct Foo 
{ 
    Bar bar; 
    // Other fields follow 
} 

Und dass Sie beide Foo und Bar zu Lua über LuaWrapper ausgesetzt haben. Das wichtige Bit hier ist, dass bar das erste Feld auf Ihrem Foo Struct ist. Alternativ können Sie eine Klasse haben, die von einer anderen Basisklasse erbt und sowohl die abgeleitete Klasse als auch die Basisklasse sind LuaWrapper ausgesetzt.

LuaWrapper verwendet eine Funktion namens Identifier, um jedes Objekt eindeutig zu verfolgen (z. B. ob das angegebene Objekt bereits zum Lua-Status hinzugefügt wurde oder nicht). Standardmäßig verwendet es die Objektadresse als Schlüssel. In Fällen wie dem oben genannten ist es möglich, dass sowohl Foo als auch Bar dieselbe Adresse im Speicher haben, und somit kann LuaWrapper verwirrt werden.

Dies kann dazu führen, dass beim Versuch, eine Methode nachzuschlagen, das Metatable des falschen Objekts erfasst wird. Da es das falsche Metatable ansieht, wird es natürlich nicht die gewünschte Methode finden, und so scheint es, als ob Ihr Metatabel auf mysteriöse Weise Einträge verloren hat.

Ich habe eine Änderung eingecheckt, die die Daten jedes Objekts pro Typ und nicht in einem riesigen Stapel verfolgt. Wenn Sie Ihre Kopie LuaWrapper auf die neueste aus dem Repository aktualisieren, bin ich ziemlich sicher, dass Ihr Problem behoben wird.

+1

Ich denke, es ist möglich, dass Ihre Diagnose richtig ist, aber aus dem falschen Grund. Es ist möglich, dass die Klassen verwirrt werden, aber nicht wegen der Vererbung, sondern weil wir Objekte schnell zyklisch durchlaufen und C++ eine Adresse für einen anderen Objekttyp neu zuweisen kann, bevor die Müllsammlung von Lua die Möglichkeit hatte, Dinge zu bereinigen. Subclassing ist hier nicht das Problem. Das heißt, Ihre letzten Änderungen an LuaWrapper können das Problem beheben. – Watusimoto

+0

Wow, danke, dass du das gefunden hast! Wir werden die Änderungen übernehmen und Sie wissen lassen. – raptor

+0

hm, ich hatte nicht den Fall der Wiederverwendung von Adressen schneller als der Lua GC gc-ing sie berücksichtigt. Vielleicht ist es notwendig, dass LuaWrapper eine Art von luaW_forget einleitet, um zu vergessen, dass es ein bestimmtes Objekt verfolgt. Eine alternative Lösung könnte sein, eine eigene Bezeichnerfunktion zu schreiben, die einen eindeutigen Wert für jedes Objekt garantiert (wenn Sie eine Möglichkeit haben, Objekte eindeutig zu identifizieren, könnten Sie diese nutzen). – Alex