简体   繁体   English

回调函数C ++

[英]Callback function C++

I'm having a great deal of problems trying to make a callback system. 我在尝试建立一个回调系统时遇到了很多问题。 I want to pass a function to another class's function to receive data. 我想将一个函数传递给另一个类的函数来接收数据。

I want ExampleClass to call SeperateThread::operate , and I want SeperateThread::operate to be able to call ExampleClass::updateNumber(int) to return a value. 我希望ExampleClass调用SeperateThread::operate ,我希望SeperateThread::operate能够调用ExampleClass::updateNumber(int)来返回一个值。 I've been trying for hours with various function pointers etc but can't seem to get it to work. 我已经尝试了几个小时的各种函数指针等但似乎无法让它工作。

SeperateThread is another thread so it doesn't block the main thread that ExampleClass is running in, but when SeperateThread has done it's calculations, I need to return the value to ExampleClass . SeperateThread是另一个线程,因此它不会阻止运行ExampleClass的主线程,但是当SeperateThread完成它的计算时,我需要将值返回给ExampleClass

If that makes sense? 如果这有道理? Here's a run down of what I'm trying to do. 这是我想要做的事情。 In this example, I want SeperateThread::operate to call ExampleClass::updatenumber(15); 在这个例子中,我希望SeperateThread::operate调用ExampleClass::updatenumber(15); ... ...

class ExampleClass
{
    public:
        ExampleClass()
        {   
            int numberToPass = 10;
            // call SeperateThread::operate and pass value and updatenumber function as pointer
            thread.operate(numberToPass, *updatenumber(int number));
        }
        ~ExampleClass();

        void updatenumber(int number)
        {
            // Do some stuff to the number passed to this function
        }

    private:
        SeperateThread thread;
}



class SeperateThread
{
    public:
        SeperateThread();
        ~SeperateThread();

        void operate(int number, &FunctionToCallBack)
        {
            // Do some calculations (result 5 for example purposes)
            int result = numberToPass + 5;

            // Call the callback function and pass result int
            FunctionToCallBack(result);
        }
}

There is something important about pointing to a class member function, you have to keep in mind that a function pointer is just a regular pointer but instead of a value it points to a function, but in a class there is a special hidden variable this which makes it tricky. 也有一些是指向类成员函数重要的是,你要记住,一个函数指针只是一个普通的指针,但不是值时,它指向一个功能,但在一类是有特殊的隐藏变量里面让它变得棘手。

One of the main problems here is that there is no pointer to the object since that would mean that you point to a function that exists within a specific object but it doesn't it just a plain function that contains this as a parameter. 这里的一个主要问题是没有指向对象的指针,因为这意味着你指向一个特定对象中存在的函数,但它不仅仅是一个包含作为参数的普通函数。

thread.operate(numberToPass, *updatenumber(int number)); thread.operate(numberToPass,* updatenumber(int number));

Here you call a function that is in another class and overall you never pass a pointer like this, it should be just the function's name since C will recognize that you want to pass it as a pointer. 在这里你调用一个在另一个类中的函数,总的来说你永远不会传递这样的指针,它应该只是函数的名称,因为C会识别你想要将它作为指针传递。 Generally the workaround would be to make the function static to avoid the problem with the this pointer. 通常,解决方法是使函数静态以避免this指针的问题。

One possible workaround would be to hold onto the class object and somehow hackishly call that function where you manually pass the this of the original object ( ExampleClass ). 一种可能的解决方法是保持类对象,并以某种方式hackishly调用该函数,您手动传递原始对象的这个(ExampleClass)。

You didn't say much about your design, but the fact that you put the source into the same field means that these classes "know" each other so why don't you just pass the class object and call the function that way like: 你没有多说你的设计,但你把源放到同一个字段的事实意味着这些类“彼此”“知道”,所以为什么不直接传递类对象并调用函数:

class BaseClass
{
public:

    BaseClass() {}
    ~BaseClass() {}

    virtual void updatenumber(int number)=0;    // pure virutal method, you MUST implement this in the subclasses!
}

class ExampleClass : public BaseClass
{
 public:
    ExampleClass()
    {   
        int numberToPass = 10;
        // call SeperateThread::operate and pass value and updatenumber function as pointer
        thread.operate(numberToPass, this);
    }
    ~ExampleClass();

    // this is now a virtual method
    void updatenumber(int number)
    {
    // Do some stuff to the number passed to this function
    }

 private:
    SeperateThread thread;
}

class SeperateThread
{
 public:
    SeperateThread();
    ~SeperateThread();

