Zum Inhalt springen

C++ Diamantproblem


 Teilen

Empfohlene Beiträge

Hi,

hab hier ein Problem und will das gleich mal praktischerweise unter

Verwendung von Qt erläutern. In diesem Beispiel wird das Problem eigentlich durch Qt verursacht, hatte ähnliche Fälle aber bereits auch Qt-unabhängig.

Ich habe eine Basisklasse A. Von A gibt es mehrere Ableitungen, sagen wir B und C. A wird von Klasse Base verwendet. Eine abstrakte Methode "getWidget" von A gibt mir einen Pointer auf QWidget zurück, der von Base weiterverwendet wird. Diese Methode muss also in B und C implementiert werden, diese können intern aber natürlich eine höherwertige Klasse von QWidget, also z.B. ein QLabel oder gar einen QGraphicsView, verwenden.

Base MUSS aber davon unabhängig sein!

Damit ist die Klasse A quasi eine Schnittstelle.

Jetzt bin ich an dem Punkt, an dem ich die Schnittstelle erweitern muss. Solange es sich bei der Erweiterung um einfache Methoden handelt, ist das kein Problem. Ich erstelle mir eine Klasse AWidget, die die Methode implementiert oder abstrakt enthält. Der verwendete Widget erbt danach von AWidget + beliebiger Widget. AWidget wird also als Interface verwendet.

Die Methode "getWidget" von A gibt danach keinen QWidget mehr zurück sondern einen AWidget. Damit steht die erweiterte Funktionalität zur Verfügung. (Alternativ kann weiterhin ein QWidget zurückgegeben werden, der via dynamic cast aber auf AWidget gecastet werden kann).

Jetzt möchte ich AWidget aber mit einem SIGNAL ausstatten. Um in AWidget aber Signals & Solts verwenden zu können, muss ich AWidget zumindest von QObject ableiten. Dabei kriege ich aber das Problem, dass AWidget von QWidget (oder höher) und QObject erbt. QWidget erbt aber auch von QObject und damit habe ich hier ein Diamantproblem.

Ergo: Ich will die Funktionalität einer Basisklasse erweitern, ohne dessen Code direkt ändern zu können (die Qt-Sourcen ändern wäre nicht gut). Ich kann aber auch nicht QWidget ableiten und erweitern und dieses dann immer weiter verwenden, da ich natürlich auch bereits vorhandene, von QWidget abgeleitete Klassen verwenden will.

Ist jemandem eine Lösung dieses Problems bekannt, oder hat vielleicht jemand Tips für mich, wie man das umgehen könnte?

Bin für jede Hilfe dankbar.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich habe eine Basisklasse A. Von A gibt es mehrere Ableitungen, sagen wir B und C. A wird von Klasse Base verwendet. Eine abstrakte Methode "getWidget" von A gibt mir einen Pointer auf QWidget zurück, der von Base weiterverwendet wird.

Eine Klasse ist entweder abstrakt / virtual oder nicht (XOR), damit kann sie nicht in Deiner Klasse Base verwendet werden.

Diese Methode muss also in B und C implementiert werden, diese können

Da A abstrakt ist und somit die Schnittstellen Methoden liefert, ja.

intern aber natürlich eine höherwertige Klasse von QWidget, also z.B. ein QLabel oder gar einen QGraphicsView, verwenden. Base MUSS aber davon unabhängig sein!

A ist eine Schnittstelle für B und C, nicht für Base. Was letztendlich in QWidget steckt ist irrelevant, denn Du arbeitest mit QWidget

Jetzt bin ich an dem Punkt, an dem ich die Schnittstelle erweitern muss.

Deine Klasse A wird um Methoden / Properties erweitert.

Solange es sich bei der Erweiterung um einfache Methoden handelt, ist das kein Problem. Ich erstelle mir eine Klasse AWidget, die die Methode implementiert oder abstrakt enthält.

Wieso "oder", Du arbeitest abstrakt xor nicht-abstrakt.

Der verwendete Widget erbt danach von AWidget + beliebiger Widget. AWidget wird also als Interface verwendet.

Welcher Widget? Du arbeitest in AWidget! Ist AWidget abstrakt xor nicht?

