Methoden Callbacks



  • Hallo,

    ich schreibe mir ein kleines UI System und will jetzt irgendwie für UI Controls Methoden registrieren lassen.
    Das Design sollte in etwa so aussehen:

    MyClass::onMouseClick(MouseEvent& e) {
       cout << "Clicked";
    }
    
    MyClass::method() {
       Button* b = new Button;
       b->registerClickHandler(this, MyClass::onMouseClick);
    }
    

    Wie könnte man mit C++ eine Methode wie registerClickHandler() implementieren?
    Danke!



  • Ich würde mit C++11 std::function benutzen:
    http://en.cppreference.com/w/cpp/utility/functional/function

    Für ältere Compiler gibt's das Gleiche bei boost.



  • Hier war schonmal eine ziemlich ähnliche Frage, die ich ausführlich beantwortet habe:
    http://www.c-plusplus.net/forum/p2167812#2167812



  • afaik arbeitet std::function mit dynamische alloziertem speicher, was die sache nicht gerade performant macht



  • bhatty schrieb:

    afaik arbeitet std::function mit dynamische alloziertem speicher, was die sache nicht gerade performant macht

    Solange du keine gemeinsame Basisklasse für alle möglichen Objekte hast, wirst du wohl nicht ohne dynamisch alloziiertem Speicher auskommen.
    Außerdem finde ich Performance-Bedenken bei Listenern schon ziemlich übertrieben.
    So viel langsamer ist es nämlich auch nicht.

    Auch wenn der folgende Code bei mir das gewünschte Ergebnis liefert, habe ich starke Bedenken, dass er standard-konform ist:

    #include <iostream> 
    #include <vector>
    
    struct Dummy{
    };
    
    struct Foo{
    	void print_i(){
    		std::cout << "i=" << i << std::endl;
    	}
    
    	int i;
    };
    
    struct Bar{
    	void print_j(){
    		std::cout << "j=" << j << std::endl;
    	}
    
    	int j;
    };
    
    int main(){
    	Foo foo;
    	foo.i = 3;
    	Bar bar;
    	bar.j = 5;
    	typedef void (Dummy::*dummy_ptr_type)();
    	std::vector<std::pair<Dummy*, dummy_ptr_type> > listeners;
    	listeners.push_back(std::make_pair(reinterpret_cast<Dummy*>(&foo), reinterpret_cast<dummy_ptr_type>(&Foo::print_i)));
    	listeners.push_back(std::make_pair(reinterpret_cast<Dummy*>(&bar), reinterpret_cast<dummy_ptr_type>(&Bar::print_j)));
    	for(std::vector<std::pair<Dummy*, dummy_ptr_type> >::iterator it = listeners.begin(); it!=listeners.end(); ++it){
    		Dummy* obj = it->first;
    		dummy_ptr_type ptr = it->second;
    		(obj->*ptr)();
    	}
    }
    

    i=3
    j=5

    Gruß,
    XSpille



  • bhatty schrieb:

    afaik arbeitet std::function mit dynamische alloziertem speicher, was die sache nicht gerade performant macht

    Jain. Eine gescheite Implementierung macht von der "small function optimization" gebrauch. Man kann von einer solchen Implementierung ruhig erwarten, kleine und trivial kopierbare Funktoren direkt im std::function<>-Objekt abzulegen, wie z.b. so etwas:

    std::function<void()> f = [this]{this->foo();};
    

    Da hat sich sogar mal Dave Abrahams intensiv mit beschäftigt. Er untersuchte, wieviel Overhead hier bei abfällt, wenn man std::function intelligent implementiert. Das Ergebnis: Der Overhead ist vernachlässigbar. Damit flog dann auch std::reference_closure<> aus dem Standard, was ursprünglich für Lambdas aus Performancegründen eingeführt wurde.

    (Angaben ohne Gewähr. Ich habe die Dinge nicht extra nochmal verfolgt, sondern verlasse mich da auf meine Erinnerung)

    Nachtrag:
    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2845.html


Log in to reply