Binär-Kompatibilität von C++ Klassen-Objekten



  • Hi!

    Ich mache mir gerade Gedanken zur binär-kompatibilität von C++ objekten. Und zwar nicht im Falle der Veränderung der zugrundeliegenden Klasse, sondern im Falle der Compilierung einer der selben Klasse mit verschiedenen Compilern. Ich möchte also eine Klasse mit verschiedensten, standardkonformen Compilern übersetzen und es soll nach dem Linken möglich sein, daß Funktionen der verschiedenen Module Objekte des Klassentyps allozieren, austauschen und bearbeiten können, ohne daß es dabei zu Fehlfunktionen kommt.

    Mir ist inzwischen klar, daß virtuelle Vererbung und virtuelle Methoden im Prinzip die Binärkompatibilität durchbricht, da beides nicht standardisiert ist. Zumindest sagen das alle Quellen die ich finden konnte. Das Selbe scheint für statische Member zu gelten.

    Was ich jedoch nicht finden konnte: Wenn ich eine einfach Klasse schreibe, ohne virtuelle oder statische Anteile, kann ich dann Binärkompatibilität erreichen? Was ist mit dem sogenannten "Padding"?

    Ist sowas binärkompatibel?:

    class foo
    {
    public:
       foo(int _x) : x(_x) { y = new char[20]; }
       ~x{ delete [] y; y = NULL; }
    
       inline char* Get() const { return y; }
       inline void Set(char* newy) { /* irgendwas */ }
    
       void Bar() { /* blah */ }
    
    private:
       int x;
       char* y;
    
       double anderermember;
    };
    

    Wenn sowas nicht binärkomptibel ist, warum? Und wie kann ich das ändern? Wenn das nicht möglich ist, läßt sich zumindest absichern, daß sich die Klasse nicht mit einem inkompatiblen Compiler übersetzen läßt?

    Es gibt natürlich noch weitere Probleme. Nicht nur bezüglich der Binärkompatibilität der auszutauschenden Objekte, sondern auch bezüglich der Calling-Conventions und des Namemangling. Ich denke aber, daß sich diese Probleme mit 'extern "C"' lösen lassen. Vermutlich könnten die verschiedenen Module nur auf Basis herkömmlicher Funktionen kommunizieren (denn eine Klasse und ihre Methoden lassen sich IMHO nicht ohne C++ namemangling kompileren), aber das würde reichen. Wichtig wäre mir, daß diese Funktionen wie oben beschrieben untereinander Objekte austauschen können.

    Danke für eure Meinungen!



  • Vorweg: ich geb keine Garantie, dass ich nicht nur Bockmist verbreite, aber soweit mein Wissen reicht, wuerd ich folgendes sagen:

    Jordy schrieb:

    Es gibt natürlich noch weitere Probleme. Nicht nur bezüglich der Binärkompatibilität der auszutauschenden Objekte, sondern auch bezüglich der Calling-Conventions und des Namemangling. Ich denke aber, daß sich diese Probleme mit 'extern "C"' lösen lassen. Vermutlich könnten die verschiedenen Module nur auf Basis herkömmlicher Funktionen kommunizieren (denn eine Klasse und ihre Methoden lassen sich IMHO nicht ohne C++ namemangling kompileren), aber das würde reichen. Wichtig wäre mir, daß diese Funktionen wie oben beschrieben untereinander Objekte austauschen können.

    Du hast den Nagel auf den Kopf getroffen. Namemangling etc. verhindert, dass du die Objekt-Dateien von Compilern untereinander austauschen kannst. Es gibt naemlich keinen einheitlichen Standard dafuer, wie das Mangling zu machen ist, also machts jeder anders. "extern C" funktioniert nur fuer C-Programme, d.h. du kannst keine Klassendefinition "extern C" machen. Das wuerd ja heissen "stell allen C-Programmen diese Klasse und ihre Methoden zur Verfuegung" ---> geht nicht, C kennt ja keine Klassen.
    Was du machen koenntest waere, eine DLL zu schreiben, die intern zwar deine Klasse Foo verwendet, nach aussen hin aber nur reine C-Funktionen anbietet.

    d.h. fuer ein foo.doSomething(int x) muesstest du einen Wrapper "doSomething(int x)" oder aehnliches schreiben. Wenn du mit mehreren Klasseninstanzen von Foo arbeiten willst, muesstest du sowas wie "doSomething(int x, struct bar)" schreiben, wobei "bar" ein Handle auf eine Foo-Instanz waere. (z. B. kann bar einfach ein integer sein, und "intern" verwaltest du ein Array von Foo-Objekten, und dass bar sagt dir, auf welches Foo du dich jetzt beziehst).



  • d.h. fuer ein foo.doSomething(int x) muesstest du einen Wrapper "doSomething(int x)" oder aehnliches schreiben. Wenn du mit mehreren Klasseninstanzen von Foo arbeiten willst, muesstest du sowas wie "doSomething(int x, struct bar)" schreiben, wobei "bar" ein Handle auf eine Foo-Instanz waere. (z. B. kann bar einfach ein integer sein, und "intern" verwaltest du ein Array von Foo-Objekten, und dass bar sagt dir, auf welches Foo du dich jetzt beziehst).

    Ja, genau. Das Problem ist: Ich möchte, daß diese (anhand ihres Handles) ausgetauschten Objekte von beiden Seiten modifzierbar sind. Dazu füge ich den Sourcecode der "bar" class/struct zum Sourcecode sowohl der Anwendung als auch der DLL hinzu. Die Frage die sich nun stellt: Wird dieser Sourcecode von beiden Sieten auf eine Weise kompliert, so daß die resultierenden objecte zueinander binärkompatibel sind, oder nicht?!


Anmelden zum Antworten