it-swarm.com.de

C++ - Rückruf mit Klassenmitglied

Ich weiß, dass das so oft gefragt wurde, und deshalb ist es schwierig, durch die Scheiße zu graben und ein einfaches Beispiel dafür zu finden, was funktioniert.

Ich habe das, es ist einfach und funktioniert für MyClass...

#include <iostream>
using std::cout;
using std::endl;

class MyClass
{
    public:
        MyClass();
        static void Callback(MyClass* instance, int x);
    private:
        int private_x;
};

class EventHandler
{
    public:
        void addHandler(MyClass* owner)
        {
            cout << "Handler added..." << endl;
            //Let's pretend an event just occured
            owner->Callback(owner,1);
        }
};

EventHandler* handler;

MyClass::MyClass()
{
    private_x = 5;
    handler->addHandler(this);
}

void MyClass::Callback(MyClass* instance, int x)
{
    cout << x + instance->private_x << endl;
}

int main(int argc, char** argv)
{
    handler = new EventHandler();
    MyClass* myClass = new MyClass();
}

class YourClass
{
    public:
        YourClass();
        static void Callback(YourClass* instance, int x);
};

Wie kann das umgeschrieben werden, damit EventHandler::addHandler() sowohl mit MyClass als auch mit YourClass funktioniert. Es tut mir leid, aber es ist genau so, wie mein Gehirn funktioniert. Ich muss ein einfaches Beispiel sehen, was funktioniert, bevor ich verstehen kann, warum/wie es funktioniert. Wenn Sie eine Lieblingsmethode für diese Arbeit haben, können Sie sie jetzt zur Schau stellen. Markieren Sie den Code und senden Sie ihn zurück.

[Bearbeiten]

Es wurde beantwortet, aber die Antwort wurde gelöscht, bevor ich das Häkchen setzen konnte .. Die Antwort in meinem Fall war eine Templatfunktion. AddHandler zu diesem geändert ...

class EventHandler
{
    public:
        template<typename T>
        void addHandler(T* owner)
        {
            cout << "Handler added..." << endl;
            //Let's pretend an event just occured
            owner->Callback(owner,1);
        }
};
60
BentFX

Anstatt statische Methoden zu verwenden und einen Zeiger auf die Klasseninstanz zu übergeben, können Sie Funktionen im neuen C++ 11-Standard verwenden: std::function und std::bind :

#include <functional>
class EventHandler
{
    public:
        void addHandler(std::function<void(int)> callback)
        {
            cout << "Handler added..." << endl;
            // Let's pretend an event just occured
            callback(1);
        }
};

Die addHandler-Methode akzeptiert jetzt ein std::function-Argument, und dieses "Funktionsobjekt" hat keinen Rückgabewert und eine Ganzzahl als Argument.

Um es an eine bestimmte Funktion zu binden, verwenden Sie std::bind:

class MyClass
{
    public:
        MyClass();

        // Note: No longer marked `static`, and only takes the actual argument
        void Callback(int x);
    private:
        int private_x;
};

MyClass::MyClass()
{
    using namespace std::placeholders; // for `_1`

    private_x = 5;
    handler->addHandler(std::bind(&MyClass::Callback, this, _1));
}

void MyClass::Callback(int x)
{
    // No longer needs an explicit `instance` argument,
    // as `this` is set up properly
    cout << x + private_x << endl;
}

Sie müssen beim Hinzufügen des Handlers std::bind verwenden, da Sie den ansonsten impliziten Zeiger this explizit als Argument angeben müssen. Wenn Sie eine freistehende Funktion haben, müssen Sie std::bind nicht verwenden:

void freeStandingCallback(int x)
{
    // ...
}

int main()
{
    // ...
    handler->addHandler(freeStandingCallback);
}

Wenn der Event-Handler std::function-Objekte verwendet, können auch die neuen C++ 11 Lambda-Funktionen verwendet werden.

handler->addHandler([](int x) { std::cout << "x is " << x << '\n'; });
136

