Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What’s the recommended way to invoke callback function defined in container class from contained class?

Tags:

c++

oop

Imagine you had a class hierarchy like the following:

class Robot
{
public:
    void OnTaskCompleted() {}

private:
    Task *m_pTask;
};

class Task
{
public:
    virtual void DoTask() = 0;
};

class TidyUp : public Task
{
public:
    void DoTask()
    {
        // When TidyUp task is compeleted invoke OnTaskCompleted() from here.
    }
};

I need to call OnTaskCompleted() from TidyUp::DoTask(). What would be the recommended way to do that?

I'd like to avoid:

  • making OnTaskCompleted() static
  • passing Robot pointer to Task
like image 459
jpen Avatar asked Aug 08 '12 10:08

jpen


1 Answers

The static route is not feasible unless there is only one Robot in your program, and an instance of that robot is available statically.

Passing a Robot to the task may be OK, but it might reveal too much information and prohibit task usages with objects other than robots.

A third alternative would be to make an interface-like class for completion notifications, extending it in the Robot, and calling it from the task. Unfortunately, C++ does not make it particularly easy by pushing you into the virtual inheritance territory.

You could adopt a callback approach that is common in POSIX thread libraries (passing a void pointer and a function pointer that takes a void pointer), but that is not too C++-ish.

Finally, if you are using C++11, you have anonymous functions that let you address the issue very gracefully by wrapping both a function and an object on which it operates in a single closure without using an external library, such as boost.

Here is a quick example of the third approach (link to ideone):

#include <iostream>
#include <string>
using namespace std;

class WithNotification {
public:
    virtual void notify()=0;
};

class Robot : public virtual WithNotification {
private:
    string name;
public:
    Robot(const string& n) : name(n) {}
    virtual void notify() {cout << name << " has been notified" << endl; }
};

class Task {
private:
    WithNotification& onFinished;
public:
    Task(WithNotification& f) : onFinished(f) {}
    void run() {
        cout << "The task is running" << endl;
        onFinished.notify();
    }
};

int main() {
    Robot r1("Quick");
    Robot r2("Brown");
    Task t1(r1);
    Task t2(r2);
    t1.run();
    t2.run();
}
like image 95
Sergey Kalinichenko Avatar answered Oct 05 '22 10:10

Sergey Kalinichenko