Ableitung "nicht definiert"?



  • Hi.
    Es soll halt eine Art Pluginsystem werden. Ich hab mir viel dazu durchgelesen und wenn ich das richtig verstanden habe, mache ich das auch so "wie üblich".

    Also IScript und IModule werden veröffentlicht (als SDK), dann noch eine .lib und schon kann man Plugins basteln. Nun gut, da aber die SDK CModule gar nicht kennt, verwende ich in IScript eben auch IModule zum returnen (soweit richtig?).

    Im Programm selber will ich dann aber natürlich die Daten für die "echten" Klassen speichern (also CScript und CModule), damit ich vollen Zugriff darauf habe (nur im Programm kann man Module und Scripte instanzieren).

    Ich hoffe ich habe das ganze gut beschrieben.

    Zum Vector: Muss ich mir dann echt einen zweiten Vector anlegen, der die gleichen Inhalte, nur halt nach IModule* gecastet hat?

    Gruß



  • theliquidwave schrieb:

    Also IScript und IModule werden veröffentlicht (als SDK), dann noch eine .lib und schon kann man Plugins basteln. Nun gut, da aber die SDK CModule gar nicht kennt, verwende ich in IScript eben auch IModule zum returnen (soweit richtig?).

    Ja.

    Zum Vector: Muss ich mir dann echt einen zweiten Vector anlegen, der die gleichen Inhalte, nur halt nach IModule* gecastet hat?

    Wenn du GetModules() in die Schnittstellendefinition von IScript aufnehmen willst (was ja Sinn macht) hast du das Problem dass du ueberall nur auf abstrakte IModules zugreifen kannst (oder dumm downcasten musst). Oder eben wie du gesagt hast eine Kopie des vectors erstellen musst.

    Ein

    template<typename ModuleType>
    virtual vector<ModuleType> const& getModules();
    

    geht ja leider nicht.

    iteratoren dagegen bieten was tolles. Statt vector<IModule*> zu returnen, liefern wir einfach einen begin und end iterator. bzw. idealerweise eine Range. der Einfachheithalber erklaere ich es jetzt mit iteratoren, in deinem Code wuerde ich aber begin und end zu einem Range Objekt zusammen fassen.

    getModulesBegin() liefert einfach einen module_iterator<IModule>. Dieser speichert sich den passenden vector<IModule*>::iterator, castet aber immer passend:

    template<typename ModuleType>
    class module_iterator {
    private:
      vector<IModule*>::iterator iter;
    public:
      template<typename OtherModuleType>
      module_iterator(module_iterator<OtherModuleType> const& other)
      : iter(other.iter) {
      }
    
      ModuleType& operator*() {
        return *static_cast<ModuleType>(*iter);
      }
      //...
    };
    

    Wir casten somit bei jedem Zugriff automatisch, so dass der Client Code ganz normal aussieht.

    Der Trick dabei ist der template Konstruktor. Er erlaubt es uns module_iterator<IModule> in module_iterator<CModule> umzuwandeln. Idealerweise kann man hier noch sanity checks machen OtherModuleType und ModuleType zueinander passen.

    Es erlaubt uns folgendes zu schreiben:

    CScript script;
    C\1::iterator begin, end;
    //in CScript gibts einfach ein typedef von module_iterator<CModule> auf iterator
    begin=script.getModuleBegin();
    end=script.getModuleEnd();
    while(begin!=end) {
      begin->something();
      ++begin;
    }
    

    Das waere das was ich machen wuerde.



  • Danke, funktioniert so 1a.

    Jedoch das mit dem GetMainModule nicht.
    Du schriebst:

    CModule und IModule (btw furchtbare Prefixe) sind miteinander verwandet. Das heisst du kannst in IScript IModule* returnen und in CScript CModule* -> das faellt unter covarianz.

    Weiterhin kommt aber dieser Fehler:

    error C2555: 'CScript::GetMainModule': Der überschreibende virtuelle Funktionsrückgabetyp unterscheidet sich und ist keine 'covariant' von 'IScript::GetMainModule'
      -> Siehe Deklaration von 'IScript::GetMainModule'
    

    Und das obwohl CModule 100% von IModule abgeleitet wird. Ich kann mir das einfach nicht erklären?!?!

    Gruß



  • Kannst du den genauen Code zeigen der den Fehler produziert?

    PS:
    wenn es um meinen Code gehen sollte, du musst immer module_iterator<IModule> returnen. Du wandelst den nur im Client Code dann zu einem module_iterator<CModule> um.



  • Hi.
    Es handelt sich um diesen "kleinen" Abschnitt:

    class IScript
    {
    public:
        // ...
        virtual IModule *GetMainModule() const = 0; // entspricht Zeile 10 aus dem Fehler
    };
    
    class CScript : public IScript
    {
    public:
        // ...
        virtual CModule *GetMainModule() const // entspricht Zeile 22 aus dem Fehler
        {
            return this->m_pMainModule;
        }
    
    private:
        // ...
        CModule *m_pMainModule;
    };
    

    Hier noch der Code von CModule und IModule:

    class IModule
    {
    public:
        // ...
    };
    
    class CModule : public IModule
    {
    public:
        // ...
    
    private:
        // ...
    };
    

    Der genaue Fehler ist:

    1><Pfad>\CScript.hpp(22) : error C2555: 'CScript::GetMainModule': Der überschreibende virtuelle Funktionsrückgabetyp unterscheidet sich und ist keine 'covariant' von 'IScript::GetMainModule'
    1>        <Pfad>\IScript.hpp(10): Siehe Deklaration von 'IScript::GetMainModule'
    

    Gruß



  • Du hast alles richtig gemacht, es ist aber ein Bug im Compiler:
    http://support.microsoft.com/kb/240862



  • Oh man, das heißt, dass ich das nicht fixxen kann?
    Nervt ja übelst.

    Aber da steht gar nichts von MS VC++ 2008, nur von älteren Versionen. Komisch...

    Gruß und Danke



  • theliquidwave schrieb:

    Oh man, das heißt, dass ich das nicht fixxen kann?
    Nervt ja übelst.

    Du kannst entweder auf die Covarianz verzichten oder aber eine template Funktion verwenden um den Cast zu automatisieren.

    Aber da steht gar nichts von MS VC++ 2008, nur von älteren Versionen. Komisch...

    Keine Ahnung. Dein Code ist korrekt, mehr kann ich nicht sagen.



  • Hi...
    Mir ist gerade aufgefallen, dass genau das gleiche Problem an einer anderen Stelle nicht auftritt:

    class IScript
    {
    public:
        // ...
    
    private:
        // ...
    };
    
    class CScript : public IScript
    {
    public:
        // ...
    
    private:
        // ...
    };
    
    class IModule
    {
    public:
        virtual IScript *GetScript() const = 0;
        // ...
    };
    
    class CModule : public IModule
    {
    public:
        virtual CScript *GetScript() const; // hier kracht gar nichts
        // ...
    
    private:
        CScript *m_pScript;
    };
    

    Theoretisch müsste es dort doch genau so krachen, tut es aber nicht o_O
    Woran könnte das liegen? Mir fällt dazu echt nichts mehr ein...

    Gruß


  • Administrator

    Ich denke, dass ich dein Problem kenne. Allerdings warst du wahrscheinlich nicht ganz ehrlich mit uns. Dein tatsächlicher Code sieht nämlich so aus:

    class IModule 
    { 
    public: 
        // ... 
    }; 
    
    // ... 
    
    class IScript 
    { 
    public: 
        // ... 
        virtual IModule *GetMainModule() const = 0;
    }; 
    
    // ... 
    
    class CModule; // Vorwärtsdeklaration
    
    class CScript : public IScript 
    { 
    public: 
        // ... 
        virtual CModule *GetMainModule() const;
    
    private: 
        // ... 
        CModule *m_pMainModule;
    };
    
    // ...
    
    class CModule : public IModule 
    { 
    public: 
        // ... 
    
    private: 
        // ... 
    };
    

    Für die Kovarianz reicht eine Vorwärtsdeklaration nicht, weil der Kompiler dann nicht erkennen kann, dass CModule von IModule erbt.

    Grüssli



  • Stimmt -.-
    Habe ich echt total vergessen, bitte vergebt mir 😃

    Komisch ist aber, dass ich IModule, CModule, CScript und IScript ebenfalls "vorwärts" deklariere.
    Eine Vorwärtsdeklaration wie class CModule : public IModule; funktioniert nicht.

    Gibt es dafür andere Lösungen? Denn ohne Vorwärts-Deklaration würde das ganze nicht mehr funktionieren.
    Entweder das ist ein Design-Fehler meinerseits, oder es geht einfach nicht anders (ich vermute ersteres), hätte aber auch keine Idee, wie ich es anders lösen könnte.

    Gruß



  • Dravere schrieb:

    Dein tatsächlicher Code sieht nämlich so aus

    👍 Nice one
    Den Fehler wuerde ich ewig suchen.

    @theliquidwave:
    Warum brauchst du denn die Gegenseitige Forward Deklaration?
    Fuer die Covarianz reicht es ja wenn CModule in CScript bekannt ist - in den anderen Dateien kannst du ja immernoch Forwarden.

    uU reicht ein pimpl-Idiom an anderer Stelle aus um die notwendigen Forwards zu reduzieren?



  • Das würde nicht funktionieren, da ich in den Headerdateien bereits CModule deklarieren muss, da der Compiler sonst meckert. Es würde also auch nichts bringen die Deklaration in die C++-Datei zu packen 😕

    Mit pimpl-Idiom meinst du "Opaque pointers"? Also so wie ich das verstanden habe, das einfache benutzen von void* anstatt CModule* in den Headerdateien? Dann könnte ich auch gleich IModule* benutzen.

    Gruß



  • theliquidwave schrieb:

    Das würde nicht funktionieren, da ich in den Headerdateien bereits CModule deklarieren muss, da der Compiler sonst meckert. Es würde also auch nichts bringen die Deklaration in die C++-Datei zu packen 😕

    Ich glaube da reden wir aneinander vorbei.

    Mit pimpl-Idiom meinst du "Opaque pointers"? Also so wie ich das verstanden habe, das einfache benutzen von void* anstatt CModule* in den Headerdateien? Dann könnte ich auch gleich IModule* benutzen.

    Nein. Du versteckst mit PImpl lediglich die Implementierung und die privaten Daten. Der Punkt aber ist: du versteckst die privaten Daten.

    Warum braucht CModule eine Forward deklaration fuer CScript? zB weil CModule einen Zeiger auf das CScript besitzt dem es gehoert. Mit PImpl wuerdest du nun diesen Zeiger auslagern in die Implementations Datei und somit kein Forward mehr fuer CScript in der normalen cmodule.hpp Datei brauchen.

    Es kann natuerlich andere Grunde geben warum CModule CScript kennen muss. Wenn zB CScript direkt in der Interfacedefinition von CModule vorkommt ist das natuerlich unpraktikabel. Deshalb war ja die Frage: warum muss CModule CScript schon in der *.hpp Datei kennen.



  • Naja, damit eben der Compiler CModule kennt. Das liegt daran, dass mehrer Headerdateien sich gegenseitig includen; naja. Wenn ich die Includereihenfolge verändere (also Module vor Script include), dann kommt der Fehler den ich ganz oben auf dieser Seite eigentlich erwartet hätte, dieser Fehler wäre aber weg. Ein Katz und Maus Spiel 😕

    Gruß


Anmelden zum Antworten