Hier ist eine prägnante Version, die mit Klassenmethoden-Rückrufen und regulären Funktionsrückrufen funktioniert. In diesem Beispiel verwendet die Rückruffunktion zwei Parameter: bool und int.

class Caller {
  template<class T> void addCallback(T* const object, void(T::* const mf)(bool,int))
  {
    using namespace std::placeholders; 
    callbacks_.emplace_back(std::bind(mf, object, _1, _2));
  }
  void addCallback(void(* const fun)(bool,int)) 
  {
    callbacks_.emplace_back(fun);
  }
  void callCallbacks(bool firstval, int secondval) 
  {
    for (const auto& cb : callbacks_)
      cb(firstval, secondval);
  }
private:
  std::vector<std::function<void(bool,int)>> callbacks_;
}

class Callee {
  void MyFunction(bool,int);
}

//then, somewhere in Callee, to add the callback, given a pointer to Caller `ptr`

ptr->addCallback(this, &Callee::MyFunction);

//or to add a call back to a regular function
ptr->addCallback(&MyRegularFunction);

Dies beschränkt den C++ 11-spezifischen Code auf die addCallback-Methode und private Daten in der Klasse Caller. Für mich minimiert dies zumindest die Wahrscheinlichkeit von Fehlern bei der Implementierung.

3
rsjaffe

Sie möchten eine Schnittstelle erstellen, die diesen Code behandelt, und alle Ihre Klassen implementieren die Schnittstelle.

class IEventListener{
public:
   void OnEvent(int x) = 0;  // renamed Callback to OnEvent removed the instance, you can add it back if you want.
};


class MyClass :public IEventListener
{
    ...
    void OnEvent(int x); //typically such a function is NOT static. This wont work if it is static.
};

class YourClass :public IEventListener
{

Beachten Sie, dass die Funktion "Rückruf" für diese Funktion nicht statisch ist, wobei ich glaube, dass eine Verbesserung darstellt. Wenn Sie möchten, dass es statisch ist, müssen Sie dies tun, wie JaredC es mit Vorlagen empfiehlt.

3
Karthik T

MyClass und YourClass könnten beide von SomeonesClass abgeleitet werden, die eine abstrakte (virtuelle) Callback-Methode hat. Ihre addHandler akzeptiert Objekte des Typs SomeonesClass und MyClass und YourClass kann Callback überschreiben, um ihre spezifische Implementierung des Rückrufverhaltens bereitzustellen.

1
s.bandara

Ein vollständiges Arbeitsbeispiel aus dem obigen Code .... für C++ 11:

#include <stdlib.h>
#include <stdio.h>
#include <functional>

#if __cplusplus <= 199711L
  #error This file needs at least a C++11 compliant compiler, try using:
  #error    $ g++ -std=c++11 ..
#endif

using namespace std;

class EventHandler {
    public:
        void addHandler(std::function<void(int)> callback) {
            printf("\nHandler added...");
            // Let's pretend an event just occured
            callback(1);
        }
};


class MyClass
{
    public:
        MyClass(int);
        // Note: No longer marked `static`, and only takes the actual argument
        void Callback(int x);

    private:
        EventHandler *pHandler;
        int private_x;
};

MyClass::MyClass(int value) {
    using namespace std::placeholders; // for `_1`

    pHandler = new EventHandler();
    private_x = value;
    pHandler->addHandler(std::bind(&MyClass::Callback, this, _1));
}

void MyClass::Callback(int x) {
    // No longer needs an explicit `instance` argument,
    // as `this` is set up properly
    printf("\nResult:%d\n\n", (x+private_x));
}

// Main method
int main(int argc, char const *argv[]) {

    printf("\nCompiler:%ld\n", __cplusplus);
    new MyClass(5);
    return 0;
}


// where $1 is your .cpp file name... this is the command used:
// g++ -std=c++11 -Wall -o $1 $1.cpp
// chmod 700 $1
// ./$1

Ausgabe sollte sein:

Compiler:201103

Handler added...
Result:6
0
Craig D