    void operate(int number,BaseClass* ObjectToCallBack)
    {
        // Do some calculations (result 5 for example purposes)
        int result = numberToPass + 5;

        // Call the callback function and pass result int
        // Note that here that this points to the BaseClass pointer but it can be a subclass of it effectively hiding it's "unneded members" at this specific point
        ObjectToCallBack->updatenumber(result);
    }
}

In case you want to hide the implementation you can just use a pure virtual class and pass that type of pointer to the SeperateThread class. 如果您想要隐藏实现,您可以使用纯虚拟类并将该类型的指针传递给SeperateThread类。

Edit : updated my example to use a base class. 编辑:更新我的示例以使用基类。

There are two issues here: 这里有两个问题:

1. A Callback Function Is Not Enough 1.回调函数不够用

You'll need both an address for the code to call back, and the identity of the object on which the code should operate. 您将需要一个用于回调代码的地址,以及代码应在其上运行的对象的标识。 The idiomatic C++ way to do this is to encapsulate this in an object: 惯用的C ++方法是将它封装在一个对象中:

class SeparateThread { 
public:
    class Callback {
    public:
        virtual void ThreadDone(int result) const = 0;
        virtual ~Callback() {}
    };
    void operate(int number, const Callback & callback)
    {
        // Calculate result
        callback.ThreadDone(result);
    }
 };

ExampleClass can then either inherit privately from SeparateThread::Callback and implement ThreadDone() or define a separate callback class: 然后ExampleClass可以从SeparateThread::Callback私下继承并实现ThreadDone()或定义一个单独的回调类:

class ExampleClassThreadCallback : public SeparateThread::Callback {
public:
    ExampleClassThreadCallback(ExampleClass * obj) : fObj(obj) {}
    void ThreadDone(int result) const override {
        fObj.updatenumber(result);
    private:
        ExampleClass * fObj;
    }
};

You then simply call the thread as: 然后,您只需将该线程称为:

thread.operate(number, ExampleClassThreadCallback(this));

2. Concurrency 2.并发

In a design like this, where your class gets updated from a separate thread, you are likely to run into concurrency issues, so you'll have to design appropriate mechanisms to make sure that this updating does not cause problems. 在这样的设计中,您的类从单独的线程更新,您可能会遇到并发问题,因此您必须设计适当的机制以确保此更新不会导致问题。

There is a way to pass a member of a specific class instance to another function whether in a thread or not. 有一种方法可以将特定类实例的成员传递给另一个函数,无论是否在线程中。 If the callback is a member you need to wrap it together with the class instance you want the callback to affect. 如果回调是一个成员,您需要将它与您希望回调影响的类实例一起包装。

template<typename T, typename F, typename R>
struct callback
{
    callback(T cthis, F func) : _this(cthis), _func(func) { }
    void operator()(R result)
    {
        (_this->*_func)(result);
    }
    T _this;
    F _func;
};


class SeperateThread
{
    public:
        SeperateThread() { }
        ~SeperateThread() { }

        template<typename T, typename F, typename R>
        void operate(int number, callback<T,F,R> cb)
        {
            // Do some calculations (result 5 for example purposes)
            int result = number + 5;

            // Call the callback function and pass result int
            cb(result);
        }
};

class ExampleClass
{
    public:
        ExampleClass()
        {   
            int numberToPass = 10;
            // call SeperateThread::operate and pass value and updatenumber function as pointer
            thread.operate(numberToPass, callback<ExampleClass * const, void (ExampleClass::*)(int), int>(this, &ExampleClass::updatenumber) );
        }
        ~ExampleClass() { }

        void updatenumber(int number)
        {
            // Do some stuff to the number passed to this function
            printf("Result is %d\n", number);
        }

    private:
        SeperateThread thread;
};



void test()
{
    ExampleClass a;

}

The above will print: Result is 15. 以上将打印:结果为15。

Please note that I did not address the synchronization issues due to multithreading. 请注意,由于多线程,我没有解决同步问题。

If 'updatenumber' is called by more than one thread, and your code inside it accesses other data members, then you need to serialize it by adding a mutex lock at the beginning and unlock it before returning. 如果'updatenumber'被多个线程调用,并且其中的代码访问其他数据成员,则需要通过在开头添加互斥锁并在返回之前将其解锁来序列化它。 Best is to use std::mutex if you have C++11 compiler, or do this within a small struct, locking in the constructor and unlocking in the destructor. 如果你有C ++ 11编译器,最好是使用std :: mutex,或者在一个小结构中执行此操作,锁定构造函数并在析构函数中解锁。 Then you just create one such instance immediately on updatenumber() entry. 然后,您只需在updatenumber()条目上立即创建一个此类实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM