Speicherlayout von Kindklassen


  • Mod

    Wenn ich eine Klasse mit zwei Kindklassen habe die der Basisklasse keine Datenmember hinzufügen:

    class Base{
     int element1;
     double element2;
    };
    
    class Foo: public Base{};
    class Bar: public Base{};
    

    Kann ich dann garantiert davon ausgehen, dass Foo, Bar und Base das gleiche Speicherlayout haben? Mit gleichem Speicherlayout meine ich, dass (&element2 - &element1) bei allen dreien das selbe ergibt.

    Zweck der ganzen Geschichte: Foo und Bar fügen Base ein paar statische Methoden hinzu, daher dieses Design. Nun will ich Objekte dieser Klassen mittels MPI senden/empfangen und dafür muss man das Speicherlayout kennen. Es würde Arbeit sparen, wenn das Programm das Layout nur einmal an Base ausmisst und es dann für die abgeleiteten Klassen verwenden kann.



  • SeppJ schrieb:

    class Base{
     int element1;
     double element2;
    };
    
    class Foo: public Base{};
    class Bar: public Base{};
    

    Kann ich dann garantiert davon ausgehen, dass Foo, Bar und Base das gleiche Speicherlayout haben?

    Nein.

    SeppJ schrieb:

    Mit gleichem Speicherlayout meine ich, dass (&element2 - &element1) bei allen dreien das selbe ergibt.

    Kannst Du die Bedingung anders formulieren? &element2-&element1 ist Quatsch -- selbst wenn element1 und element2 vom gleichen Typen wären.

    SeppJ schrieb:

    Zweck der ganzen Geschichte: Foo und Bar fügen Base ein paar statische Methoden hinzu, daher dieses Design.

    Was bringt das? Gibt's in dem "echten" Base geschützte Elemente (protected) auf denen Du so zugreifen kannst?

    SeppJ schrieb:

    Nun will ich Objekte dieser Klassen mittels MPI senden/empfangen und dafür muss man das Speicherlayout kennen.

    Jein. Geht das noch konkreter? Also Base ist ein POD. Solange es keine Zeiger enthält und alle Rechner die gleiche OS/Compiler-Kombination mit denselben Optionen benutzen, kannst Du ein Foo* in ein Base* und dann in ein void* verwandeln und anschließend die ersten sizeof(Base) Bytes auf den Weg schicken. Die Frage ist, was soll dann auf der Empfängerseite passieren?

    Gruß,
    SP


  • Mod

    Sebastian Pizer schrieb:

    SeppJ schrieb:

    Kann ich dann garantiert davon ausgehen, dass Foo, Bar und Base das gleiche Speicherlayout haben?

    Nein.

    Ok, das ist schonmal ungünstig, dann muss ich wohl sowieso das Layout für jede Klasse neu ausrechnen.

    SeppJ schrieb:

    Mit gleichem Speicherlayout meine ich, dass (&element2 - &element1) bei allen dreien das selbe ergibt.

    Kannst Du die Bedingung anders formulieren? &element2-&element1 ist Quatsch -- selbst wenn element1 und element2 vom gleichen Typen wären.

    Etwas ungünstig formuliert, ich wollte nicht zuviel MPI reinbringen, aber das ist wohl doch nötig: MPI hat eine Funktion MPI_GetAddress, die eine Adresse in einem bestimmten MPI internen Adressformat liefert (vermutlich einfach ein Typedef auf int). Damit kann man bei zusammengesetzten Typen berechnen, welchen Abstand die Datenelemente im Speicher haben. Damit kann man dann neue, versendbare Datentypen konstruieren, die intern diese Abstände verwenden.

    SeppJ schrieb:

    Zweck der ganzen Geschichte: Foo und Bar fügen Base ein paar statische Methoden hinzu, daher dieses Design.

    Was bringt das? Gibt's in dem "echten" Base geschützte Elemente (protected) auf denen Du so zugreifen kannst?

    Genauso ist es.

    SeppJ schrieb:

    Nun will ich Objekte dieser Klassen mittels MPI senden/empfangen und dafür muss man das Speicherlayout kennen.

    Jein. Geht das noch konkreter? Also Base ist ein POD. Solange es keine Zeiger enthält und alle Rechner die gleiche OS/Compiler-Kombination mit denselben Optionen benutzen, kannst Du ein Foo* in ein Base* und dann in ein void* verwandeln und anschließend die ersten sizeof(Base) Bytes auf den Weg schicken. Die Frage ist, was soll dann auf der Empfängerseite passieren?

    Also es ist naturgemäß bei allen Prozessen der gleiche Binärcode, und exakt gleiche Systeme. Und es wird auch nur POD versendet, MPI und Deepcopy passt glaube ich nicht so gut zusammen, zumindest nicht ohne Tricksereien.

    Das was du da beschreibst, bedeutet aber doch, dass Foo und Base für die Datenelemente das gleiche Layout haben. Ich vermute mal, dass MPI intern ziemlich ähnlich arbeitet, man übergibt einen Zeiger auf das zu versendende Objekt, dieses wird intern umgecastet und dann versendet.



  • Bei sowas serialisiert man besser. Gib der Basisklasse ein serialize() und unserialize() und gut ist.
    Ist kein nennenswerter Mehraufwand und Probleme sind ausgeschlossen, da man sich nicht ins Reich der undefined behaviour begibt.



  • SeppJ schrieb:

    Sebastian Pizer schrieb:

    Also Base ist ein POD. Solange es keine Zeiger enthält und alle Rechner die gleiche OS/Compiler-Kombination mit denselben Optionen benutzen, kannst Du ein Foo* in ein Base* und dann in ein void* verwandeln und anschließend die ersten sizeof(Base) Bytes auf den Weg schicken. Die Frage ist, was soll dann auf der Empfängerseite passieren?

    [...]
    Das was du da beschreibst, bedeutet aber doch, dass Foo und Base für die Datenelemente das gleiche Layout haben.

    Das bedeutet, dass in dem Foo-Objekt ein Base-Objekt drinsteht. Das bedeutet aber nicht, dass sizeof(Foo)==sizeof(Base) garantiert wird, oder dass Foo*->Base*->void* dieselbe Adresse liefert wie Foo*->void*. Die relative Anordnung der Bits und Bytes des Base-Objekts ist immer die gleiche, da POD. Bei nicht-PODs gibt es diese Garantie nicht mehr.

    Gruß,
    SP


Anmelden zum Antworten