Die Methode "getWidget" von A gibt danach keinen QWidget mehr zurück sondern einen AWidget. Damit steht die erweiterte Funktionalität zur Verfügung. (Alternativ kann weiterhin ein QWidget zurückgegeben werden, der via dynamic cast aber auf AWidget gecastet werden kann).

Ja

Jetzt möchte ich AWidget aber mit einem SIGNAL ausstatten. Um in AWidget aber Signals & Solts verwenden zu können, muss ich AWidget zumindest von QObject ableiten. Dabei kriege ich aber das Problem, dass AWidget von QWidget (oder höher) und QObject erbt. QWidget erbt aber auch von QObject und damit habe ich hier ein Diamantproblem.

Wenn ich nach Deiner Ausführung folgendes habe ich folgendes:

  • Base enthält A, somit ist Base nicht abstrakt
  • A (virtual) generalisiert konkrete Klassen B und C
  • konkrete (nicht virtual) Implementierung von A als AWidget
  • nach Deiner Ausführung würde ich behaupten, dass A von QWidget und damit von QObject erbt, somit B, C und AWidget ebenfalls

Ist jemandem eine Lösung dieses Problems bekannt, oder hat vielleicht jemand Tips für mich, wie man das umgehen könnte?

Im Moment sehe ich - nach meinen Ausführungen - das Diamantene-Problem nicht, den A erbt von QObject und Base enthält A (1:1 Komposition oder Assoziation).

HTH Phil

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wie kann ich also QWidget erweitern, ohne es selbst zu ändern, aber so, dass sich die Änderung trotzdem auf die abgeleiteten Klassen auswirkt.

Dies ist ein völliger Widerspruch, Du willst QWidget ändern ohne es zu ändern !

Wahrscheinlich kann man das gar nicht so machen, deshalb frage ich ja auch nach praktikablen Lösungsansätzen, das Problem zu umgehen.

Änder' Dein Konzept: QWidget ableiten in X => ableiten nach Y

X ist Deine Ableitung von QWidget und Y sind Deine abgeleiteten Klassen

HTH Phil

P.S.: Ich wollte nicht Deine einzelnen Klassen durchsehen, sondern im Grunde geht es mir um das Konzept

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dies ist ein völliger Widerspruch, Du willst QWidget ändern ohne es zu ändern !

Mhhh, ich will es nicht direkt ändern, ich will es "nur" erweitern.

Änder' Dein Konzept: QWidget ableiten in X => ableiten nach Y

X ist Deine Ableitung von QWidget und Y sind Deine abgeleiteten Klassen

Das ist das Problem. Dann kann ich keine Qt-Klassen mehr verwenden. D.h. ich muss mir im Grunde Ableitungen von ALLEN QtGui Klassen machen.

Dabei will ich nur eine Methode dazuhaben, die sich wiederum auch mit QWidget-Level begnügen würde.

Demnach müsste ich in meinen Klassen sämtliche Funktionalitäten aller von QWidget abgeleiteten Klassen neu implementieren, weil an die Originale komm ich ja nicht mehr ran, ohne wieder in das Diamantproblem zu geraten.

P.S.: Ich wollte nicht Deine einzelnen Klassen durchsehen, sondern im Grunde geht es mir um das Konzept

Darum geht es mir auch. :)

Dachte nur, dass der Pseudocode es vielleicht verständlicher macht.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Warum leitest Du nicht QWidget ab in X und verwendest anstatt QWidget in Deinem Programm eben X. In X implementierst Du, was eben das QWidget erweitert.

Von X leitest Du A,B,C... ab, die aber zusätzlich von QLabel (A), QWindow (B),... erben. Beim Binden wird dann entschieden, welche Eigenschaft/Methode verwendet wird. Du musst dann eben auch statt QWindows eben B usw verwenden

Phil

Link zu diesem Kommentar
Auf anderen Seiten teilen

Warum leitest Du nicht QWidget ab in X und verwendest anstatt QWidget in Deinem Programm eben X. In X implementierst Du, was eben das QWidget erweitert.

Von X leitest Du A,B,C... ab, die aber zusätzlich von QLabel (A), QWindow (B),... erben. Beim Binden wird dann entschieden, welche Eigenschaft/Methode verwendet wird. Du musst dann eben auch statt QWindows eben B usw verwenden

