Klassen offset von der Kind Klasse zu den Eltern Klassen ermitteln



  • Ist es möglich die einzelnen Offset der Eltern Klassen zur ermitteln?
    Am Besten wäre compile Zeit, der Compiler müsste es definitiv wissen.

    struct A { int a; };
    struct B { int b; };
    struct C : A, B { int c; };
    

    Hier in dem Beispiel sollte der Offset von Klasse C zu A 0 sein.
    Von Klasse C zu B => sizeof(int)

    Hat jemand ne Idee wie ich an diese Offsets komme, ohne das ich jetzt auf die member zugreife?

    PS: Mit ist klar das dass C++ Object layout nicht standardisiert ist.
    Mir reicht eine Antwort für VS oder gcc.



  • Du kannst es mit offsetof versuchen.
    Alternativ die Pointer voneinander abziehen, sollte der Compiler als Konstante erkennen. Oder das in eine Funktion tun und constexpr davorschreiben.


  • Mod

    Nun, offsetof böte sich an 🙂 . Funktioniert aber nur bei Klassen mit Standardlayout und das aus gutem Grund. Man könnte leicht ein Gegenbeispiel konstruieren, wo bei einer Klasse ohne Standardlayout die Frage nach einem Offset gar keinen Sinn mehr macht und ein offsetof entsprechend total schief geht.
    Allgemein ist offsetof aber ziemliches low-level Gehacke, wo man sogar in C schon schief angeguckt wird, wenn man es benutzt. Daher muss man erst recht in C++ fragen: Wozu willst du das wissen? Bei allem was ein bisschen komplexer wird, fliegt dir das um die Ohren.

    P.S.: Die Voraussetzung von "Standardlayout" ist schon die entspannte Voraussetzung aus C++11. In C++98 ist die Voraussetzung sogar die strengere Forderung nach POD. Das geht dann mit fast nichts mehr, was man in C++ so gewöhnlich macht. Aber das liegt eher da dran, dass die Unterscheidung POD/Non-POD in C++98 ein bisschen doof ist(was durch die Änderung in C++11 korrigiert wurde). Das heißt, ein offsetof wird in Fällen, wo es in C++11 Standardlayout genannt würde, trotzdem funktionieren, selbst wenn es kein C++98-POD ist. Ebenso eigene Nachbauten von offsetof, welches wirklich nicht sehr komplex wäre (übliche Implementierung ist eine Pointerdifferenz). Aber ganz streng genommen, kann man sich nicht drauf verlassen. Bei GCC und MSVC kannst das aber schon so machen.



  • Ich wüsste nicht, wie sich hier offsetof anbietet. Eine Lösung für den GCC:

    constexpr C* c = reinterpret_cast<C*>(0xDeadBeef); // darf nicht null sein                                                                           
      constexpr int offset = reinterpret_cast<char*>(static_cast<A*>(c)) - reinterpret_cast<char*>(c);
      std::cout << offset << '\n';
      constexpr int offset2 = reinterpret_cast<char*>(static_cast<B*>(c)) - reinterpret_cast<char*>(c);
      std::cout << offset2 << '\n';
    

  • Mod

    nwp3 schrieb:

    Oder das in eine Funktion tun und constexpr davorschreiben.

    Zeigerdifferenzen sind niemals konstante Ausdrücke. reinterpret_cast auch nicht.



  • das ganze soll natürlich für alle möglichen Klassen typen funktionieren.
    Also mit polymorphe Klassen (virtual methods), virtual base class etc.

    Und bei offsetof muss man auf die member zugreifen, das wollte ich wie gesagt nicht.

    Und btw. liefert mir in C++ nicht auch &T::member die offset adresse?



  • Nash26 schrieb:

    das ganze soll natürlich für alle möglichen Klassen typen funktionieren.
    Also mit polymorphe Klassen (virtual methods), virtual base class etc.

    Wozu brauchst du das?


  • Mod

    Nash26 schrieb:

    das ganze soll natürlich für alle möglichen Klassen typen funktionieren.
    Also mit polymorphe Klassen (virtual methods), virtual base class etc.

    Dann macht die Frage an sich keinen Sinn mehr. Wenn virtual im Spiel ist, wirst du in der Regel eine konkrete Instanz benötigen, um überhaupt eine Aussage machen zu können, diese Information existiert gar nicht zur Compilezeit. daher kann man auch nicht danach fragen. Es kann daher keine sinnvolle Anwendung für diese Information geben. Daher Umformulierung der Gegenfrage von "Wozu brauchst du das?" zu: Wozu meinst du, das zu brauchen?



  • @SeppJ
    Dann musst du mir erklären warum es nicht zur Compilezeit geht, bzw. warum eine
    konkrete Instanz benötigt wird.
    Etwas anderes wäre, wenn du gesagt hättest ich komme nicht an diese Information ran.

    struct A { int a; };
    struct B { int b; virtual fooBar() {} };
    struct C : A, B { int c; };
    
    C c;
    c.fooBar(); // wird eventuell so hier übersetzt => c.__vptr[0]->fooBar(&c);
    // wo der __vptr sich befindet sollt der compiler wissen.
    

    IMHO weiß der Compiler hier ganz genau zur Compilezeit denn offset.
    Aber natürlich nur wenn er das Kind Objekt hat.
    Klärt mich gerne auf warum das nicht so sein sollte.


  • Mod

    Ich dachte da primär an virtuelle Vererbung. Mit virtuellen Funktionen lässt sich bestimmt auch eine Gegenbeispiel basteln. Aber mit virtueller Vererbung ist es leicht:

    struct A { int a; };
    struct B : public virtual A   { int b; };
    struct C : public virtual A   { int c; };
    struct D : public B, public C { int d; };
    
    int main()
    {
      // Von oben das offsetof Marke Eigenbau
      int offset = reinterpret_cast<char*>(static_cast<A*>(reinterpret_cast<D*>(0xDeadBeef))) - reinterpret_cast<char*>(reinterpret_cast<C*>(0xDeadBeef));
    }
    

    Sollte krachen, denn hier muss im vtable nachgeschlagen werden. Aber wir haben keine Instanz. Oder anstatt vtable, wie auch immer das virtual implementiert ist. Das ist eben auch so ein Problem, dass das alles mögliche sein könnte.



  • ok dann gehe ich einen Schritt zurück.

    D d;
    

    Bezug nehmend auf dein Beispiel:
    Wenn ich dieses D Objekt erzeuge, sollt doch der compiler alle offsets kennen (weil er sie ja selbst erzeugt)?
    Wenn nein, warum nicht?
    Wenn ja, wie komme ich an diese?

    PS: was sollen diese komischen HexDez zahlen?



  • Nash26 schrieb:

    PS: was sollen diese komischen HexDez zahlen?

    Einfach nur ein beliebiger Zeiger. Wenn der Ausdruck zur Compilezeit ausgewertet wird, ist es ja egal, ob der Zeiger irgendwann zur Laufzeit einmal gültig ist oder nicht.


  • Mod

    Nash26 schrieb:

    ok dann gehe ich einen Schritt zurück.

    D d;
    

    Bezug nehmend auf dein Beispiel:
    Wenn ich dieses D Objekt erzeuge, sollt doch der compiler alle offsets kennen (weil er sie ja selbst erzeugt)?

    In diesem einfachen Fall vielleicht, aber nicht allgemein. Was ist der Offset von C zu A? Das ist schon nicht mehr zu beantworten.

    Wenn nein, warum nicht?

    Weil bei der virtuellen Vererbung das genaue Klassenlayout vom dynamischen Typ abhängt. Wenn das C Teil eines D ist, dann ist der Offset C zu A ein anderer als bei einem einfachen C.

    Wenn ja, wie komme ich an diese?

    Wie schon oft gesagt: Die Frage macht gar keinen Sinn, weil du nach etwas fragst, was es gar nicht gibt. Wozu sollte man das brauchen?


Anmelden zum Antworten