Name für Basisklasse + Typedef gesucht



  • hustbaer schrieb:

    Es war auf jeden Fall nicht höflich.Etwas als "sinnlos" zu bezeichnen, was man (offensichtlich) nicht verstanden hat, ist nicht höflich. Höflich wäre gewesen nachzufragen.

    Nicht nicht verstanden - vergessen. Das ist ein wesentlicher Unterschied. 😉

    hustbaer schrieb:

    Da du das, in deiner immerwährenden Selbstüberschätzung (aus der die Sicherheit erwächst etwas verstanden zu haben, wenn du es eben nicht verstanden hast), sehr häufig machst, reagiere ich darauf etwas genervt.

    Im Nachhinein betrachtet hätte ich wohl auch nicht anders reagiert - ich vergebe dir.

    hustbaer schrieb:

    Punkt 2 ist weiterhin eine Frage der Konvention, ein absolutes Statemend dazu ist dementsprechend weiterhin Blödsinn.

    Und diese Konvention hat sich weitestgehend durchgesetzt.



  • BTW: da das Thema nun schon aufgegriffen wurde: findet ihr die Verwendung einer solchen Basisklasse + typedef darin auch schlecht/fragwürdig?

    Man könnte das natürlich auch alles anders lösen, ganz ohne die "Zwischen-Basisklasse", nur gefallen mir alle anderen Lösungen (die mir eingefallen sind) noch weniger.

    Speziell was das Thema shared_from_this() angeht.

    Immer Casten wo der abgeleitete Type gebraucht wird finde ich nicht gut.
    Es in jeder abgeleiteten Klasse zu wiederholen finde ich auch nicht gut.
    Bliebe noch die Möglichkeit ein Makro zu verwenden, aber ... naja. Den "Aufruf" des Makros müsste man auch in jeder Klasse wiederholen, und ... hmpf. Gefällt mir also auch nicht so richtig.

    Das einzige was mich bei der "Zwischen-Basisklasse" stört, ist, dass diverse Tools wie Doxygen sich damit nicht so richtig anfreunden können. Eigentlich sollte die "Zwischen-Basisklasse" in Doxygen Charts gar nicht auftauchen, sondern direkt der "BASE" Typ als Basisklasse. Dazu konnte ich Doxygen aber blöderweise nicht überreden.



  • @314159265358979:
    Ja ja, wie du meinst.



  • Zeus schrieb:

    Default, Default, Default, ... 🤡

    Default klingt für mich, im Zusammenhang mit C++, immer nach etwas, was man bekommt wenn man irgendwas nicht angibt.
    Default-Ctor (keine Parameter angegeben), Default-Argument (kein Argument angegeben), Default-Implementierung (keine Implementierung angegeben, mit SpecialFunction() = default).
    Das passt in dem Fall IMO nicht gut.

    Standard passt IMO besser. Standard heisst für mich ... naja, dass es eben "Standard" ist, aber man es auch anders machen kann (wenn es gute Gründe dafür gibt).



  • hustbaer schrieb:

    Zeus schrieb:

    Default, Default, Default, ... 🤡

    ...

    Das war ein Joke von einen Java-Programmierer 😛



  • Zeus schrieb:

    hustbaer schrieb:

    Zeus schrieb:

    Default, Default, Default, ... 🤡

    ...

    Das war ein Joke von einen Java-Programmierer 😛

    Den verstehe ich nicht 😕
    🙂



  • hustbaer schrieb:

    BTW: da das Thema nun schon aufgegriffen wurde: findet ihr die Verwendung einer solchen Basisklasse + typedef darin auch schlecht/fragwürdig?

    Es ist immerhin eine weitere Abtraktion, die ich erst lernen muß. Ich hasse Abstraktionen, die sich nicht aus den Begriffen der Programmlogik ergeben.
    Wenn ich recht sehe, willst Du nur...

    class HappyFooControl : public FooControl
    {
    public:
        typedef FooControl Base;//...nur diese kleine Zeile automagisieren
    
        using Base::SomeFunction();
    
        virtual void SomeOverride()
        {
            Base::SomeOverride();
            Whatever();
        }
    };
    


  • Offtopic for only one, hustbear - a litte pattern:

    public interface AbilityOne { void one(); }
    public interface AbilityTwo { void two(); }
    
    public class DefaultOne implements AbilityOne {
        @Override
        public void one() {
            System.out.println("rly i am meaningful");
        }
    }
    
    public class DefaultTwo implements AbilityTwo {
        @Override
        public void two() {
            System.out.println("sure i am meaningful");        
        }
    }
    
    public abstract class AbstractMeaningful implements AbilityOne, AbilityTwo {
    
    }
    
    public class DefaultMeaningful extends AbstractMeaningful {
    
        private AbilityOne one = new DefaultOne();
        private AbilityTwo two = new DefaultTwo();
    
        @Override
        public void one() {
            one.one();
        }
    
        @Override
        public void two() {
            two.two();
        }
    
    }
    


  • hustbaer schrieb:

    Bfindet ihr die Verwendung einer solchen Basisklasse + typedef darin auch schlecht/fragwürdig?

    Man könnte das natürlich auch alles anders lösen, ganz ohne die "Zwischen-Basisklasse", nur gefallen mir alle anderen Lösungen (die mir eingefallen sind) noch weniger.

    Ich find's ok. Nicht optimal, aber man schreibt idR selten eigene Controls, da ist kurze Einarbeitungszeit in Ordnung. Anwesenheit des typedefs ist ok wenn man ohne Nachschlagen/Intellisense sieht, dass eben jenes Parent damit gemeint ist.

    Rein aus Interesse: Woran liegt es denn, dass man nicht einfach nur von direkt Control ableiten kann? Oder spezifische Hilfsfunktionen?

    hustbaer schrieb:

    Speziell was das Thema shared_from_this() angeht.

    Das "spezialisieren" von shared_from_this finde ich eine coole Idee, hab ich so noch nicht gesehen.



  • So mal C++ 🙂

    template<class T> class ControlUtility {
    protected:
    	explicit ControlUtility(T* base_) : base(base_) {
    	}
    private: 
         T* base;
    };
    
    class FooControl : public Control, private ControlUtility<FooControl>
    {
    public:
    	using ControlUtility<FooControl>::SomeFunction();
    
    	virtual void SomeOverride() 
        { 
            ControlUtility<FooControl>::SomeOverride(); 
            Whatever(); 
        } 	
    };
    


  • volkard schrieb:

    Wenn ich recht sehe, willst Du nur...

    typedef FooControl Base;//...nur diese kleine Zeile automagisieren
    

    Nicht nur, ich will:

    class HappyFooControl : public FooControl
    {
    public:
        // die folgenden 3 zeilen automagisieren
        typedef FooControl Base;
        shared_ptr<HappyFooControl> shared_from_this() { return static_pointer_cast<HappyFooControl>(Control::shared_from_this()); }
        shared_ptr<HappyFooControl const> shared_from_this() const { return static_pointer_cast<HappyFooControl const>(Control::shared_from_this()); }
        // Control::shared_from_this() statt Base::shared_from_this() hier deswegen,
        // damit bei tieferen Hierarchien nicht verschachtelt static_pointer_cast() aufgerufen werden muss,
        // da der Compiler die Aufrufe leider nicht wegoptimieren kann, und Interlocked-Calls teuer sind
    
        using Base::SomeFunction();
    
        virtual void PostConstructor()
        {
            Base::PostConstructor();
            ConnectToEvent(someEvent, &HappyFooControl::FunFunFun, shared_from_this());
        }
    
    private:
        void FunFunFun(EventData& eventData);
    };
    

    Hast du nen Vorschlag (ausser die 3 Zeilen in jede Control-Klasse reinzuschreiben)?

    volkard schrieb:

    Es ist immerhin eine weitere Abtraktion, die ich erst lernen muß. Ich hasse Abstraktionen, die sich nicht aus den Begriffen der Programmlogik ergeben.

    Ja, ich normalerweise auch.



  • fdfdg schrieb:

    Rein aus Interesse: Woran liegt es denn, dass man nicht einfach nur von direkt Control ableiten kann? Oder spezifische Hilfsfunktionen?

    Man kann durchaus direkt ableiten. Nur tu' ich es nicht, weil ich es mit der Zwischenbasisklasse praktischer finde.

    Das selbe würde sich mit einem Makro erreichen lassen:

    #define CONTROL_STUFF_IMPL(DERIVED, BASE) \
        public: \
        typedef BASE Base; \
    	BOOST_STATIC_ASSERT(boost::is_base_and_derived<BASE, DERIVED>::value); \
    	BOOST_STATIC_ASSERT(boost::is_base_and_derived<Control, DERIVED>::value); \
        shared_ptr<DERIVED> shared_from_this() \
        { \
    		return static_pointer_cast<DERIVED>(Control::shared_from_this()); \
    	} \
        shared_ptr<DERIVED const> shared_from_this() const \
        { \
    		return static_pointer_cast<DERIVED const>(Control::shared_from_this()); \
        } // end of CONTROL_STUFF_IMPL()
    

    Und Verwendung:

    class HappyFooControl : public FooControl
    {
        CONTROL_STUFF_IMPL(HappyFooControl, FooControl);
    public:
    
        using Base::SomeFunction();
    
        virtual void PostConstructor()
        {
            Base::PostConstructor();
            ConnectToEvent(someEvent, &HappyFooControl::FunFunFun, shared_from_this());
        }
    
    private:
        void FunFunFun(EventData& eventData);
    };
    

    hustbaer schrieb:

    Speziell was das Thema shared_from_this() angeht.

    Das "spezialisieren" von shared_from_this finde ich eine coole Idee, hab ich so noch nicht gesehen.[/quote]
    Ich hab' in einigen Projekten sowas gesehen:

    class Control : public enable_shared_from_this<Control>
    {
    public:
        template <class DERIVED>
        shared_ptr<DERIVED> shared_from_this()
        {
            BOOST_STATIC_ASSERT(boost::is_base_and_derived<Control, DERIVED>::value);
            return static_pointer_cast<DERIVED>(enable_shared_from_this<Control>::shared_from_this());
        }
        // + const overload
    };
    

    Dummerweie schützt einen das nicht davor:

    void SomeValidControlType::PostConstructor()
    {
        Connect(..., shared_from_this<AnotherValidControlType>());
    }
    

    Natürlich kann man in Control::shared_from_this<T> nen Runtime-Assert über dynamic_cast reinmachen. Bloss in wenig befahrenen Code-Zweigen kanns schonmal passieren, dass der beim Testen nie triggert.



  • class Control
    {
        private:
        shared_ptr<Control> shared_from_control()
        {//KA, was Control::shared_from_this vorher machte
        }
        public:
        template<typename DERIVED>
        static shared_ptr<DERIVED> shared_from(DERIVED* d)//Ich benutze den Typ von this
        {
            return static_pointer_cast<DERIVED>(d->shared_from_control());
        }
    };
    
    class HappyFooControl : public FooControl
    {
    public:
        typedef FooControl Base;//jetzt nur noch private Bequemlichkeit
    
        virtual void PostConstructor()
        {
            Base::PostConstructor();
            ConnectToEvent(someEvent, &HappyFooControl::FunFunFun, shared_from(this));//eh netterer aufruf, oder?
        }
    


  • Hmja, das wäre ne Möglichkeit.

    eh netterer aufruf, oder?

    Nö, gefällt mir eigentlich nicht so gut.

    Hier zieht auch wieder das "zusätzliche Abstraktion" Argument: shared_from_this() ist die Signatur die die Leute kennen und erwarten, etwas anderes muss man erst wieder lernen/kennen.
    Obwohl ich zugeben muss dass es weniger "versteckt" ist als das automagische Vorhandensein von shared_from_this() mit dem passenden Returntyp.

    Muss ich mal, wenn ich Musse habe, die Vor- und Nachteile der beiden Varianten gegeneinander abwägen. Das "Doxygen-Problem" wäre dadurch natürlich gelöst, und das fände ich nicht schlecht. Hmmm...

    🙂



  • shared_from_this() aus boost kannte ich nicht. Schade, daß es dort nicht schon shared_from(this) heißt.

    Ich würde trotzdem ungern eine Zwischenklasse in die Vererbungshierarchie einhängen, nur um einen kleinen Trick abzuziehen, der nicht soo wichtig ist. Das Makro wäre ja auch keine Schande.

    Also nicht

    class HappyFooControl : public ControlProxyEnableSharedFromThis<HappyFooControl, FooControl>
    

    wo gemeint ist

    class HappyFooControl : public FooControl
    

    Vielleicht wäre das genehm?

    template<typename BASE,typename DERIVED>
    class ControlMixinEnableSharedFromThis
    {
        typedef BASE Base;
        shared_ptr<DERIVED> shared_from_this() {
            return static_pointer_cast<DERIVED>(static_cast<DERIVED*>(this)->Control::shared_from_this());
        }
    };
    class HappyFooControl : public FooControl, ControlMixinEnableSharedFromThis<HappyFooControl,FooControl>
    {
    


  • Auch nicht doof 🙂

    Wobei... wird das nicht ambiguous? Muss ich morgen ausprobieren.



  • hustbaer schrieb:

    Auch nicht doof 🙂
    Wobei... wird das nicht ambiguous? Muss ich morgen ausprobieren.

    Es wird ambig.



  • lol, dann kommen wir heute zu einem sehr sehr seltenen Gast, der virtuellen Vererbung. Oder mein Compiler ist nur müde geworden und ist zu erschöpft, mir noch zu widersprechen.

    #include <iostream>
    #include <boost/shared_ptr.hpp>
    #include <boost/enable_shared_from_this.hpp>
    using namespace boost;
    
    class Control:virtual public enable_shared_from_this<Control>
    {
    };
    
    template<typename DERIVED>
    struct EnableSharedFromThisControlMixin:virtual public enable_shared_from_this<Control>
    {
        shared_ptr<DERIVED> shared_from_this()
        {
            return static_pointer_cast<DERIVED>(enable_shared_from_this<Control>::shared_from_this());
        }
    };
    
    class FooControl : public Control
    {
    public:
        void SomeFunction()
        {
        }
    };
    
    class HappyFooControl : public FooControl, public EnableSharedFromThisControlMixin<HappyFooControl>
    {
    public:
        typedef FooControl Base;
    
        using Base::SomeFunction;
    
        static void test(shared_ptr<HappyFooControl>)
        {
            std::cout<<"sphfc\n";
        }
    
        virtual void PostConstructor()
        {
            test(shared_from_this());
        }
    
    private:
        void FunFunFun(int& eventData);
    };
    
    int main()
    {
        shared_ptr<HappyFooControl> hfc(new HappyFooControl);
        hfc->PostConstructor();
    }
    


  • hustbaer schrieb:

    Man kann durchaus direkt ableiten. Nur tu' ich es nicht, weil ich es mit der Zwischenbasisklasse praktischer finde.

    Das selbe würde sich mit einem Makro erreichen lassen

    Auch wenn Makros per se nicht so schön sind, würde ich diese Lösung favorisieren; aber halt nur als einer von etlichen potentiellen Nutzern deiner Library (bin übrigens gespannt, hoffe du schaffst es das durchzuziehen!).

    Das typedef könnte man dann ja noch weglassen, statt Base:: dann OwnerDrawControl:: oder BaseControl:: o.Ä. zu schreiben, wäre ja in Ordnung und direkter ersichtlich. Nennt man das Makro dann noch z.B. CONTROL_IMPL_SHAREDFROMTHIS , ist das ganze IMO ein sehr, sehr verständliches Ding 🙂



  • Ja, das Typedef ist noch mehr als der shared_from_this() Override Bequemlichkeit.
    Viele GUI Controls enthalten viel ähnlichen Code, und wenn die Basisklasse überall über den selben Namen referenziert wird, muss man weniger anpassen, wenn man Code aus einer Klasse als Vorlage für Code einer neuen Klasse kopiert.
    Mag jetzt doof und kleinlich klingen, aber ich finde das schon recht praktisch.

    Und es hilft beim Refactoring, wenn man Klassen umbenennt. Wenn ich FooControl mal in QuuxControl umbenenne, dann muss ich an deutlich weniger Stellen was ändern, wenn abgeleitete Klassen nur an jeweils 1-2 Stellen "FooControl" stehen haben, und die restlichen Stellen einfach "Base::" verwenden.
    Obwohl das mit Visual Assist X schon recht schön automatisiert ist, aber auch der übersieht mal was.

    Bzw. auch wenn man ein Control "rebased", also das Basis-Control umstellt. Mach' ich zwar nicht oft, aber ist durchaus schon mehr als 1x vorgekommen.

    aber halt nur als einer von etlichen potentiellen Nutzern deiner Library (bin übrigens gespannt, hoffe du schaffst es das durchzuziehen!).

    Ich hoffe bloss du rechnest jetzt nicht mir irgendwas open-sourcigem - die Library mach' ich für meine Firma, da wird sich leider nix veröffentlichen lassen.


Anmelden zum Antworten