Wenn du dir das Codebeispiel ansiehst, siehst du, dass ich A und B nicht von X ableite, sondern von AWidget.

Abgesehen davon tritt bei dem was du jetzt geschrieben hast, das gleiche Problem auf. QWidget -> X -> A, B, C enthält schon QObject (QWidget stammt auch von QObject ab).

Wenn ich jetzt als Y von A und QLabel ableite, habe ich zweimal QObject drin, einmal von A, einmal von QLabel (QObject -> QWidget -> QLabel).

Das führt dazu, dass der Compiler schonmal eine Warnung ausgibt. Spätestens, wenn ich in meiner Ableitung auf irgendwas von QObject zugreife (und das geschieht zwangsläufig schon durch das Meta-Object System von Qt), kriegt man einen Kompilerfehler, weil nicht klar ist, auf welches QObject man zugreifft.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Sorry, ich habe noch mal mein Post gelesen. Im Kopf hatte ich folgende Struktur

QWidget => QLabel, QWindow

Deine Klasse Z (abstract) mit der Methode die Du in alle Widget haben willst.

Dann müsste es doch so sein:

A erbt von QLabel und Z

B erbt von QWindow und Z

Das Prinzip warum Z noch von QWidget erben soll ist mir nämlich nicht klar, denn QLabel erbt ja selbst von QWidget, damit hast Du schon die Spezialisierung von QWidget mit allen Eigenschaften von QWidget

Phil

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ja, so stimmts, nur bei dieser Lösung muss ich jedesmal wenn ich für jede Q*-Klasse die ich verwenden will, erst ne Ableitung machen.

Das will ich eigentlich vermeiden.

Ich will/muss ja dabei nichtmal an dieser Struktur festhalten. Bin allgemein auf einer Suche, dieses Problem irgendwie zu lösen, da ich die gleiche Thematik jetzt schon öfters hatte.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich denke ohne einer Veränderung der Originalklassen kannst Du das nicht so realisieren. Evtl schaust Du Dir mal D an, ich meine da ließe sich das realisieren. Wenn Du aber bei C bleiben möchtest würde ich die Struktur anpassen. Ich versuche in meinen Projekten möglichst klare OOP Strukturen zu schaffen.

Im Grunde ist mir nicht klar, warum Du diese Struktur haben möchtest, es entsteht bei mir der Eindruck einer Frage "Bequemlichkeit vs Struktur" / "definiertes Konzept vs einfacher Code".

Ein definiertes Konzept, vor allem das man ordentlich via OOP umsetzen kann, ist meiner Ansicht auf Dauer besser, wie es einfach gecodet. Evtl wäre noch Ableitung und Tempates mögglich

Phil

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich muss bei C++ bleiben, da wir das in diesem Projekt mit Qt einsetzen. Die Software soll auch auf Mac laufen, deshalb auch Qt.

Ich leg sehr viel Wert auf eine saubere OOP-Struktur, will aber trotzdem einen möglichst einfachen Weg gehen. Deshalb suche ich eben auch nach einer praktikablen Lösung.

Templates sind mir auch schon in den Sinn gekommen. Dabei stoß ich allerdings auf das Problem, dass das ganze im Rahmen einer PlugIn-Struktur realisiert wird und dieses Problem die Schnittstelle betrifft.

Templates müssen aber zur Kompilezeit mit den entsprechenden Typen erzeugt werden. Sollte also in einem PlugIn ein neuer Typ verwendet werden, kennt das Hauptprogramm diese Templatevariante nicht. Ich müsste also das Hauptprogramm ändern, was den PlugIn-Sinn zerstören würde.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ohne jetzt alle Beiträge im Thread genau gelesen zu haben, erstmal ein Versuch einer Zusammenfassung Deiner Problemstellung, bevor ich weiter darauf eingehe:

