So implementieren Sie ein Adapter-Framework in C ++, das sowohl unter Linux als auch unter Windows funktioniert

9

Hier ist, was ich versuche zu tun:

Ich entwickle eine plattformübergreifende IDE (Linux und Windows), die Plugins unterstützt. Ich muss die Erweiterbarkeit mithilfe eines Adapterframeworks unterstützen, das dem von Eclipse ähnelt. Siehe hier für weitere Details, aber im Grunde brauche ich Folgendes:

Lassen Sie Adaptee und Adapted völlig unabhängige Klassen sein, die bereits existieren und die wir in keiner Weise ändern dürfen. Ich möchte eine AdapterManager -Klasse erstellen, die eine Methode

hat %Vor%

erstellt eine Instanz von Adapted mit einer Instanz von Adaptee . Wie genau die Instanz erstellt wird, hängt von einer Adapterfunktion ab, die mit AdapterManager registriert werden muss. Jedes neue Plug-in sollte in der Lage sein, Adapterfunktionen für beliebige Typen bereitzustellen.

Hier sind meine Gedanken über eine mögliche Lösung und warum es nicht funktioniert:

  • Die RTTI-Funktionen von C ++ 11 und die Klasse type_info stellen eine Methode hash_code() bereit, die für jeden Typ im Programm eine eindeutige Ganzzahl zurückgibt. Siehe hier . Daher könnte AdapterManager einfach eine Map enthalten, die bei gegebenen Hash-Codes für die Adaptee und Adapter-Klassen einen Funktionszeiger auf die Adapterfunktion zurückgibt. Dies macht die Implementierung der Funktion adapt() über trivial:

    %Vor%

    Jedes Plug-in kann das Framework einfach erweitern, indem es einfach eine zusätzliche Funktion in die Map einfügt. Beachten Sie auch, dass jedes Plug-in versuchen kann, eine beliebige Klasse an eine andere Klasse anzupassen und erfolgreich zu sein, wenn eine entsprechende Adapterfunktion mit AdapterManager registriert ist, unabhängig davon, wer sie registriert hat.

  • Ein Problem damit ist die Kombination von Templates und Plugins (shared objects / DLLs). Da zwei Plug-Ins eine Template-Klasse mit den gleichen Parametern instanziieren können, könnte dies möglicherweise zu zwei separaten Instanzen der entsprechenden type_info -Strukturen und möglicherweise anderen hash_code() -Ergebnissen führen, wodurch der obige Mechanismus durchbrochen wird. Adapterfunktionen, die von einem Plug-In registriert werden, funktionieren möglicherweise nicht immer in einem anderen Plug-In.
  • Unter Linux scheint der dynamische Linker in der Lage zu sein, unter bestimmten Bedingungen mit mehreren Deklarationen von Typen in verschiedenen gemeinsam genutzten Bibliotheken umzugehen, wie in dies (Punkt 4.2). Das eigentliche Problem ist jedoch in Windows, wo es scheint, dass jede DLL ihre eigene Version einer Template-Instanziierung erhält, unabhängig davon, ob sie auch in anderen geladenen DLLs oder der Haupt-Executable definiert ist. Der dynamische Linker scheint im Vergleich zu dem in Linux verwendeten sehr unflexibel zu sein.
  • Ich habe erwogen, explizite Template-Instanziierungen zu verwenden, die das Problem zu reduzieren scheinen, aber es immer noch nicht lösen, da zwei verschiedene Plug-Ins die gleiche Vorlage auf die gleiche Weise instanziieren können.

Fragen:

  1. Kennt jemand eine Möglichkeit, dies in Windows zu erreichen? Wenn Sie vorhandene Klassen ändern könnten, würde dies helfen?
  2. Kennen Sie einen anderen Ansatz, um diese Funktionalität in C ++ zu erreichen, während alle gewünschten Eigenschaften erhalten bleiben: keine Änderung an bestehenden Klassen, keine Templates, keine Plug-Ins und plattformübergreifend?

