TFrame, ich möchte zusätzlich von Interface erben
-
Ich habe eine Klasse, die von TFrame erbt (erstellt mit Designer), diese bräuchte aber noch ein Interface von der sie erbt.
Aber wenn ich dem Interface, ein virtuellen Destruktor gebe, erhalte ich Linker Probleme.
Hab ich was falsch gemacht?//--------------------------------------------------------------------------- class FrameInterface { public: // das hier einkommentieren -> Linker Fehler //virtual ~FrameInterface() __attribute__((fastcall)) = default; virtual void translate() = 0; }; //---------------------------------------------------------------------------
//--------------------------------------------------------------------------- class THeaderOptionsFrame : public TFrame , public FrameInterface { __published: // Von der IDE verwaltete Komponenten TComboBox *HeaderLayer; TLabel *Label1; void __fastcall Label1Click(TObject *Sender); private: // Benutzer-Deklarationen public: // Benutzer-Deklarationen // ~THeaderOptionsFrame() __attribute__((fastcall)) = default; void translate() override; __fastcall THeaderOptionsFrame(TComponent* Owner); }; //--------------------------------------------------------------------------- extern PACKAGE THeaderOptionsFrame *HeaderOptionsFrame; //---------------------------------------------------------------------------
"[ilink32 Fehler] Error: Nicht auflösbares externes '__fastcall _ZTh-696_THeaderOptionsFrame::~_ZTh-696_THeaderOptionsFrame()' referenziert von ***\WIN32\DEBUG\HEADER_OPTIONS.OBJ"
also "unresolved external..."btw: ich kriege ich meine IDE (Berlin 10.1) nicht auf Englisch umgestellt, das Tool ignoriert mich einfach...
-
5cript schrieb:
Ich habe eine Klasse, die von TFrame erbt (erstellt mit Designer)...
Hab ich was falsch gemacht?Man kann mich gerne korrigieren, aber nach meiner Erkenntnis geht es nicht. Bei allen auf Delphi basierenden Klassen (VCL-Datentypen) scheiterte mein Versuch der Mehrfachvererbung.
Ich habe mir dann mit Templates ausgeholfen, die bei einer Klasse eine bestimmte Schnittstelle (Nicht im Sinne von Vererbung, sondern rein von der Methodensignatur her) erwartet hatten.
-
Hallo,
Soviel wie ich weiß geht das auch nicht.
Ich behelfe mir so, dass ich von TFrame eine abstrakte Basisklasse ableite die das Interface implementiert. Von der wiederum leite ich dann die konkreten Frames ab.
-
Delphi unterstützt keine echte Mehrfachvererbung, man kann allerdings Interfaces (Klassen, die nur pure virtual Methoden besitzen) vererben:
struct __declspec( uuid( "{EC2265C9-9A41-4E52-9D28-F67613A93A2D}" ) ) IPrintable { virtual void print() = 0; virtual void preview() = 0; }; class MyFrame : public TFrame, public IPrintable { ... }
Über einen
dynamic_cast
kommt man da allerdings nicht wieder dran, sondern muss einige Verrenkungen übder das Delphi Typsystem machen (deshalb auch das__declspec( uuid (...) )
.template<typename DestT> static DestT delphi_dynamic_cast( TObject* obj ) { if( obj ) { TInterfaceEntry* Entry = obj->GetInterfaceEntry ( __uuidof( DestT ) ); // funktioniert nur in 32bit Umgebungen, bei 64bit muss // man erst auf __int64 casten if( Entry ) return (DestT) ((int) obj + Entry->IOffset); } return nullptr; }
Und so setzt man das ein:
IPrintable* Printable = delphi_dynamic_cast<IPrintable>( theFrame ); if( Printable ) { Printable->print(); Printable->preview(); }
-
DocShoe schrieb:
...
Wow, das ist irgendwie eklig, aber sehr gut zu wissen.
Wenn ich das gleich gewusst hätte, hätte ich das so gemacht.Braunstein schrieb:
...
Hmmm, da muss ich nochmal drüber nachdenken, wenn ich vor dem code sitze.
Ich hatte in der Zwischenzeit ein Workaround mit templates gebaut.
-
DocShoe schrieb:
Über einen
dynamic_cast
kommt man da allerdings nicht wieder dran, sondern muss einige Verrenkungen übder das Delphi Typsystem machen [...]Verzeih, aber das ist ja furchtbar. Zum Glück geht das viel einfacher, nämlich mit
interface_cast<>()
.
-
Aber interface_cast verlangt, dass die Methoden QueryInterface, AddRef und Release implementiert sind, wie bei den Delphi Interfaces.
class THeaderOptionsFrame : public TFrame , public TCppInterfacedObject<IOptionsFrame>
ist ja schon wieder quark, weil das ist wieder Mehrfachvererbung.
(Für folgenden code)__interface __declspec(uuid("{5898CCAF-1EE4-4EB4-A785-48920E5E97A5}")) IOptionsFrame : public IInterface { virtual void translate() = 0; virtual void setOwner(WikiElements::BasicElement* element) = 0; };
Alternativ:
__interface __declspec(uuid("{5898CCAF-1EE4-4EB4-A785-48920E5E97A5}")) IOptionsFrame { virtual void translate() = 0; virtual void setOwner(WikiElements::BasicElement* element) = 0; };
resultiert in:
[bcc32c Fehler] systobj.h(264): kein Member mit Namen 'AddRef' in 'IOptionsFrame'
oder, dass THeaderOptionsFrame abstrakt ist, weil QueryInterface, AddRef und Release nicht implementiert sind.
bei Aufruf von:auto* frame = element->getOptionsFrame(); // auto = THeaderOptionsFrame if (frame) { // Ich hatte es kurz nachgeprüft: interface_cast <IOptionsFrame*> ist falsch. interface_cast <IOptionsFrame> (frame)->translate(); // ... }
EDIT: Zur Zeit benutze ich die Verrenkung von DocShoe
-
Und renne gleich wieder gegen eine Wand, weil: ???
Habe jetzt ein weiteres Frame hinzugefügt und sobald ich das auch erben lasse vom Interface, dann erhalte ich untenstehenden Linkerfehler. -.-//--------------------------------------------------------------------------- #ifndef text_optionsH #define text_optionsH //--------------------------------------------------------------------------- #include "frame_interface.h" #include "../element_fwd.h" #include <System.Classes.hpp> #include <Vcl.Controls.hpp> #include <Vcl.StdCtrls.hpp> #include <Vcl.Forms.hpp> //--------------------------------------------------------------------------- class TTextOptionsFrame : public TFrame , public IOptionsFrame { __published: // Von der IDE verwaltete Komponenten TLabel *Label1; private: // Benutzer-Deklarationen bool translated_; WikiElements::Text* owner_; public: // Benutzer-Deklarationen void translate(); void setOwner(WikiElements::BasicElement* owner); __fastcall TTextOptionsFrame(TComponent* Owner); }; //--------------------------------------------------------------------------- extern PACKAGE TTextOptionsFrame *TextOptionsFrame; //--------------------------------------------------------------------------- #endif
[ilink32 Fehler] Error: Nicht auflösbares externes 'TTextOptionsFrame::' referenziert von D:\DEVELOPMENT_IWS\WIKI-PROJECT\WIKI-EDITOR-VCL\WIN32\DEBUG\TEXT.OBJ
"externes 'TTextOptionsFrame::'" was soll das für nen Schmarn?
-
audacia schrieb:
DocShoe schrieb:
Über einen
dynamic_cast
kommt man da allerdings nicht wieder dran, sondern muss einige Verrenkungen übder das Delphi Typsystem machen [...]Verzeih, aber das ist ja furchtbar. Zum Glück geht das viel einfacher, nämlich mit
interface_cast<>()
.Witzigerweise habe ich das von dir
-
5cript schrieb:
Aber interface_cast verlangt, dass die Methoden QueryInterface, AddRef und Release implementiert sind, wie bei den Delphi Interfaces.
Natürlich. Interface-Implementierung bei Delphi-Klassen wird nur für COM-Interfaces unterstützt, dein Interface muß also von
IUnknown
erben. Das Implementieren von Interfaces, die nicht vonIUnknown
erben, wird nicht unterstützt. (Gab es dazu nicht mal eine Compilerwarnung? Vielleicht hat es die nicht zum Clang-Compiler geschafft?)5cript schrieb:
class THeaderOptionsFrame : public TFrame , public TCppInterfacedObject<IOptionsFrame>
ist ja schon wieder quark, weil das ist wieder Mehrfachvererbung.
Genau. Aber
TFrame
erbt vonTComponent
, welchesIInterface
(=IUnknown
) implementiert und also schon Implementationen der drei Methoden mitbringt.Hier erweist es sich als hinderlich, daß Delphi anders mit Methodenüberschreibungen umgeht als C++. Folgendes Beispiel:
type TBase = class(TInterfacedObject) procedure Foo; end; IMyInterface = interface procedure Foo; end; TDerived = class(TBase, IMyInterface);
In Delphi benutzt
TDerived
die geerbte (nicht notwendig virtuelle!) MethodeFoo()
vonTBase
, um implizitIMyInterface.Foo()
zu implementieren. In C++ passiert das nicht, hier mußt du explizit an die geerbte Methode verweisen. Für den Spezialfall derIUnknown
-Methoden gibt es dafür das MakroINTFOBJECT_IMPL_IUNKNOWN()
:class TTextOptionsFrame : public TFrame , public IOptionsFrame { public: INTFOBJECT_IMPL_IUNKNOWN(TFrame) ... };
"externes 'TTextOptionsFrame::'" was soll das für nen Schmarn?
Das ist die VMT zur Klasse
TTextOptionsFrame
. Den Linkerfehler verstehe ich gerade auch nicht; nimm doch malTDUMP
zur Hilfe, um nachzusehen, warumtext.obj
auf diese VMT verweist und warumtext_options.obj
das VMT-Symbol nicht exportiert.DocShoe schrieb:
Witzigerweise habe ich das von dir
Erwischt
Genaugenommen hast du es aber nicht von mir, sondern gemeinsam mit mir aus dieserm QC-Report, auf den ich in der fraglichen Diskussion vor 8 Jahren verlinkt hatte. Die bessere Lösung heißt TObject::GetInterface<>(), was ich in demselben Post auch erwähne.
Außerdem schrieb ich in demselben Thread drei Jahre später, daß es seit C++Builder XE den
interface_cast<>()
gibt, der Casts in beide Richtungen unterstützt und einem die ganzen haarigen Details abnimmt. Die Hoffnung, daß dadurch die furchtbaren Workarounds wieder verschwinden möchten, war wohl leichtfertig
-
(Die späte Antwort, weil ich nur sporadisch an diesem Projekt arbeite)
Ich habe das mit der vtable Sache nicht "schnell genug" lösen können, deswegen habe ich den ganzen Kram über den Haufen geworfen und benutze jetzt ein Klassentemplate als Adapter.
-
jetzt wo ich alles umgebaut habe ist mir aufgefallen wo der Fehler her kam,
bzw ich wette es zu wissen.void TTextOptionsFrame::translate();
war nicht aufgelöst. Das fehlte.
bescheuerte unverständliche Fehlermeldung....