Probleme mit Templatemetaprogrammierung in verbindung mit std::tr1::tuple



  • Ich hab mir zwar vor einiger Zeit gesagt, dass ich die Template Metaprogrammierung nicht mehr anfassen werde, aber ich habs doch wieder getan.

    In diesem Fall brauch ich sie, um folgendes Problem lösen zu können:
    Ich hab einige Events, die an einige Observer eines Objekts gesendet werden.
    Da aber sogut wie jedes Event eigene Parameter mit übermitteln muss, hab ich das nun ein wenig...komplex gelöst, da ich die Typsicherheit nicht einschränken wollte.

    Für jedes Event gibt es nun einen "Caller", der die nötigen Typinformationen bekommt, um genau die richtigen Funktionen für diesen Zweck aufzunehmen.
    Als Parameter für die funktionen verwende ich entweder einen Tuple oder einen
    einfachen Typ, wenn das Event nur eine information übergeben will.

    Dann gibt es die Klasse Observer. Sie teilt den einzelnen Callern für das Event die funktoren zu. Dies funktioniert mit der Methode "addObserver". Aufgerufen wird die funktion so:addObserver<ControlActivate>(&Control::activate).
    Mithilfe des Events als template parameter kann ich nun alle Caller durchgehen, und nach der nötigen Typinformation suchen, in dem Fall, welchen Typ der Funktor hat. Dieses suchen erledigt die nested class Get von Observer.

    Das Problem ist nun folgendes: aus irgendeinem Grund findet er den richtigen Caller nicht, und läuft dann durch, bis die abbruchbedingung eintritt. Da dieser Caller aber da ist(Siehe EventTuple im codeanhang), muss irgendwo anders was schieflaufen. Ich konnte den fehler jetzt auch nach stundenlanger suche nicht finden, meine einzige Vermutung ist, dass der Fehler irgendwo bei tuple_element liegt, allerdings bin ich mit solchen aussagen inzwischen sehr vorsichtig, da es wesentlich wahrscheinlicher ist, dass ich wiedermal irgendeinen dummen flüchtigkeitsfehler gemacht hab, der euch sofort ins Auge springt 😉

    //die Enum Event fasst alle Events zusammen, die gesendet werden
    enum Event
    {
        MouseClick,
        MouseDoubleClick,
        ControlActivate
        //...
    };
    
    //Caller ist ein Objekt, dass die funktionen der Observer für ein event abspeichert
    //eventA gibt das event an, um das sich dieses CallerObjekt kümmert
    //TupleT gibt den Typ des Parametertuple an, das die aufgerufene funktion
    //übergeben bekommt
    //Functor stellt den Typ der Funktoren da, die Caller verwaltet.
    template<Event eventA,class TupleT>
    class Caller{
        public:
            typedef TupleT Tuple;
            typedef std::tr1::function<void(Object*,Tuple)> Functor;
            static const Event event=eventA;
        //...
    };
    
    //Das EventTuple
    typedef std::tr1::tuple<
        Caller<MouseClick,std::tr1::tuple<Button,int,int> >,
        Caller<MouseDoubleClick,std::tr1::tuple<Button,int,int> >,
        Caller<ControlActivate,bool>
        //...
    > EventTuple;
    
    //dem template Parameter wird immer das typedef EventTuple übergeben.
    template<class EventTuple>
    class Observer{
        private:
            EventTuple eventTuple;
        public:
            //Get geht das EventTuple durch und sucht nach dem Caller für das entsprechende event
            template<Event event,int i=std::tr1::tuple_size<EventTuple>::value-1 >
            class Get{
                private:
                    //Das Element an Position i im Tuple suchen
                    typedef typename std::tr1::tuple_element<i,EventTuple>::type Element;
    
                    //Test: ist das Element an position i das, das für das gesuchte
                    //      Event zuständig ist?
                    typedef typename  IF<
                        Element::event==event,
                        Element,//ja, das ist das gesuchte Element
                        Get<event,i-1>//nein, weiter suchen
                    >::Value Result;
                public:
                    typedef typename Result::Tuple Tuple;//parameterTuple
                    typedef typename Result::Functor Functor;//Functor Typ den Caller verwaltet
            };
            //Dies ist die Abbruchbedingung. Wenn der Compiler hierhin kommt, ist ein Fehler
            //passiert: das Event ist nicht in der Liste enthalten.
            template<Event event>
            Get<event,-1>{};
    
        public:
            //hier krachts
            //Fehlermeldung:no type named Tuple in class Observer<EventTuple>::Get<ControlActivate,-1>
            //der aufruf sieht so aus:
            //addObserver<ControlActivate>(&Control::activate);
            template<Event event>
            void addObserver(typename Get<event>::Functor functor);
    
            //...
    };
    


  • Hallo,
    mir kommt der else-Teil deines IFs eigenartig vor. Sollte da nicht ein
    typename Get<event,i-1>::Element stehen? Andernfalls ist im Fall Element::event!=event Result = Get<event,i-1> und Tuple = Result::Tuple = Get<event,i-1>::Tuple = Tuple, was natürlich nicht geht -> zirkulär.



  • HumeSikkins schrieb:

    Hallo,
    mir kommt der else-Teil deines IFs eigenartig vor. Sollte da nicht ein
    typename Get<event,i-1>::Element stehen? Andernfalls ist im Fall Element::event!=event Result = Get<event,i-1> und Tuple = Result::Tuple = Get<event,i-1>::Tuple = Tuple, was natürlich nicht geht -> zirkulär.

    hmm, müsste das dann nicht

    Tuple = Result::Tuple = Get<event,i-1>::Tuple = Get<event,i-1>::Result::Tuple sein? Und da result mit einem andren i berechnet wird, dürfte dort auch nix zirkulär sein, oder?



  • hmm, müsste das dann nicht

    Tuple = Result::Tuple = Get<event,i-1>::Tuple = Get<event,i-1>::Result::Tuple sein? Und da result mit einem andren i berechnet wird, dürfte dort auch nix zirkulär sein, oder?

    Hm, richtig. Denkfehler meinerseits.

    Was ist eigentlich das?

    //Dies ist die Abbruchbedingung. Wenn der Compiler hierhin kommt, ist ein Fehler
    //passiert: das Event ist nicht in der Liste enthalten.
    template<Event event>
    Get<event,-1>{};
    

    Da fehlt doch ein class.

    Btw: Mit welchem Compiler arbeitest du? Ich habe das Ganze jetzt mal mit dem VS 2005 probiert (dabei statt tr1, boost::function und boost::fusion::tuple verwendet) und da funktioniert das ohne Probleme. Zumindest mit freien Funktionen. Mir ist noch nicht ganz klar, wie ich von &Control::activate auf function<void(Object*,bool)> kommen soll. Ein function<void(Control*, bool)> ist ja klar...

    PS: was ich noch ändern musste: der VC 7.1 kommt mit dem Def-Paramter von Get nicht klar, weshalb der addObserver dort so aussehen muss:

    template<Event event>
    void addObserver(typename Get<event, boost::fusion::tuple_size<EventTuple>::value-1>::Functor functor);
    


  • Hallo,
    noch ne Frage: wozu brauchst du eigentlich das komplizierte Get-Template?

    Reicht nicht auch einfach ein simples:

    template<Event event>
    class Get {
    private:
      //Das Element an Position i im Tuple suchen
      typedef typename boost::fusion::tuple_element<event, EventTuple>::type Result;
    public:
      typedef typename Result::Tuple Tuple;//parameterTuple
      typedef typename Result::Functor Functor;//Functor Typ den Caller verwaltet
    };
    

    Oder übersehe ich hier jetzt was entscheidendes?



  • [quote="HumeSikkins"]

    Da fehlt doch ein class.

    Btw: Mit welchem Compiler arbeitest du? Ich habe das Ganze jetzt mal mit dem VS 2005 probiert (dabei statt tr1, boost::function und boost::fusion::tuple verwendet) und da funktioniert das ohne Probleme. Zumindest mit freien Funktionen. Mir ist noch nicht ganz klar, wie ich von &Control::activate auf function<void(Object*,bool)> kommen soll. Ein function<void(Control*, bool)> ist ja klar...

    *hust* beides Fehler meinerseits 😞

    das erste war ein flüchtigkeits kopierfehler, und das andre ist ein umbau fehler der mir entgangen ist.

    Der fehler liegt aber auch wohl an der lib die ich benutzt hab, hab ebenso "umgebaut" und es funktioniert jetzt auch.

    Hallo,
    noch ne Frage: wozu brauchst du eigentlich das komplizierte Get-Template?

    Reicht nicht auch einfach ein simples:

    template<Event event>
    class Get {
    private:
      //Das Element an Position i im Tuple suchen
      typedef typename boost::fusion::tuple_element<event, EventTuple>::type Result;
    public:
      typedef typename Result::Tuple Tuple;//parameterTuple
      typedef typename Result::Functor Functor;//Functor Typ den Caller verwaltet
    };
    

    was passiert, wenn EventTuple nicht so angeordnet ist, wie die enum? Aber eigentlich haste recht, wenn ich jetzt nichts aussergewöhnliches mache, wie unterschiedliche Eventlisten für verschiedene Objekte etc, dann brauch ich sowas nicht unbedingt. Ich denke wohl zuviel "was wäre wenn".


Log in to reply