Update 1:
Dieses Projekt verwendet das Qt-Framework für viele Dinge einschließlich der Plug-in-Infrastruktur. Qt hilft wirklich bei der plattformübergreifenden Entwicklung. Wenn Sie eine Qt-spezifische Lösung für das Problem kennen, ist das auch willkommen.

Update 2:
Der Kommentar von n.m. hat mir gezeigt, dass ich das Problem nur theoretisch kenne und nicht wirklich getestet habe. Also habe ich einige Tests in Windows und Linux mit der folgenden Definition durchgeführt:

%Vor%

Diese Klasse wird in zwei verschiedenen gemeinsam genutzten Bibliotheken / DLLs mit T = int instanziiert. Beide Bibliotheken werden zur Laufzeit explizit geladen. Hier ist, was ich gefunden habe:

In Linux funktioniert alles:

  • Die beiden Instanzen verwendeten dieselbe vtable .
  • Das von typeid zurückgegebene Objekt befand sich an der gleichen Adresse.
  • Sogar das statische Datenelement war das gleiche.
  • Die Tatsache, dass das Template in mehreren dynamisch geladenen Shared Libraries instanziiert wurde, machte also keinen Unterschied. Der Linker scheint einfach die erste geladene Instanz zu verwenden und ignoriert den Rest.

In Windows sind die beiden Instanzen 'etwas' verschieden:

  • Die typeid für die verschiedenen Instanzen gibt type_info Objekte an verschiedenen Adressen zurück. Diese Objekte sind jedoch gleich, wenn sie mit == getestet werden. Die entsprechenden Hash-Codes sind ebenfalls gleich. Es scheint, als ob unter Windows die Gleichheit zwischen den Typen unter Verwendung des Namens des Typs hergestellt wird - was sinnvoll ist. So weit, so gut.
  • Die VTables für die beiden Instanzen waren jedoch unterschiedlich. Ich bin mir nicht sicher, wie groß das Problem ist.In meinen Tests konnte ich dynamic_cast verwenden, um eine Instanz von TypeIdTest auf einen abgeleiteten Typ über die Grenzen einer gemeinsam genutzten Bibliothek hinweg zu reduzieren.
  • Ein Problem ist auch, dass jede Instanz eine eigene Kopie des statischen Feldes data verwendet hat. Das kann viele Probleme verursachen und statische Felder in Vorlagenklassen grundsätzlich nicht zulassen.

Insgesamt scheint es, dass selbst in Windows die Dinge nicht so schlecht sind, wie ich dachte, aber ich zögere immer noch, diesen Ansatz zu verwenden, da Template-Instanziierungen immer noch verschiedene vtables und statischen Speicher verwenden. Weiß jemand, wie man dieses Problem vermeidet? Ich habe keine Lösung gefunden.

    
Dimitar Asenov 14.02.2012, 10:10
quelle

1 Antwort

1

Ich denke Boost Extension beschäftigt sich genau mit dieser Problemdomäne:

  • Ссылка

      

    ( als Vorbereitung für die Einreichung dieser Bibliothek bei Boost zur Überprüfung )

Besonders interessiert Sie, was der Autor in diesem Blogpost geschrieben hat: "Ressourcenverwaltung über DLL-Grenzen hinweg :

  

RTTI funktioniert nicht immer über DLL-Grenzen hinweg wie erwartet. Schauen Sie sich die type_info-Klassen an, um zu sehen, wie ich damit umgehen kann.

Ich bin mir nicht sicher, ob seine Lösung wirklich robust ist, aber er hat diesen Gedanken schon einmal gemacht. In der Tat gibt es einige Beispiele, die Boost Extensions verwenden, die Sie ausprobieren können, vielleicht möchten Sie es verwenden.

    
sehe 15.02.2012 09:03
quelle