Offset von Daten-Member in Klasse
-
Hallo!
Ich möchte herausfinden, wie groß der Offset eines
(erst zur laufzeit bekannten) Daten-Members vom Beginn
des Objekts aus ist - ich habe jedoch nur eine
FieldInfo-Struktur des Members (bzw. den System::Type
der Klasse), kann also nicht direkt über&Obj->member
auf die Adresse zugreifen.
Fällt jemandem ein Workaround dafür ein?Alex
-
Warum? Es macht keinen Sinn einen Offset zu erfragen, wenn er immer anders sein kann...
Offsets machen nur Sinn bei Strukturen, wenn Du explizit das[StructLayout(LayoutKind::Explicit)]
angegeben hast
-
Du schreibst, dass der Offset immer anders sein kann.
Ändert er sich während der Laufzeit eines Programmes?
Wie würde denn dann garantiert, dass der Zugriff auf ein
Daten-Member die richtige Position erwischt?Ich würde jetzt LayoutKind.Automatic eher so interpretieren,
dass der JIT beim compilieren die Reihenfolge optimiert.
(Also offensichtlich z.B. der Größe nach sortiert zuerst
8-Byte-Typen, dann 4-Byte-Typen, dann 2-Byte-Typen usw.,
um den Zugriff auf die Felder zu optimieren.Alex
-
Und WARUM willst Du das wissen? WAS kannst Du mit dieser Info anfangen?
-
Tut mir leid, dass ich etwas spät antworte.
Ich benötige ein C++/CLI-Äquivalent für einen
Member-Pointer in C++.
Das lässt sich durch einen interior_ptr und
etwas Pointer-Arithmetik erreichen.Allerdings muss ich wissen, wo sich das
entsprechende Feld in der Klasse befindet,
benötige also den Offset von der Adresse des
Objekts aus.Alex
-
ich hab jetzt einen "hässlichen" Workaround.
Ich verwende eine DynamicMethod. Mit ein wenig
IL-Code lässt sich der Offset berechnen:ilg->Emit(OpCodes::Ldarg_0); ilg->Emit(OpCodes::Ldflda, Field); ilg->Emit(OpCodes::Conv_I); ilg->Emit(OpCodes::Ldarg_0); ilg->Emit(OpCodes::Conv_I); ilg->Emit(OpCodes::Sub); ilg->Emit(OpCodes::Ret);
Diesen kann ich dann als Delegate mit int-Rückgabetyp und einem
beliebigen(!) Objekt (kein nullptr) ausführen.Alex
-
Wenn du schon zur Laufzeit Code erstellst (mit all den Unannehmlichkeiten die das unter .NET bringt) ... wieso dann nicht gleich ausgehend von C# Source?
Der Compiler ist doch schliesslich Teil des Frameworks wenn ich mich nicht irre, und wenn du es sowieso an einen delegate bindest, sollte doch egal sein wo der Code herkommt. Dann müsstest du nicht umständlich irgendwo den Offset rauskletzeln.Und weil du schreibst "mit einem beliebigen Objekt": wenn du da *irgendein* Objekt an den delegate übergibst, dann bekommst du auch *irgendwas* als Ergebnis. Oder wie willst du sonst sicherstellen dass ein bestimmtes Member bei zwei unverwandten Objekten am selben Offset zu finden ist? (Und wenn sie verwandt sind, dann sind es ja nichtmehr *beliebige* Objekte
)
-
Hallo hustbaer!
Von welchen Unannehmlichkeiten redest du?
Es gibt leider weder in Visual C++ noch in C#
"inline MSIL" - ich benötige jedoch den IL-OpCode
"Ldflda", um an die Adresse des Members zu kommen.Den Delegate nutze ich nicht, um den Wert des Members
auszulesen, sondern um die Adresse (die irgendwo
versteckt in dem FieldInfo ist) zu erhalten. Der
Zugriff erfolgt direkt über einen interior_ptr.Und weil du schreibst "mit einem beliebigen Objekt": wenn du da *irgendein* Objekt an den delegate übergibst, dann bekommst du auch *irgendwas* als Ergebnis. Oder wie willst du sonst sicherstellen dass ein bestimmtes Member bei zwei unverwandten Objekten am selben Offset zu finden ist? (Und wenn sie verwandt sind, dann sind es ja nichtmehr *beliebige* Objekte )
Um genau zu sein: ich übergebe "frech" das
DynamicMethod-Objekt. Der Objekttyp interessiert
im IL-Code überhaupt nicht (außer wenn der JIT
Typesafety überprüft - das muss ich eben verhindern)
- nur die FieldInfo (und die gehört zu dem speziellen
Typ).Alex
-
LionAM schrieb:
Hallo hustbaer!
Von welchen Unannehmlichkeiten redest du?
AFAIK muss man, um dynamisch Code zu erzeugen, eine Assembly erzeugen. Und diese wird man nichtmehr los, bis die AppDomain entladen wird. Was bei normalen Programmen heisst: bis man das Programm beendet. D.h. man kann es hübsch vergessen, wenn man viel dynamischen Code erzeugen möchte, da irgendwann der Speicher voll ist. Viele Dinge kann man dabei natürlich über Caching erschlagen - wie z.B. in deinem Fall: es gibt ja nur endlich viele Offsets, und wenn du schon Code für Offset X generiert hast, dann brauchst du den nicht nochmal zu generieren. Bzw. wenn es um Member geht: die in einem Programm verwendeten Objekte haben auch nur endlich viele Member, und wenn du den Offset für ein bestimmtes Member einer bestimmten Klasse schon ermittelt hast, musst du es ja nicht nochmal machen. Dadurch limitiert man die Anzahl der dynamisch erzeugten Assemblies, und es sollte kein Problem geben.
Den Delegate nutze ich nicht, um den Wert des Members
auszulesen, sondern um die Adresse (die irgendwo
versteckt in dem FieldInfo ist) zu erhalten. Der
Zugriff erfolgt direkt über einen interior_ptr.Und weil du schreibst "mit einem beliebigen Objekt": wenn du da *irgendein* Objekt an den delegate übergibst, dann bekommst du auch *irgendwas* als Ergebnis. Oder wie willst du sonst sicherstellen dass ein bestimmtes Member bei zwei unverwandten Objekten am selben Offset zu finden ist? (Und wenn sie verwandt sind, dann sind es ja nichtmehr *beliebige* Objekte )
Um genau zu sein: ich übergebe "frech" das
DynamicMethod-Objekt. Der Objekttyp interessiert
im IL-Code überhaupt nicht (außer wenn der JIT
Typesafety überprüft - das muss ich eben verhindern)
- nur die FieldInfo (und die gehört zu dem speziellen
Typ).Alex
Hm. Verstehe ich jetzt nicht so recht.
Auch wenn der Objekt-Type im IL Code nicht interessiert, musst du doch sicherstellen, dass du ein "passendes" Objekt übergibst. Wenn du ein falsches übergibst, erhältst du einen interior_ptr der auf irgendwas zeigt, bloss nicht auf das, was du ansprechen wolltest.Kannst du ein Beispiel geben wie die Anwendung von sowas aussehen sollte? Ich kann mir grad überhaupt keinen Reim mehr machen was du da machen willst... und wozu.
-
Hallo nochmal!
DynamicMethod ist "Lightweight Code Generation".
Es wird keine neue Assembly erzeugt, und wenn die
Funktion nicht mehr benötigt wird (der delegate
nicht mehr existiert), dann kümmert sich die Garbage
Collection drum.Nutzen kann ich es folgendermaßen:
double get(MyObject^ Obj) { interior_ptr<char> ptr = (interior_ptr<char>)Obj; return *(interior_ptr<double>)(ptr+offset); }
wobei offset der mit dem Delegate berechnete Wert ist.
Alex