Einige "Special"-Klassen sollen um irgendeine Funktion erweitert werden. Um dies bequem umzusetzen, willst Du diese Funktion in einer Klasse AWidget implementieren. Die "Special"-Klassen sollen dann durch Erben von einer Q-Klasse und der AWidget Klasse (Mehrfachvererbung) entstehen und damit schnell implementiert sein, da die Funktionalität (die in AWidget "gekapselt" wurde) bei allen gleich sein soll. Dein Problem ist dabei, dass AWidget für die Funktion und für einen ebenfalls beabsichtigten cast von einer Q-Basisklasse erben müsste, was sich natürlich in diesem Fall nicht mit der späteren Mehrfachvererbung verträgt, da die Q-Klassen von dieser Basisklasse ebenfalls bereits erben.

Ist das Deine Problemstellung?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich muss bei C++ bleiben, da wir das in diesem Projekt mit Qt einsetzen. Die Software soll auch auf Mac laufen, deshalb auch Qt.

Arbeite selbst an so einer Software. OSX arbeitet mit GCC und wie ich Deinen anderen Threads entnehme Du mit Visualstudio. Arbeite auf beiden Plattformen parallel, VS und GCC unterscheiden sich

Templates sind mir auch schon in den Sinn gekommen. Dabei stoß ich allerdings auf das Problem, dass das ganze im Rahmen einer PlugIn-Struktur realisiert wird und dieses Problem die Schnittstelle betrifft.

Templates müssen aber zur Kompilezeit mit den entsprechenden Typen erzeugt werden. Sollte also in einem PlugIn ein neuer Typ verwendet werden, kennt das Hauptprogramm diese Templatevariante nicht. Ich müsste also das Hauptprogramm ändern, was den PlugIn-Sinn zerstören würde.

Wie realisierst Du denn Deine Plugins. Wenn Du sie dynamisch machst, dann müssen sie in kompilierter Form vorhanden sein (dylib, la, dll). Dein Hauptprogramm muss die Includes der Lib enthalten. Wenn Du eine Art Parser für Deine Plugins geschrieben hast, dann sind Dir die Typen bekannt und dies kannst Du über Templates oder direkt realisieren.

So wie sich das anhört willst Du eine dynamische Typenbindung haben, was so nicht gehen wird. Vielleicht beschreibst Du einmal was Du als Ergebnis haben möchtest

Phil

Bearbeitet von flashpixx
Link zu diesem Kommentar
Auf anderen Seiten teilen

Das PlugIn-System hat mit dem Problem an sich nichts zu tun, also dazu nur soviel:

Es gibt eine Basisklasse PlugIn von der alle PlugIns abgeleitet sind. Das Hauptprogramm kennt nur die rudimentäre PlugIn-Klasse. Alle Spezialitäten müssen demnach innerhalb der PlugIns realisiert werden.

PlugIn ist in diesem Projekt vielleicht sogar der falsche Name, weil die Anwendung eigentlich nur ein Container für die PlugIns ist und die eigentliche Arbeit fast komplett aus den PlugIns kommt.

Ein QWidget, der aus dem PlugIn stammt, wird in die Gui eingebunden. Um flexibel zu halten, kriegt die Anwendung nur ein QWidget, auch wenn das PlugIn intern beispielsweise einen QGraphicsView verwendet.

Jetzt hab ich allerdings an diesen Widget spezielle Anforderungen, die ALLE Widgets aus ALLEN PlugIns mitbringen, obwohl diese intern höherwertige Klassen von QWidget verwenden. Die Hauptanwendung muss demnach diese Spezialität kennen. In einem puren QWidget ist diese Spezialität aber natürlich nicht enthalten.

Würde es sich bei dieser Spezialität um eine simple Methode handeln, z.B ganz einfach "bool isXYSet()", wäre es kein Problem dies wie in meinem Beispiel zu realisieren.

Jetzt brauch ich aber als Spezialisierung ein Signal oder einen Slot, um aber in meinem Interface (AWidget) einen Slot definieren zu können, muss ich selbst schon von QObject erben (und auch das Q_OBJECT Makro einbauen), da Slots sonst nicht funktionieren.

Dieses geerbte QObject wird aber dann zum Problem, wenn ich das Interface (AWidget) via Mehrfachvererbung in einen QWidget einbinden will -> Mehrfachvererbung von der gleichen Basisklasse.

Die einfachste Lösung wäre, wenn QWidget QObject nur virtuell erben würde und ich AWidget auch nur virtuell von QObject ableiten würde.


QWidget müsste dann so aussehen:
class QWidget : virtual public QObject {};

