template argument deduction bei komplexen templates möglich?



  • also, ich hab hier ein relativ komplexes funktionstemplate, bei dem ich die type deduction nicht hinkrieg. Ich hab inzwischen fast das Gefühl, dass das nicht geht, aber vielleicht hat jemand eine idee.

    Es geht darum, einen functor korrekt zu typisieren.

    folgender code funktioniert:

    //das Observer template ist sehr komplex,es sucht aus einer typliste für
    //ein event ein passendes argument tuple raus. das ergebnis ist dann zb tuple<int,int>
    template<Event event,class ClosureData>
    void addObserver(std::tr1::function<void(Object<StringT>*,typename Observer::template Get<event>::Tuple&,ClosureData&)> functor)
    {...
    }
    

    während event angegeben werden muss, wird ClosureData richtig erkannt. Diese Lösung ist zwar richtig, aber eigentlich ist Observer dafür zuständig, die functor typen komplett zusammenzusetzen. Ohne den zweiten template parameter klappt das auch sehr gut:

    //das ist dieselbe funktion ohne den ClosureData parameter. hierbei kann der komplette typ über Observer zusammengebastelt werden.
    template<Event event>
    void addObserver(const typename Observer::template Get<event>::Functor& functor)
    {
       ...
    }
    

    allerdings bekomm ich keine Lösung hin, bei der der zweite template parameter nicht explizit beim funktionsaufruf mit angegeben werden muss:

    //so sah mein ansatz aus:
    ...
    template<Event event>
    struct Get
    {
        ....
        typedef .... Tuple;
        template<class ClosureData>
        struct ClosureFunctor
        {
            typedef std::tr1::function<Object<StringT>*,Tuple&,ClosureData&> Functor
        };
    };
    template<Event event,class ClosureData>
    void addObserver(const typename Observer::template Get<event>::template ClosureFunctor<ClosureData>::Functor& functor)
    {
        ...
    }
    

    die Lösung ist zwar nicht unbedingt schöner als das von hand zusammensetzen, allerdings brauch ich das an mehreren stellen und wenn ich mal was am Functor ändern muss, dann is es mir lieber das alles am selben Ort zu haben 😉



  • Ich kann dir zwar bei deinem Problem nicht helfen, habe aber dennoch eine Frage.

    otze schrieb:

    template<Event event,class ClosureData>
    void addObserver(const typename Observer::template Get<event>::template ClosureFunctor<ClosureData>::Functor& functor)
    {
        ...
    }
    

    Sollte das nicht so aussehen:

    template<Event event,class ClosureData>
    void addObserver(const typename Observer::template typename Get<event>::template typename ClosureFunctor<ClosureData>::Functor& functor)
    {
        ...
    }
    


  • das erste typename reicht, da danach klar ist, dass das was am ende rauskommt ein typ sein muss.



  • Achso, danke.


  • Mod

    Dieses

    typename Observer::template Get<event>::template ClosureFunctor<ClosureData>::Functor
    

    Ist ja kein deduzierbarer Kontext und ohne Kenntnis der betreffenden Templates auch nicht eindeutig. Eine Möglichkeit, die ich sehe, wäre eine Templatemetafunktion zu schreiben, die das leistet, was du durch Argumentdeduktion erreichen willst:

    template<Event event>
    struct Get
    {
        ....
        typedef .... Tuple;
        template<class ClosureData>
        struct ClosureFunctor
        {
            typedef std::tr1::function<Object<StringT>*,Tuple&,ClosureData&> Functor
        };
    };
    template<typename Functor> struct ArgumentsFromFunctor;
    template<typename TupleT, typename ClosureDateT> struct ArgumentsFromFunctor<std::tr1::function<Object<StringT>*,Tuple&,ClosureData&> >
    {
        typedef TupleT Tuple;
        typedef ClosureDataT ClosureData;
    };
    template<typename TupleT> struct EventFromTuple;
    template<...> struct EventFromTuple<...>
    {
        // Irgendetwas, dass aus der typedef-Dekaration in Get auf das Templateargument schließt
    }
    
    template<typename Functor>
    void addObserver(const Functor& functor)
    {
        typedef typename ArgumentsFromFunctor<Functor>::Tuple Tuple;
        typedef typename ArgumentsFromFunctor<Functor>::ClosureData ClosureData;
        static const Event event = EventFromTuple<Tuple>::Event;
        ...
    }
    


  • //edit

    Ahh, ich glaube du hast mich falsch verstanden. der event paramter muss sein, da ich die funktion mithilfe des events aussuche. das einzige Problem ist der 3. parameter der funktion der trotz allen versuchen nicht schön herleitbar ist.

    generell ist zwar die idee, das ganze mit ner Klasse zu lösen nicht schlecht, allerdigns ist mir vohin dieselbe idee gekommen, und da hab ich gemerkt, dass ich bereits was eingebaut hab, was dieser Lösung entgegenspricht:

    //in derselben Klasse wie die funktionen von oben:
    protected:
    
    template<Event event,class Class>
    void addObserver(void(Class::*pointer)(const typename Observer::template Get<event>::Tuple&))
    {
         observer.addObserver<event>(pointer);
    }
    

    Die funktion darf auch nicht public sein, weil sonst auch fremde nicht von dieser Klasse abgleitete Objekte ihre methoden da reinschreiben könnten. Und das geht dann an dem Punkt schief, wo der übergebene this pointer der aufrufenden Klasse in diesen Typ umgewandelt werden würde.

    Ich glaub, man kann das so garnicht mehr schön lösen.


  • Mod

    otze schrieb:

    Die funktion darf auch nicht public sein, weil sonst auch fremde nicht von dieser Klasse abgleitete Objekte ihre methoden da reinschreiben könnten. Und das geht dann an dem Punkt schief, wo der übergebene this pointer der aufrufenden Klasse in diesen Typ umgewandelt werden würde.

    Das ist mir zu kompliziert, kannst du das ein bisschen mit Code illustrieren?



  • hab oben nochmal editiert. aber ich kanns dir zeigen:

    template<Event event,class Class>
    void addObserver(void(Class::*pointer)(const typename Observer::template Get<event>::Tuple&))
    {
         observer.addObserver<event>(pointer);
    }
    //ruft über umwege das auf:
    template<class Class>
    void addObserver(void(Class::*function)(Tuple))
    {
        Functor newFunctor=std::tr1::bind(functionWrapper<Class>,function,std::tr1::placeholders::_1,std::tr1::placeholders::_2);
    }
    template<class Class>
    static void functionWrapper(void(Class::*function)(Tuple),Object<StringT>* pointer,const Tuple& tuple)
    {
        (static_cast<Class*>(pointer)->*function)(tuple);//da is der cast
    }
    
    //und so wird die funktion in Object aufgerufen:
    template<Event event>
    void sendEvent(const typename Observer::template Get<event>::Tuple& tuple)
    {
        ....
        observer.template call<event>(this,tuple);
    }
    


  • problem gelöst. Ich hab das ganze deutlich umgebastelt und kann nun observer "einfach" vererben, und damit entfällt das ganze problem, da sich observer ja um den ganzen Kram kümmert. bin unglaublich zufrieden mit der Lösung, zumal das das erste mal war, dass ich typlisten(bzw komplexe tuple) und metatemplate programmierung sinnvoll eingesetzt hab 🙂

    danke an alle die sich das mal angeschaut haben.


Anmelden zum Antworten