Wie in C++ Objektmethoden miteinander verbinden, wie in Qt das connect?



  • Hallo,

    ich zerbreche mir schon seid Tagen den Kopf :), und zwar möchte ich das ein Object (Klasse) eine Funkion von einem anderen Objekt aufruft ohne das andere Objekt zu kennen, so wie in Qt connect(). Qt möchte ich aber nicht verwenden.

    Vereinfachter Beispiel Code:

    // Basisklasse
    class Object{
        ...
    
        public:
        void Object();
    
        ...
    };
    
    // Objekt 1
    class Tank : public Object{
        ...
    
        public:
        void Tank();
        void Shoot();
    
        ...
    };
    
    // Objekt 2
    class Button : public Object{
        ...
    
        public:
        void Button();
        void Click();
    
        ...
    };
    
    ...
    ...
    
    Tank *tank = new Tank();
    Button *button = new Button();
    
    // !!! Button::Click() mit Tank::Shoot() verbinden ???
    
    button->Click();    // soll Panzer::Shoot() aufrufen/auslösen
    


  • Schau dir mal das Signal/Slot Konzept an - so macht Qt es.
    Ich würde dir empfehlen eine bestehende Bibliothek wie libsigc++ oder Boost.Signals2 zu verwenden. Damit geht



  • Da hilft dir das Command-Pattern weiter.

    Kleines Beispiel:

    #include <iostream>
    using namespace std;
    
    // Command-Interface
    class Command{ public:  virtual void execute() =0;};
    
    // Objekt 1 - Receiver 1
    class Tank {
        public:
    		void Shoot() {cout << "Tank::Shoot()\n";}
    };
    
    // Objekt 2 - Receiver 2
    class Button {
        public:
        void Click(){cout << "Button::Click()\n";};
    };
    
    //Konkrete Command-Klasse für Tank Shoot
    class Tank_shoot:public Command
    {
    public:
    	Tank_shoot(Tank& tank):tank(tank){}
    		virtual void execute() { tank.Shoot();}
    private:
    	Tank& tank;
    };
    //Konkrete Command-Klasse für Button Click
    class Button_click:public Command
    {
    public:
    	Button_click(Button& button):button(button){}
    	virtual void execute() { button.Click();}
    private:
    	Button& button;
    };
    //Invoker
    class Connect
    {
    public:
    	Connect(Command& c1, Command& c2):c1(c1),c2(c2) {}
    	void operator() (){c1.execute(); c2.execute();}
    private:
    	Command& c1;
    	Command& c2;
    };
    
    int main()
    {
    	Tank t;
    	Button b;
    	Tank_shoot shoot(t);
    	Button_click click(b);
    	Connect connect(shoot,click);
    	connect();
    }
    


  • Boost.Signals in Verbindung mit Boost.Function finde ich recht praktisch. function und bind() bieten eine mächtige Möglichkeit, um Callbacks von beliebigen Funktionen, Memberfunktionen und Funktionsobjekten mit sehr wenig Code einzurichten. Würde ich mir mal anschauen. 😉



  • @ einwurf:
    Genau sowas hat man mit Boost eben nicht. Der Boilerplate-Code für Vererbung etc. ist im Prinzip nicht nötig.

    #include <iostream>
    #include <boost/signals.hpp>
    #include <boost/bind.hpp>
    
    class Button
    {
    	public:
    		void Click() { std::cout << "Button::Click()\n"; }
    };
    
    class Tank
    {
    	public:
    		void Shoot() { std::cout << "Tank::Shoot()\n"; }
    };
    
    int main()
    {
    	Button b;
    	Tank t;
    
    	boost::signal<void()> s;
    	s.connect( boost::bind(&Button::Click, &b) );
    	s.connect( boost::bind(&Tank::Shoot, &t) );
    	s();
    }
    


  • Jo, Danke für eure Posts. Das mit Boost gefählt mir :D.



  • Nexus schrieb:

    @ einwurf:
    Genau sowas hat man mit Boost eben nicht. Der Boilerplate-Code für Vererbung etc. ist im Prinzip nicht nötig.

    Dafür ist es grauenhaft zu debuggen.



  • jaber schrieb:

    Nexus schrieb:

    @ einwurf:
    Genau sowas hat man mit Boost eben nicht. Der Boilerplate-Code für Vererbung etc. ist im Prinzip nicht nötig.

    Dafür ist es grauenhaft zu debuggen.

    (Bedingte) Haltepunkte helfen. Nur step by step sollte man vielleicht nicht durch den Code gehen.



  • Nexus schrieb:

    @ einwurf:
    Genau sowas hat man mit Boost eben nicht. Der Boilerplate-Code für Vererbung etc. ist im Prinzip nicht nötig.

    #include <iostream>
    #include <boost/signals.hpp>
    #include <boost/bind.hpp>
    
    class Button
    {
    	public:
    		void Click() { std::cout << "Button::Click()\n"; }
    };
    
    class Tank
    {
    	public:
    		void Shoot() { std::cout << "Tank::Shoot()\n"; }
    };
    
    int main()
    {
    	Button b;
    	Tank t;
    
    	boost::signal<void()> s;
    	s.connect( boost::bind(&Button::Click, &b) );
    	s.connect( boost::bind(&Tank::Shoot, &t) );
    	s();
    }
    

    Wie kann ich denn jetzt eine Funktion erstellen die "&Tank::Shoot" als Argument erwartet?

    Button::SetClick( ??? &shoot, Tank &t){
        ...connect( boost::bind(shoot, t) );
    }
    


  • ghostboss schrieb:

    Wie kann ich denn jetzt eine Funktion erstellen die "&Tank::Shoot" als Argument erwartet?

    Button::SetClick( ??? &shoot, Tank &t){
        ...connect( boost::bind(shoot, t) );
    }
    

    Nimm doch einfach ein Funktions-Objekt als Parameter entgegen:

    template<class Fun>
    Button::SetClick(Fun f){
        ...connect(f);
    }
    

    SetClick kannst Du dann ja mit boost::bind aufrufen.

    kk


Anmelden zum Antworten