AWidget wäre dann:
class AWidget : virtual public QObject {};
[/PHP]

leider erbt QWidget aber QObject nicht virtuell.

Ich arbeite aktuell mit VS2005 mit dem Microsoft Kompiler. Die Mac-Portierung folgt erst noch, ist also aktuell noch nicht in Arbeit.

EDIT: Bevor danach gefragt wird: Die PlugIns sind DLLs die dynamisch geladen werden. Um sie dynamisch laden zu können, steht über dem ganzen nochmal eine C-Schnittstelle und die PlugIns werden darüber über eine Factory geladen.

Bearbeitet von FinalFantasy
Link zu diesem Kommentar
Auf anderen Seiten teilen

Es geht Dir im Grunde über die Signal-Slot-Mechanismen. Warum koordinierst Du Deine Events dann nicht über einen Eventlistener. Deine abstrakte Plugin Schnittstelle stellt eben einen Eventlistener / -sender zur Verfügung. Bei der Vererbung bleibt dieser natürlich vorhanden.

Die Schnittstelle erbt dann eben einmal von dem Eventlistener und einmal von QWidget

Phil

Link zu diesem Kommentar
Auf anderen Seiten teilen

Exakt.

Die schnellstmögliche Antwort ist leider: Das geht so nicht, wie Du es gerne möchtest.

Allerdings: Du könntest AWidget in ein reines Interface (in C++ eine rein abstrakte Klasse) umwandeln und so den Vererbungs-Bezug zu einer Q-Klasse vermeiden. Dieses Interface könnte die speziellen Methoden und eine Funktion beschreiben, die einen Zeiger (oder eine Refrenz) auf ein QWidget liefert. Dadurch könntest Du AWidgets zwar nicht mehr direkt in ein QWidget casten, aber es wäre möglich das dazugehörige QWidget zu erhalten.

Die Funktionalität von AWidget implementierst Du in den einzelnen abgeleiteten Klassen. Wenn die Implementierung praktisch überall gleich ist, kannst Du Dir Schreibarbeit (oder viel copy&paste) durch die Verwendung von Templates oder des Präprozessors ersparen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Vielleicht um es noch etwas klarer zu sagen: Dein Signal musst Du in den spezialisierten Klassen implementieren/einfügen, die von QObject erben, nicht in AWidget, das nur noch ein reines Interface für Deine selbst definierten Funktionen wäre.

Implementierungsarbeit könntest Du Dir ggf. mit Templates ersparen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das mit dem Eventlistner werd ich mir mal genauer anschauen, klingt jedenfalls vernünftig.

Bei mir sind eben viele Strukturen im Programm, z.B. komplexe mathematische Berechnung, GUI, Netzwerk, Threading usw. Zusätzlich ist ein Trace in das Programm eingebaut, so dass ich sehe "wo und wie" etwas getan wird. Das Problem, natürlich kann ich über all ein std::cout machen oder Signal-Slot-Sachen implementieren, aber damit ist es schwer Erweiterungen (und die sind jetzt schon eingeplant) zu implementieren. Im Grunde stelle ich eine "große" zentrale Schnittstelle zur Verfügung: Das sind mehrere Klasse, die alle von "Listener" abgeleitet werden und eben QObject

enthalten. Beim Start wird ein zentrales Objekt "MainListener" erzeugt in dem dann weitere Listener enthalten sind, GUI, Network, Math.... Soll nun irgendwo ein Event ausgelöst werden, rufe ich aus dem entsprechenden Listener einfach meine Methode "void send<Event>(<Params>)" auf. Diese löst dann mit Hilfe von Signal-Slot alles weitere aus.

Manchmal sieht man den Wald vor lauter Bäumen nicht. :upps

Passiert schon mal. Sag mal bescheid, wie Du das Problem gelöst hast

Phil

Link zu diesem Kommentar
Auf anderen Seiten teilen

Deine Meinung

Schreibe jetzt und erstelle anschließend ein Benutzerkonto. Wenn Du ein Benutzerkonto hast, melde Dich bitte an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

 Teilen

Fachinformatiker.de, 2021 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...

Wichtige Information

Fachinformatiker.de verwendet Cookies. Mehr dazu in unserer Datenschutzerklärung