VCL und dynamic_cast



  • Hallo,

    ich habe eine MDI Anwendung, die MDI Kindfenster von verschiedenen Formulartypen hat. Da die VCL keine Mehrfachvererbung sondern nur Interfacevererbung beherrscht habe ich einige Formulartypen vom Interface IPrintableForm erben lassen:

    struct IPrintableForm
    {
       virtual void print() const = 0;
       virtual void preview() const = 0;
    }
    

    Im Hauptfenster prüfe ich bei jedem Wechsel des aktiven MDI Kindfensters, ob das neue Fenster vom Typ IPrintableForm ist, um in der Toolbar den Status der entsprechenden Buttons zu aktualisieren. So weit, so gut, nur die VCL spielt mal wieder nicht mit:

    void TMainForm::update_toolbar()
    {
       IPrintableForm* pForm = dynamic_cast<IPrintableForm*>( ActiveMDIChild );
       BtnPrintForm->Enabled = (pForm != NULL);
       BtnPreviewForm->Enabled = (pForm != NULL);
    }
    

    Beim dynamic_cast liefert mir der BC6 folgended Fehlermeldung:
    Typumwandlung von TForm* nach IPrintableForm* nicht zulässig.

    Kann mir jemand folgende zwei Dinge erklären:

    1. was soll der Quatsch? Gegen Interfaces zu programmieren macht hier überhaupt keinen Sinn, weil man das Interface nicht bestimmen kann. Bin wieder mal auf 180 wegen der unglaublichen VCL Macken.
    2. wie löse ich das jetzt?


  • Ich würde mal vermuten, dass das daran liegt, dass die struct nicht im VCL-Stil ist. Die sollte, meiner Meinung nach, mindestens von TObject abgeleitet sein.



  • Joe_M. schrieb:

    Ich würde mal vermuten, dass das daran liegt, dass die struct nicht im VCL-Stil ist. Die sollte, meiner Meinung nach, mindestens von TObject abgeleitet sein.

    Dann hätte man tatsächlich Mehrfachvererbung - und das geht mit Delphi-Klassen nicht.

    DocShoe schrieb:

    1. was soll der Quatsch? Gegen Interfaces zu programmieren macht hier überhaupt keinen Sinn, weil man das Interface nicht bestimmen kann. Bin wieder mal auf 180 wegen der unglaublichen VCL Macken.

    Man sollte dynamic_cast<>, Delphi-Klassen und Interfaces nach Möglichkeit nicht zusammen verwenden. Die Gründe habe ich hier ausführlich erläutert.

    Edit: Interfaces sind durchaus auch ohne dynamic_cast<> sinnvoll, jedenfalls in anderen Konstellationen. Die Interface-Vererbung in Delphi und C++Builder wurde für COM-Interfaces entworfen; daher könntest du, sofern dein Interface von IUnknown ableitet, TObject::GetInterface<>() benutzen.

    DocShoe schrieb:

    1. wie löse ich das jetzt?

    In deinem speziellen Fall könntest du dir den zugehörigen QC-Report und den enthaltenen Workaround ansehen.

    Mit einem kleinen Makro kann man sogar einen Workaround einführen, der die Verwendung von dynamic_cast<> ermöglicht, so daß du deinen Code gar nicht anpassen mußt:

    // dynamic_cast.hpp
    
    #ifndef _DYNAMIC_CAST_HPP
    #define _DYNAMIC_CAST_HPP
    
    #include <System.hpp>
    
    namespace detail
    {
    
    enum reftype_t
    {
        rtType = 0,
        rtPointer = 1,
        rtReference = 2,
        rtPointerReference = 3,
    };
    
    template <typename T> struct RefType
    {
        enum { value = 0 };
        typedef T  type;
        typedef T* pointer;
        typedef T& reference;
    };
    
    template <typename T> struct RefType <T*>
    {
        enum { value = 1 };
        typedef T  type;
        typedef T* pointer;
        typedef T& reference;
    };
    
    template <typename T> struct RefType <T&>
    {
        enum { value = 2 };
        typedef T  type;
        typedef T* pointer;
        typedef T& reference;
    };
    
    template <typename T> struct RefType <T*&>
    {
        enum { value = 3 };
        typedef T  type;
        typedef T* pointer;
        typedef T& reference;
    };
    
    #ifndef __CODEGEARC__
        // from boost/type_traits/is_convertible.hpp
    // Copyright 2000 John Maddock (john@johnmaddock.co.uk)
    // Copyright 2000 Jeremy Siek (jsiek@lsc.nd.edu)
    // Copyright 1999, 2000 Jaakko J„rvi (jaakko.jarvi@cs.utu.fi)
    /*
    Boost Software License - Version 1.0 - August 17th, 2003
    
    Permission is hereby granted, free of charge, to any person or organization
    obtaining a copy of the software and accompanying documentation covered by
    this license (the "Software") to use, reproduce, display, distribute,
    execute, and transmit the Software, and to prepare derivative works of the
    Software, and to permit third-parties to whom the Software is furnished to
    do so, all subject to the following:
    
    The copyright notices in the Software and this entire statement, including
    the above license grant, this restriction and the following disclaimer,
    must be included in all copies of the Software, in whole or in part, and
    all derivative works of the Software, unless such copies or derivative
    works are solely in the form of machine-executable object code generated by
    a source language processor.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
    SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
    FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
    */
    
    // BEGIN boost
    
    typedef char yes_type;
    struct no_type
    {
       char padding[8];
    };
    
    struct any_conversion
    {
        template <typename T> any_conversion(const volatile T&);
        template <typename T> any_conversion(T&);
    };
    
    template <typename T> struct checker
    {
        static no_type _m_check(any_conversion ...);
        static yes_type _m_check(T, int);
    };
    
    template <typename From, typename To>
    struct is_convertible_basic_impl
    {
        static From _m_from;
        static bool const value = sizeof(checker<To>::_m_check(_m_from, 0) )
            == sizeof(yes_type);
    };
    
    // END boost
    
    #endif // #ifndef __CODEGEARC__
    
    enum classtype_t
    {
        ctCppClass = 0,
        ctDelphiClass = 1,
    };
    
    template <typename T>
        struct ClassType
    {
    #ifdef __CODEGEARC__ // C++Builder 2009 supports intrinsic type traits
        enum { value = __is_base_of (TObject, T) ? 1 : 0 };
    #else
        enum { value = is_convertible_basic_impl <const T*, const TObject*>::value ? 1 : 0 };
    #endif
    };
    
    template <classtype_t DestCT, classtype_t SrcCT>
        struct DynamicCastT
    {
        template <typename DestT, typename SrcT>
            static inline DestT DynamicCast (SrcT obj)
        { return dynamic_cast <DestT> (obj); }
    };
    
    template <>
        struct DynamicCastT <0, 1>
    {
        template <typename DestT>
            static inline DestT DynamicCast (TObject* obj)
        {
            DestT result = 0;
    
            if (obj)
            {
        #ifdef __CODEGEARC__ // C++Builder 2009 supports class methods
                if (TInterfaceEntry * ie = obj->GetInterfaceEntry (
                    __uuidof (DestT)))
        #else
                if (TInterfaceEntry * ie = TObject::GetInterfaceEntry (obj->ClassType (),
                    __uuidof (DestT)))
        #endif
                {
                    if (ie->IOffset)
                        result = (DestT) ((int) obj + ie->IOffset);
                }
            }
            return result;
        }
    };
    
    template <typename Dest, typename Src>
        Dest _dynamic_cast (Src obj)
    {
        return DynamicCastT <
            ClassType <typename RefType <Dest>::type>::value,
            ClassType <typename RefType <Src >::type>::value>::DynamicCast <Dest> (obj);
    }
    
    } // namespace detail
    
    #define dynamic_cast detail::_dynamic_cast
    
    #endif // _DYNAMIC_CAST_HPP
    

    Wenn du diese Headerdatei einbindest, kannst du auch von TObject* nach einer C++-Klasse casten, sofern dieser eine UUID zugeordnet ist.
    Jedoch möchte ich nochmals nachdrücklich darauf verweisen, daß dynamic_cast<> zwischen C++- und Delphi-Klassen nicht sicher ist. Der Compiler wird und kann dich nicht daran hindern, einen dynamic_cast<> von IPrintableForm zu jeder anderen polymorphen C++-Klasse durchzuführen, und jeder dieser Versuche endet im glücklichsten Falle mit einer AV.



  • Danke für deinen Beitrag, audacia, ich gehe jetzt über Typlisten und caste direkt auf den konkreten Formulartyp.
    Nachdem ich mir deine Links durchgelesen und zu implementieren versucht habe (das QC Snippet habe ich schon ausprobiert, scheint für das RAD Studio gemacht zu sein. static TObject::GetInterfaceEntry( ...,... ) gibts im BCB6 nicht, der akzeptiert nur die Methode mit einem _GUID Parameter. Nach der Korrektur ist dann der Linker mit einem unresolved external ausgestiegen) fühle ich mich in meiner Meinung über BCB/CG wieder einmal bestätigt:
    Das Ding taugt nix für professionellen Einsatz. Wenn man mal schnell eine bunte Anwendung zusammenklicken will ist das ja in Ordnung, aber ich rege mich inzwischen fast täglich über diesen C++ Compiler Simulator auf.
    Wenn ich einen C/C++ Compiler kaufe, dann erwarte ich auch, dass man damit allgemeine Sprachkonzepte umsetzen kann. Ich möchte keinen Delphi/Pascal Compiler, der nebenbei auch C++ versteht und aus C++ Sourcen irgendwie irgendetwas Lauffähiges zusammendoktort ohne sich an die Standards zu halten. Und dass es für ein solch rudimentäres Problem seit 6 Jahren keine Lösung gibt ist einfach nur katastrophal. Aber was reg ich mich auf....

    Gruß,
    Doc



  • Daß es mit dem C++Builder 6 nicht funktioniert, kann gut sein; ich habe es mit C++Builder 2009 getestet, und auch mit C++Builder 2006 sollte es noch klappen.

    Sich darüber zu beschweren, daß ein mittlerweile gute acht Jahre altes Produkt nicht mehr zeitgemäß ist, halte ich, offen gesagt, für albern. Und bei allem Respekt für deinen Ärger:

    DocShoe schrieb:

    Das Ding taugt nix für professionellen Einsatz.

    Dem muß ich, im Einklang mit den meisten Benutzern, energisch widersprechen.

    DocShoe schrieb:

    aber ich rege mich inzwischen fast täglich über diesen C++ Compiler Simulator auf.

    Regst du dich auch täglich über die unzulänglichen Compiler von MSVC 6.0 oder 7.0 auf?

    DocShoe schrieb:

    Wenn ich einen C/C++ Compiler kaufe, dann erwarte ich auch, dass man damit allgemeine Sprachkonzepte umsetzen kann.

    Das kann man auch mit C++Builder 6 völlig problemlos.

    DocShoe schrieb:

    Ich möchte keinen Delphi/Pascal Compiler, der nebenbei auch C++ versteht und aus C++ Sourcen irgendwie irgendetwas Lauffähiges zusammendoktort ohne sich an die Standards zu halten. Und dass es für ein solch rudimentäres Problem seit 6 Jahren keine Lösung gibt ist einfach nur katastrophal. Aber was reg ich mich auf....

    Sorry, vollkommen falscher Ansatz.

    Mit C++Builder hast du einen vollwertigen C++-Compiler, der zusätzlich über Compiler-Erweiterungen das Delphi-Objektmodell implementiert. In dieser Hinsicht ist C++Builder gut mit Managed C++ oder C++/CLI vergleichbar.

    Solange du dich in C++Builder oder C++/CLI an die C++-Standards hältst, hast du keine compilerbedingten Probleme (das TMP-Zeugs, mit dem BCC bis einschließlich v5.8 größere Probleme hatte, mal außen vor gelassen - C++Builder 2007 und 2009 sind hier ja wieder einigermaßen konkurrenzfähig). Aber beide Objektmodelle bedürfen gesonderter Behandlung entsprechend den Paradigmata, die ihnen zugrundeliegen. In C++/CLI kannst du nicht dynamic_cast<> benutzen, um ein verwaltetes Objekt in ein natives Interface zu casten; für Delphi-Klassen in C++Builder gilt das ebenso. Für verwaltete Klassen kann man gcnew nicht überladen, ebenso kann man den new-Operator nicht für Delphi-Klassen überladen. Verwaltete C++/CLI-Klassen können nicht von nativen Klassen erben; ebenso können Delphi-Klassen nicht von C++-Klassen erben - mit der Ausnahme von abstrakten Basisklassen, die die Restriktionen für Interfaces erfüllen.

    Die sauberste Lösung wäre vielleicht gewesen, analog zu dem "interface"-Keyword aus Delphi eine neue Compiler-Erweiterung namens __interface einzuführen, die Interfaces definiert, von denen Delphi-Klassen ableiten können. Wenn du dir die vom Delphi-Compiler generierten Headerdateien ansiehst, dürfte dir auffallen, daß man offenbar auch einmal erwogen hatte, diesen Weg zu gehen.

    Nun ist es aber anders gekommen, und wir können auch von gewöhnlichen abstrakten C++-Basisklassen ableiten. Der Vorteil ist, daß jede herkömmliche Interface-Deklaration im C++-Stil ohne Umschweife verwendet werden kann und nicht erst ein __interface-Pendant erstellt werden muß. Dennoch: sofern solche abstrakten Basisklassen mit Delphi-Klassen zusammen benutzt werden, betrachtet der BCC sie als COM-Interfaces, und wenn du solche polymorph casten möchtest, mußt du nun mal QueryInterface() anstelle von dynamic_cast<> verwenden.

    In diesem Sinne könntest du deine Sichtweise vielleicht mal ein wenig ins Verhältnis setzen - oder alternativ auch gerne mal im C++/CLI-Forum die Reaktionen auf vergleichbare Rants testen.



  • Noch einmal dazu:

    DocShoe schrieb:

    (das QC Snippet habe ich schon ausprobiert, scheint für das RAD Studio gemacht zu sein. static TObject::GetInterfaceEntry( ...,... ) gibts im BCB6 nicht, der akzeptiert nur die Methode mit einem _GUID Parameter. Nach der Korrektur ist dann der Linker mit einem unresolved external ausgestiegen)

    In QC #40084 wird auf #40100 verwiesen, der dieses Problem und die Lösung beschreibt (ggf. die Kommentare lesen).
    Nachdem ich den Workaround für #40100 angewendet hatte, akzeptiert auch C++Builder 6 meinen Code ohne weiteres.



  • DocShoe schrieb:

    ....
    Das Ding taugt nix für professionellen Einsatz. ...

    Auch da werde ich mal heftigst widersprechen.

    Ich habe 2001 mit dem CB5 eine komplette Anlagensteuerung geschrieben für die Hinterachsenfertigung des Bentley, Phaeton und Passat B5, (komplett mit automatischer Auftragsvergabe, Darstellung von Tabellen auf mehreren Monitoren, Ausdruck auf mehreren Druckern z.T. mit Barcodes, Datenbank auf Oraclebasis) die Anlage läuft heute noch einwandfrei.
    Im gleichen Jahr entstand eine Auftragsvergabesoftware (ebenfalls mit CB5) für die Federbein und Schwenklagermontage bei Audi in Ingostadt. Auch heute noch in Betrieb, teilweise werden bis zu 1000 stck/Tag gefertigt.
    Im VW Werk laufen seit 2004 mehrere neue Erfassungspunkte 24 Stunden am Tag, 7 Tage die Woche, die SW dafür wurde auch mit dem CB5 geschrieben.

    So und nu kommst du. was ist daran unprofessionell?



  • So, ich hab mir lange überlegt, ob und wie ich antworten soll.
    Mag ja sein, dass es Leute gibt, die den BCB besser finden als die MS Produkte, aus welchen Gründen auch immer. Es gibt sicher Vor- und Nachteile beider Produkte, die jeweils für sich oder gegen das Konkurrenzprodukt sprechen. Woran ich meine Meinung festmache sind jedenfalls die Dinge, die ich im täglichen Umgang mit dem BCB6 erlebe. Der Umstieg auf CG2009 steht kurz bevor, aber im Moment müssen wir uns mit jeder Menge Legacy Code und 3rd Party Libraries beschäftigen, die für den CG2009 teilweise nicht mehr existieren bzw. unterstützt werden.
    Ich komme nun mal vom MS Compiler und habe jahrelang mit dem VS 6.0 gearbeitet. Die damaligen Aufgabengebiete waren komplett andere als heute, sodass ich mit den Flaws des VS 6 nie in Berührung gekommen bin. Laut Aussagen von Leuten in anderen Foren sind VS 6 und VS 2003 wohl die schlechtesten Compiler Suites, die je entwickelt worden sind. Privat benutze ich das VS 2008 und bin äußerst zufrieden damit.
    Meine Aussage über die Untauglichkeit des BCB6 zur professionellen Anwendungsentwicklung stütze ich auf folgende Punkte:
    - fehlende IDE/Debugger Stabilität I:
    Unser Team besteht aus 6 Entwicklern, bei denen der Builder während des Debuggens mit irgendwelchen seltsamen Fehlermeldungen stehenbleibt und nicht aus dem Debug Lauf zurückkommt. Einzige Lösung ist dann den BCB über den Task Manager zu beenden oder manchmal sogar den Rechner neu zu starten. Manchmal nimmt die IDE dann geöffnete Formulardateien mit und zerstört diese, sodass ein Backup eingespielt werden muss. Passiert selten, ist aber schon mehrmals vorgekommen.
    - fehlende IDE/Debugger Stabilität II:
    Bei Schleifendurchläufen mit vielen Iterationen verabschiedet sich der Variableninspektor und zeigt einfach nix mehr an. Nicht kritisch, aber äußerst ärgerlich.
    - kritische Compiler Fehler bzw. nicht nachvollziehbares Verhalten:
    Es gibt zumindest einen Compiler Bug, der micht knapp 2 Tage Arbeit gekostet hat (Codeguru forums. Ich kann mich dran erinnern, dass ich mehrere solcher Probleme hatte, konkret fiel mir allerdings nur dieser eine Fall ein.

    - dieses eigenartige Delphi-VCL Framework, dass irgendwie in einen C++ Compiler integriert wird
    Manche Dinge nerven einfach nur, weil man die Brücke zwischen Delphi/Pascal und C++ schlagen muss. Anders als bei MS hat man hier keine integrierten Alternativen (MFC/.NET) und muss die Klippen selbst umschiffen (wenn es möglich ist). 3rd party Lösungen wie wxWidgets oder QT mal aussen vor, da sie mit Borland nichts zu tun haben.

    Die Aussagen anderer Leute sind für mich weniger konkret als die Probleme, die der BCB tagtäglich bei uns verursacht. In unserem Team haben ALLE diese Probleme, ich bin also kein Einzelfall. Mag ja sein, dass eure Projekte problemlos umsetzbar sind und eure Compiler stabiler laufen, aber aus meiner Sicht ist und bleibt der BCB einfach nur schlecht.



  • DocShoe schrieb:

    Mag ja sein, dass es Leute gibt, die den BCB besser finden als die MS Produkte, aus welchen Gründen auch immer.

    Die meisten Probleme die du ansprichst gelten aber für beide Compiler aus ungefähr der gleichen Zeit (Also z.B. Vergleich VC6 zu BCB6). Das was die VCL dem BCB ist, ist die MFC des VC (und ich bin persönlich von beiden nicht sonderlich begeistert; Der VCL merkt man den Delphischwerpunkt an, der MFC das hohe Alter - Beides keine gute Basis).

    Und zumindestens damals war der BCB imho trotz VCL näher am Standard als VS (inzwischen nicht mehr, wobei der 2009 wohl wieder eine massive Verbesserung darstellen soll).

    Der VC ist imho tatsächlich stabiler, wenn gleich ich den BCB6 nicht kenne (Habe bislang mit 3/4/2007 und der 2009 Trial gearbeitet), soviel ich aber hier mitbekommen habe, war einer der Gründe von BCB6 auf BCB2007 zu wechseln in genau diesen Punkt begründet - kann sein das die 6er Version tatsächlich ein Fehlgriff dargestellt hat.

    DocShoe schrieb:

    Laut Aussagen von Leuten in anderen Foren sind VS 6 und VS 2003 wohl die schlechtesten Compiler Suites, die je entwickelt worden sind. Privat benutze ich das VS 2008 und bin äußerst zufrieden damit.

    Zumindestens für VS6 kann ich das ganze unterschreiben, aber bereits der 2003er war besser. Die Versionen 2005/2008 sind sowohl stabil als auch sehr Standardkonform (unter Windows ist IMHO VS2008 die aktuelle Referenz unter den C++ Compilern). Was VS aber nicht ist: Eine RAD Umgebung. Hier hat MS erst seit den .Net Compiler, sowie VB eine vergleichbare Umsetzung wie der BCB.

    cu André



  • Ich bin schon seit dem BCB1 dabei. Die Stabilität der einzelnen Varianten ist schon sehr unterschiedlich. BCB3 bis 5 waren eigentlich recht stabil (den 5er nutze ich immer noch manchmal) während der BCB6 und der BCB2006 deutlich unstabiler waren.
    Erst der BCB2007 läuft wieder recht ordentlich. Er hat zwar auch manchmal Schwierigkeiten (hauptsächlich Suche und Debugger) die sind aber meiner Meinung nach nicht wirklich gravierend.



  • Ich muss _DocShoe_ im Bezug auf Stabilität recht geben: Aus meiner Erfahrungen (bin ebenfalls wie Braunstein seit CB 1 und Delphi 1 dabei) sind CB6 und BDS2006 sehr instabil und neigen wirklich gerne mal zum Absturz, z.b. ist es mir selten gelungen, das BDS sauber zu beenden, sehr oft gings nur noch über den Taskmanager.

    CB2009 ist dagegen sehr stabil, damit hatte ich bisher keine Probleme.

    Aber die Aussage, das mit dem CB keine professionelle SW erstellt werden kann, ist schlichtweg falsch. 👎



  • Kannst du den BCB2007 mit dem 2009er stabilitätsmäßig vergleichen? Ich konnte mich bis jetzt noch nicht dazu durchringen den neuen zu erwerben.
    Unicode spielt für mich keine Rolle und die neuen C++Features werde ich aus Kompatibilitätsgründen so schnell nicht einsetzen. Deswegen habe ich jetzt noch keinen Bedarf für ein Update gesehen.



  • Hi Braunstein,
    die 2007er Version besitze ich nicht, deshalb kann ich dir leider keinen "Vergleich anbieten". 😉



  • Aber die Aussage, das mit dem CB keine professionelle SW erstellt werden kann, ist schlichtweg falsch.

    Das habe ich ja auch nie behauptet. Ich bin schon der Meinung, dass man mit dem BCB 6 professionelle Software erstellen kann (schliesslich tut unsere Firma genau das). Ich bin allerdings der Meinung, dass das Produkt BCB 6.0 kein professionelles Werkzeug ist, und zwar aus den Gründen, die ich früher schon angeführt habe.



  • Auch hier hat sich die Situation verändert, und zwar bereits in XE. Delphi-Klassen können nach wie vor Interfaces handhaben, allerdings nur solche, die auch von Delphi als Interfaces anerkannt werden - also solche, die von IUnknown / IInterface ableiten. Läßt man das außer acht, so bekommt man eine Warnung:

    class IMyIntf
    {
        virtual void foo (void) = 0;
    };
    
        // Warnung W8130: Interface 'IMyIntf' ist nicht von IUnknown abgeleitet. (Interfaces müssen von IUnknown abgeleitet sein)
    class TInterfacedSomething : public TObject, public IMyIntf
    {
    };
    

    Außerdem gibt es seit C++Builder XE eine neue Klasse TCppInterfacedObject<> , die einem das erneute Implementieren von QueryInterface() , AddRef() und Release() erspart:

    // die IID ist nach wie vor optional
    class __declspec (uuid ("{3B6D49E8-712C-4442-9A02-1E37F107516F}")) IMyInterface : public IUnknown
    {
    public:
        void foo (void) = 0;
    };
      // DelphiInterface<> ist ein Smart-Pointer für Delphi-/COM-Interfaces und dient dem Komfort.
      // Das Präfizieren des Typedefs mit "_di_" ist Konvention und wird auch vom .hpp-Generator des Delphi-Compilers so gemacht.
    typedef DelphiInterface<IMyInterface> _di_IInterface;
    
    class TMyObject : public TInterfacedObject, public IMyInterface
    {
    public:
        ULONG   __stdcall AddRef (void)
        { return TInterfacedObject::_AddRef (); }
        ULONG   __stdcall Release (void)
        { return TInterfacedObject::_Release (); }
        HRESULT __stdcall QueryInterface (REFIID iid, void** p)
        { return TInterfacedObject::QueryInterface (iid, p); } 
    
        void foo (void) { ... }
    };
    

    wird also zu

    class TMyObject : public TCppInterfacedObject<IMyInterface>
    {
    public:
        void foo (void) { ... }
    };
    

    (Möglich wird das durch das Beheben des in QC #66382 beschriebenen Problems.)

    Es ist weiterhin nicht möglich, dynamic_cast<>() mit Interfaces zu benutzen. Die technischen Gründe dafür stehen hier; seit XE gibt es aber als Ersatz dafür den interface_cast<>() , mit dem man nach Belieben zwischen Interfaces und Klassen hin- und hercasten kann:

    TMyObject* obj = new TMyObject;
    
            // implicit upcast to guard interface
        _di_IMyInterface myInterface = obj;
    
            // explicit object->interface upcast
        _di_IInterface i2 = interface_cast<IInterface> (obj);
    
            // explicit interface->interface downcast
        _di_IMyInterface i3 = interface_cast<IMyInterface> (i2);
    
            // explicit interface->object downcast
        TMyObject* obj2 = interface_cast<TMyObject> (i3);
    

    Daß man zu einem bestimmten Interface hin-casten kann, erfordert natürlich, daß diesem Interface mittels __declspec(uuid()) eine IID zugeordnet ist.

    Wohlgemerkt ist interface_cast<>() keine Keyword-Erweiterung, sondern ein Funktionstemplate im System-Namespace. Allerdings verweist der Compiler auf interface_cast<> , wenn man versehentlich dynamic_cast<>() verwendet:

    // Warnung W8131: Umwandlung des Interface 'IUnknown' in eine Klasse im Delphi-Stil. Verwenden Sie
            // stattdessen 'System::interface_cast<TBar>(intf)'
        TMyObject* obj3 = dynamic_cast<TMyObject*> (i3);
    


  • Danke für die nützlichen Infos.

    Zu Zeiten der in diesem Thread erwähnten BCB5 und 6, gab es so nützliche Bücher wie den C++ Builder Developers Guide. Da konnte man sowas schön nachlesen.

    Wir können dich vermutlich nicht überreden, einen Nachfolger zu verfassen ? 😕 🙂



  • nn schrieb:

    Zu Zeiten der in diesem Thread erwähnten BCB5 und 6, gab es so nützliche Bücher wie den C++ Builder Developers Guide. Da konnte man sowas schön nachlesen.

    Ich weiß, daß bei Embarcadero wieder an einem "C++Builder Developers Guide" gearbeitet wird (wobei fraglich ist, ob es das nochmal in Druckform geben wird). Das Buch bestand ja im Wesentlichen aus den in schön lesbare Form gebrachten Inhalten der Online-Hilfe. Die aktuelle Online-Hilfe beinhaltet wieder die meiste Dokumentation, die zwischenzeitlich mal verlorengegangen ist, und auch neuere Themen werden oft gut behandelt. Aber bis es wieder den schlüssigen Zusammenhang des "Developers Guide" hat, muß noch einiges getan werden.

    nn schrieb:

    Wir können dich vermutlich nicht überreden, einen Nachfolger zu verfassen ? 😕 🙂

    Haha 😉 Nein; ich habe weder einen Job bei Embarcadero noch überflüssige Lebenszeit. Ich könnte aber solche Informationen on-demand (wenn es z.B. hier im Forum angefragt wird) auf meiner Webseite in einem Artikel zusammenzustellen.



  • audacia schrieb:

    Sich darüber zu beschweren, daß ein mittlerweile gute acht Jahre altes Produkt nicht mehr zeitgemäß ist, halte ich, offen gesagt, für albern.

    Ich schon. Denn die Features sind ja "drin", sollten also funktionieren. Das taten sie halt schon vor 8 Jahren nicht. Das sie es mittlerweile in späteren Versionen tun ist keine Entschuldigung für das ursprüngliche Produkt.



  • Morle schrieb:

    Das sie es mittlerweile in späteren Versionen tun ist keine Entschuldigung für das ursprüngliche Produkt.

    Stimmt.

    Ich denke, ich muß die zitierte Aussage einschränken. Ich war mir nicht darüber im klaren, wie schwer es sein kann, ein Lock-in auf eine ältere Version zu überwinden.

    Andererseits wirst du zustimmen, daß es albern wäre, sich über die von Windows 98 verursachten Alltagsbeschwerden zu beklagen, solange man genausogut auf ein modernes Windows migrieren könnte. Darauf wollte ich hinaus.



  • audacia schrieb:

    Andererseits wirst du zustimmen, daß es albern wäre, sich über die von Windows 98 verursachten Alltagsbeschwerden zu beklagen, solange man genausogut auf ein modernes Windows migrieren könnte. Darauf wollte ich hinaus.

    Nur das man eben nicht "einfach so" migieren kann. Wir entwickeln mittlerweile mit was anderem. Für unsere Abteilung z.B. wären das mal eben 20 Lizenzen für eine aktuelle Version vom Builder zusätzlich zu unserer eigentlichen IDE. Und das nur um alte Applikationen weiter zu pflegen und sich damit ggf. auch noch Probleme beim Update einzuhandeln? Welcher Chef lässt sich denn auf sowas ein?
    Ich hatte mit Dir doch schonmal darüber geredet, glaube ich.


Anmelden zum Antworten