Klammerausdrücke in Templates


  • Mod

    Sone schrieb:

    Andererseits wuerde ich hier sowieso copy-initialisation vorziehen.

    Bei einer vernünftig designed Klasse ist der entsprechende Konstruktor aber möglicherweise explicit, dann geht es in C++03 nur mit Klammern. In C++11 kann man auch geschweifte Klammern einsetzen bei dem defaultkonstruierten Objekt, macht man das nur bei der anderen Klasse erwischt man evtl. einen initializer_list-Konstruktor, den man nicht aufrufen will.

    Das ist jedenfalls einer der wenigen Fehler, bei denen ich bei der Fehlersuche beim ersten mal lange gebraucht und geflucht habe (trotz des abstrakten Wissens darum). Die Fehlermeldung tritt nämlich erst später auf und scheint völlig sinnlos zu sein. Die Lösung ist recht einfach: eine zusätzliche Klammer um das Argument, siehe auch C++11_Noobs Post, der 2. Fall). Typdeklarationen haben nie eine sie ganz umschließende Klammer (andernfalls wäre die Grammatik mehrdeutig, man denke an sizeof(T) oder (T)x ).



  • Weil's gerade passt:

    Wie kann ich es denn umsetzen create dynamisch richtig aufzurufen? Ich habe gerade eine Methode, die eine freie Funktion erwartet, deren Parameter unterschiedlich sein können. Ich würde die Funktion natürlich gerne etwa so aufrufen:

    void foo(int a, int b)
    {
    // ...
    }
    
    void foo2(const std::string& a, Class& someRef)
    {
    // ...
    }
    
    // Aufrufe
    method(foo, 10, 20);
    method(foo2, "hallo", classInstance);
    

    Hilft mir da boost::function weiter? Irgendwas Spezielles, wonach ich suchen kann?



  • Wenn ich verstanden habe was du suchst: variadic templates.

    Hab ich es verstanden?



  • Ups. Ja, ich hab die Dinger unterschätzt.

    Ich probiere Mal mein Glück! Danke!

    Edit: Sollte ich das dennoch mit boost::function oder so kombinieren statt einen rohen Func* zu übergeben?

    Edit2: Och, jetzt hab ich gerade keine variadic templates, wie löse ich das denn dann? 😞



  • Hier mal von meiner Seite nen kleines Beispiel mit der sog. "Preffered Syntax", wie sie auch in der Boost-Bibliothek verwendet wird, und variadic Templates (C++0x/C++11)

    template<typename TSignature> class delegate;
    template<typename R, typename ... Args>
    class delegate<R(Args...)>
    {
        // Zugriff über R und Args...
    }
    

    bei

    delegate<int (char*)>
    

    wird mit R dann auf den int zugegriffen, mit Args... auf char*

    Edit:
    Wenn es dir um Performance geht, dann vergiss Boost-Funktion und Boost-Signal am besten wieder ganz schnell 😉

    Greets



  • Danke, Cyres! Und wenn man das jetzt so erweitern würde, dass man einen tatsächlichen Methodenaufruf mit Args machen würde?

    public:
        void callFunction(R* foo, Args... args)
        {
            (*foo)(args);
        }
    

    So?

    Performance wäre mir nicht so wichtig, die Aufrufe finden nicht zeitkritisch statt. Wie könnte ich es denn wohl mit boost::Gedöns lösen? Bzw. wo sollte ich nachschlagen, einfach alles zu boost::function lesen? (die Edukativen unter euch rufen hier vermutlich direkt ja!)



  • Nein, das sind einfach nur ein Klassentemplate wie jedes andere auch. Nur die Art es zu übergeben ist eine Andere.

    typedef R (*TFunc)(Args...);
    TFunc funcPtr;
    
    delegate(TFunc funcToBind)
    	: funcPtr(funcToBind) {}
    
    R operator()(Args... args)
    {
        return (*funcPtr)(args...);
    }
    

    Der Code ist nicht getestet, aber das Prinzip sollte deutlich werden.

    Edit: Bzgl Boost:
    Highscore.de erklärt die Dinge wie ich finde sehr gut. Was du suchst ist unter 3.4 zu finden.
    Wenn du willst, kann ich dir mal meine eigene Implementation von delegates Posten.



  • Super, danke für den Link! Unter 3.4 steht function erklärt, aber es gibt kein Beispiel zu variablen Parametern. Ist die Syntax da einfach genau so wie bei C++11?

    Wenn Du Deine Implementierung schon hast und es nicht zu viel Aufwand macht, würde die mich sehr interessieren!



  • Hab ich sogar direkt da, da ich sie in meinem aktuellen Projekt brauche 😉

    Getestet unter GCC 4.7

    template<typename TSignature> class delegate;
    template<typename R, typename ... Args>
    class delegate<R(Args...)>
    {
    	class Dummy;
    
    	typedef R TRet;
    	typedef TRet (*TFuncPtr)(Args...);
    	typedef TRet (Dummy::*TMemPtr)(Args...);
    
    	Dummy* objPtr;
    	TMemPtr memPtr;
    	TFuncPtr funPtr;
    
    public:
    	typedef delegate<TRet(Args...)> type;
    
    	delegate()
    			: objPtr(nullptr)
    			, memPtr(nullptr)
    			, funPtr(nullptr) {}
    
    	template<class T>
    	delegate(T* objPtr, TRet (T::*funcPtr)(Args...))
    			: objPtr(reinterpret_cast<Dummy*>(objPtr))
    			, memPtr(reinterpret_cast<TMemPtr>(funcPtr))
    			, funPtr(nullptr) {}
    
    	template<class T>
    	delegate(const T* objPtr, TRet (T::*funcPtr)(Args...) const)
    			: objPtr(reinterpret_cast<Dummy*>(const_cast<T*>(objPtr)))
    			, memPtr(reinterpret_cast<TMemPtr>(funcPtr))
    			, funPtr(nullptr) {}
    
    	delegate(TRet(*funcPtr)(Args...))
    			: objPtr(reinterpret_cast<Dummy*>(this))
    			, memPtr(reinterpret_cast<TMemPtr>(&delegate::invokeStatic))
    			, funPtr(reinterpret_cast<TFuncPtr>(funcPtr)) {}
    
    	TRet operator()(Args... args) const
    	{
    		return (objPtr->*reinterpret_cast<TMemPtr>(memPtr))(args...);
    	}
    
    private:
    	TRet invokeStatic(Args ... args) const
    	{
    		return (*(reinterpret_cast<TFuncPtr>(funPtr)))(args...);
    	}
    };
    

    Die compare-operatoren kannst ja selber dazuschreiben (memcmp ist wohl die beste Möglichkeit)



  • Wow, danke. Das sieht sehr interessant aus! Ich werde es so zwar nicht einsetzen müssen, aber das ist ein guter Referenzcode, wenn man sich dazu was fragt.



  • Diese Implementierung mag zwar funktionieren, ist aber alles andereals Standardkonform. So wie ich MSVC kenne, koennte ich mir durchaus vorstellen, dass das dort crasht.

    Ein moeglicher Ansatz ist, den Funktor-Typen ueber Type-Erasure loszuwerden. Z.B.:

    template <typename F>
    struct function;
    
    template <typename R, typename... P>
    struct function<R(P...)>
    {
        struct functor_wrapper
        {
            virtual R operator () (P...) = 0;
            virtual ~functor_wrapper() {}
        };
    
        template <typename F>
        struct functor : functor_wrapper
        {
            functor(F f)
                : f_(std::move(f))
            {}
    
            virtual R operator () (P... p)
            {
                return f_(p...);
            }
    
        private:
            F f_;
        };
    
        template <typename F>
        function(F f)
            : functor_(new functor<F>(std::move(f)))
        {}
    
        R operator () (P... p)
        {
            return (*functor_)(p...);
        }
    
    private:
        functor_wrapper* functor_;
    };
    

    Offensichtlicher Nachteil: Heapallokation sowie virtual function-call erforderlich, kein perfect forwarding.
    Damit lassen sich bereits beliebige Funktoren sowie Funktionszeiger verwenden. Das Implementieren von Memberfunktionen ueber bind sei den Lesern als Uebung ueberlassen 😉


Anmelden zum Antworten