С++ обратный вызов с использованием члена класса - программирование
Подтвердить что ты не робот

С++ обратный вызов с использованием члена класса

Я знаю, что это было задано так много раз, и из-за этого ему трудно прорываться сквозь треск и найти простой пример того, что работает.

У меня есть это, это просто и работает для 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 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);
};

Как это можно переписать, поэтому EventHandler::addHandler() будет работать как с MyClass, так и с YourClass. Извините, но это так, как работает мой мозг. Мне нужно увидеть простой пример того, что работает, прежде чем я смогу понять, почему и как он работает. Если у вас есть любимый способ сделать эту работу сейчас временем, чтобы показать ее, пожалуйста, разметьте этот код и отправьте его обратно.

[править]

На него был дан ответ, но ответ был удален, прежде чем я могу дать галочку. Ответ в моем случае был шаблонной функцией. Изменен addHandler для этого...

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

Ответ 1

Вместо статических методов и прохождения вокруг указателя на экземпляр класса вы можете использовать функциональность в новом стандарте С++ 11: std::function и std::bind:

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

Теперь метод addHandler принимает аргумент std::function, и этот "функциональный объект" не имеет возвращаемого значения и принимает целое число как аргумент.

Чтобы привязать его к определенной функции, вы используете 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;
}

Вам нужно использовать std::bind при добавлении обработчика, так как вам явно нужно указать неявный this указатель в качестве аргумента. Если у вас есть самостоятельная функция, вам не нужно использовать std::bind:

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

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

Наличие обработчика событий использует объекты std::function, а также позволяет использовать новые функции лямбда-скрипта С++ 11 :

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

Ответ 2

Вот краткая версия, которая работает с обратными вызовами метода класса и с регулярными обратными вызовами функции. В этом примере, чтобы показать, как обрабатываются параметры, функция обратного вызова принимает два параметра: bool и 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);

Это ограничивает код, специфичный для С++ 11, методу addCallback и личным данным в классе Caller. Для меня, по крайней мере, это минимизирует вероятность ошибок при ее реализации.

Ответ 3

Что вы хотите сделать, так это создать интерфейс, который обрабатывает этот код, и все ваши классы реализуют интерфейс.

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
{

Обратите внимание, что для этого функция "Обратный звонок" не является статичной, что, по моему мнению, является улучшением. Если вы хотите, чтобы он был статичным, вам нужно сделать это, как предлагает JaredC с шаблонами.

Ответ 4

MyClass и YourClass могут быть получены из SomeonesClass, который имеет абстрактный (виртуальный) метод Callback. Ваш addHandler будет принимать объекты типа SomeonesClass и MyClass и YourClass может переопределить Callback, чтобы обеспечить их конкретную реализацию поведения обратного вызова.

Ответ 5

Полный рабочий пример из кода выше.... для С++ 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 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

Выход должен быть:

Compiler:201103

Handler added...
Result:6

Ответ 6

Если у вас есть обратные вызовы с разными параметрами, вы можете использовать шаблоны следующим образом:
// компилируем с помощью: g++ -std = С++ 11 myTemplatedCPPcallbacks.cpp -o myTemplatedCPPcallbacksApp

#include <functional>     // c++11

#include <iostream>        // due to: cout


using std::cout;
using std::endl;

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

class OtherClass
{
    public:
        OtherClass();
        static void Callback(OtherClass* instance, std::string str);
    private:
        std::string private_str;
};

class EventHandler
{

    public:
        template<typename T, class T2>
        void addHandler(T* owner, T2 arg2)
        {
            cout << "\nHandler added..." << endl;
            //Let pretend an event just occured
            owner->Callback(owner, arg2);
         }   

};

MyClass::MyClass()
{
    EventHandler* handler;
    private_x = 4;
    handler->addHandler(this, private_x);
}

OtherClass::OtherClass()
{
    EventHandler* handler;
    private_str = "moh ";
    handler->addHandler(this, private_str );
}

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

void OtherClass::Callback(OtherClass* instance, std::string private_str)
{
    cout << " OtherClass::Callback(OtherClass* instance, std::string private_str) ==> " 
         << " Hello " << instance->private_str << endl;
}

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