Zwei Objekte in eine Allokation
-
Hallo Leute!
Und zwar möchte ich in einer userdata-Allokation, die Lua für mich tätigt, zwei Objekte unterbringen. Ein "statisches" am Anfang (immer gleich), und ein polymorphes Objekt dahinter. Nun muss man ja padding und alles mögliches beachten, und da frage ich mich, wie man das am besten umsetzt.
Ist es legal, folgendes zu tun:template<typename Polymorph> struct pair_object { festes_objekt anfang; Polymorph ende; };Und dann sizeof(pair_object) an Speicher zu verlangen und den void*-Pointer, den ich bekomme als "festes_objekt*" zu behandeln und ein placement new von Polymorph auf "&static_cast<pair_object*>(ptr).ende" zu tätigen?
Oder wie bekommt man das am elegantesten hin?Viele Grüße,
Deci
-
Wieso nicht std::pair<festes_objekt, Polymorph>?
Noch viel besser: class Polymorph : public festes_objekt;
Wenn du von der C++-Polymorphie abweichst brauchst du einen guten Grund, wahrscheinlich weil Lua die Polymorphie in einer bestimmten Form erwartet. Dafür müsstest du aber das Interface mal hinschreiben/verlinken. Ziemlich wahrscheinlich verwendet Lua ein anderes Speicherlayout als C++, das müsste man dann von Hand nachbauen, aber dafür steht sicher was in der Lua-Doku.
-
Nun, ich baue gerade luabind um, und die Jungs haben damals entschieden, dass sie die Repräsentation von luabind-Objekten durch eine handle-Klasse "object_rep" machen wollen, die einen Zeiger auf einen Instance-Holder hält (der dann das echte Objekt oder einen Zeiger oder was auch immer enthält). Der Instance-Holder ist das polymorphe Objekt, das ich ansprach.
Aufgrund anderer Entscheidungen, die sie getätigt haben, wäre es sogar sinnvoll, wenn man zur Laufzeit den Instance-Holder austauschen kann - weil sie es unterstützen, die Lebenszeitverwaltung zwischen lua und c++ hin- und herzuverschieben. In C++11 geschieht das natürlich explizit in Form von Smart-Pointern, aber ich möchte jetzt nicht den Support dafür komplett ausbauen. In dem Fall kann man aber sicher sein, dass der neue Instance-Holder echt kleiner ist als der alte, sodass der Speicherplatz dafür ausreicht. Ist dann ein kleiner Hack um der alten Zeiten willen.
Auf jeden Fall werden im Moment 2 Speicherblöcke für ein Objekt beschafft, obwohl nur einer nötig wäre, weil alle Typen während der Erstellung bekannt sind. Und das würde ich gerne beheben.
-
decimad schrieb:
Auf jeden Fall werden im Moment 2 Speicherblöcke für ein Objekt beschafft, obwohl nur einer nötig wäre, weil alle Typen während der Erstellung bekannt sind. Und das würde ich gerne beheben.
Dafür würde dein pair_objekt reichen, oder halt gleich std::pair nehmen. Speicherlayout und Bit-Repräsentation müssen aber in C++ und Lua übereinstimmen. Wenn die Lua-Implementation Teil deines Programms ist (also mit gleichen Compileroptionen übersetzt), dann sollte das funktionieren.
decimad schrieb:
Und dann sizeof(pair_object) an Speicher zu verlangen und den void*-Pointer, den ich bekomme als "festes_objekt*" zu behandeln und ein placement new von Polymorph auf "&static_cast<pair_object*>(ptr).ende" zu tätigen?
Oder wie bekommt man das am elegantesten hin?Was spricht gegen new pair_object?
-
Wie meinst Du das mit dem Speicherlayout und der Bit-Repräsentation eigentlich? Lua weiß ja gar nicht was da in dem Speicher steht, es weiß nur, dass da ein Userdata-Objekt ist und überlässt mir, was ich mit dem Speicher mache... missverstehe ich da jetzt etwas?
-
Mit Speicherlayout meine ich sowas:
C++:
Basisobjekt;
Padding;
Abgeleitetes_objekt;Lua:
Abgeleitetes_objekt;
Basisobjekt;
Die Speicherstellen repräsentieren einfach andere Daten. Wenn du nur mit void-Pointern hantierst und manuell Adressen berechnest sollte das aber klappen.Mit Bitrepräsentation meine ich wie man Daten als Bits darstellt. Little Endian, Bit Endian, Einerkomplement, Zweierkomplement, Vorzeichenbit, Anzahl der Bits für die Länge bei Zahlen und allerlei Kodierungen bei Strings.
"Hallo" sieht möglicherweise wegen ASCII/UTF8/16/32 in C++ anders aus als in Lua und wenn du Pech hast sogar 42.
-
Okay, ja, was Speicherlayout und Bitrepräsentation bedeuten, weiß ich so diffus. Mir ging es darum, was es Lua kümmert? Lua gibt mir nur einen Speicherbereich wie es malloc tun würde, zu keinem Zeitpunkt greift es auf diesen Speicherbereich irgendwie zu oder erwartet irgendeine Datenrepräsentation darin. Es vermerkt nur: da ist Userdata, der User wird schon wissen, was er damit anfängt.
Und die Objekte in dem Userdata-Speicherbereich werden ja immer von meinem Programm, oder dem Programm, das luabind benutzt, konstruiert (geschieht ja alles aus Templates heraus). Von daher passt es auch für Client-Code.
Ich mach das jetzt mit dem std::pair wie Du vorschlägst, aber benutze das nur für die Offsets und die Größe, weil ich etwas Flexibilität bezüglich der Konstruktionszeitpunkte benötige.
In so einer verworrenen Bibliothek herumzupfuschen ist echt immer ein Drahtseilakt
-
Ich ging davon aus, dass sich C++ und Lua Daten teilen sollen, insbesondere dass polymorphe Lua-Objekte in C++ erstellt, mit Lua verarbeitet und in C++ wieder gelesen werden sollten.
Wenn Lua nur irgendwo einen void * rumliegen hat, aber nie etwas damit tut, dann gibt es natürlich keine Probleme beim Interpretieren der Daten. Obwohl ich mich dann frage wozu Lua überhaupt diese Daten bekommt.
-
Achso, alles klar!
Das ist deshalb, weil Lua die Lebenszeit dieses Objektes bestimmt. Wenn luas garbage collector merkt, dass keine Referenz mehr auf das Userdata-Objekt gehalten wird, macht es das Userdata-Objekt platt, nachdem es vorher noch gecheckt hat, ob man per Meta-Table eine Routine registriert hat, die aufgerufen werden soll, bevor der Speicher freigegeben wird. Dort registriert man dann natürlich eine Funktion, die den Destruktor aufruft.
-
nwp3 schrieb:
Obwohl ich mich dann frage wozu Lua überhaupt diese Daten bekommt.
Nunja, Lua räumt Userdata mit dem GC weg was schon mal super ist. Außerdem ist das mit den Userdatas notwendig um C++-Objekte in Lua zu nutzen. (Irgendwo müssen die ja leben - nämlich auf dem Lua-Heap)