Callback

How do you actually implement a callback from scratch in C++? I use lots of callbacks through ROS, but I have not reasoned through how callbacks are implemented.

I know you pass in a Function Pointer, but under the hood, what happens?

Callbacks are generally used in asynchronous communication scenarios, where you don’t want a thread to be blocked.

In C

I was asked this for my Apple interview. I wasn’t familiar with the syntax of a C-style callback

Here is an example of callbacks in C.

Let’s say you want to write some code that allows registering callbacks to be called when some event occurs.

First define the type of function used for the callback:

typedef void (*event_cb_t)(const struct event *evt, void *userdata);

Now, define a function that is used to register a callback:

int event_cb_register(event_cb_t cb, void *userdata);

This is what code would look like that registers a callback:

static void my_event_cb(const struct event *evt, void *data)
{
    /* do stuff and things with the event */
}
 
...
   event_cb_register(my_event_cb, &my_custom_data);
...

In the internals of the event dispatcher, the callback may be stored in a struct that looks something like this:

struct event_cb {
    event_cb_t cb;
    void *data;
};

This is what the code looks like that executes a callback.

struct event_cb *callback;
 
...
 
/* Get the event_cb that you want to execute */
 
callback->cb(event, callback->data);

In C++

Some resources

In C++, due to its object-oriented nature, is not as easy to just pass around function pointers.

I ran into this issue while trying to pass a method as a function pointer, and it didn’t work, i.e.

something like this

class Node {
    public:
        trigger_callback(int interval, void* callback) {...}
        void custom_callback(std::string message) {
            ...
        }
    Node() {
        trigger_callback(5, custom_callback); // DOES NOT WORK, because custom_callback is not a standalone function
    }
};
  • It would work if the custom_callback is declared static
  • But then, you can’t use this inside a static function, since a static function means there is only 1 of them exists in memory across all instantiated objects of the same class
  • Talked about in this blog https://blog.stratifylabs.dev/device/2019-08-05-Callbacks-in-Cpp/

Instead, use a std::function.

Callbacks are used everywhere, especially in the C++ <algorithm> library.

Consider the for_each syntax.

std::vector<double> v{ 1.0, 2.2, 4.0, 5.5, 7.2 };
double r = 4.0;
std::for_each(v.begin(), v.end(), [&](double & v) { v += r; });
std::for_each(v.begin(), v.end(), [](double v) { std::cout << v << " "; });

Here’s how for_each is defined

template<class InputIt, class UnaryFunction>
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f)
{
  for (; first != last; ++first) {
    f(*first);
  }
  return f;
}