Callbacks are functions which are passed to other functions (or modules, libraries e.t.c) that then call the function at their choosing.
<functional> at your disposal (it’s a mixed bag, some embedded C++ environments I’ve used do support
<functional>, others do not).
First, let’s get some terminology out of the way:
|Callee||A function/method/object which gets called by the caller.|
|Caller||An object which gets passes a callback function, and then calls (executes) it.|
|Function||A basic function that does not require an instance of a class to run (e.g. standard C style functions, or static member functions).|
|Method||A function that belongs to an class, and requires an instance of that class to run.|
|Signals||Term used for "events" in an event/listener system.|
|Slots||Term used for objects which listen to signals in an event/listener system. These are normally implemented with a callback system.|
The Primitive C++ Callback: The Caller Knows The Type Of The Callee
The problem arises when you want to pass in a non-static method (function belonging to an class, that requires an instance of that class) as a callback to a library. A method is a member function of an object. To call a method, you can’t just know the functions memory address and call it, you also have to know the object that the function belongs to (the
This means that for C++ method callbacks in their most primitive form, the callee has to know the type of the object the function belongs to. For example:
(run this code at https://replit.com/@gbmhunter/pure-cpp-method-callback#main.cpp):
Using Static Methods Or Non-Member Functions (C-Style)
If you are stuck with a C-style callback, there is no direct way to call non-static (i.e. takes a
this pointer as the magic first parameter) member function. You can however easily call static member functions (they are no different in type signature to C-style functions):
(run this code at https://replit.com/@gbmhunter/c-callback-in-cpp-using-static-method#main.cpp):
Note the main limitation of the above method is that no non-static member functions can be called. This limits how “object orientated” you can get with your software/firmware architecture.
Using Static Methods Or Non-Member Functions, But Calling Member Functions Through Shared Variables
As we touched on before, if you are stuck with a C-style callback, there is no direct way to call a member function. However, given we can call static methods (as shown directly above), we can use shared variables (e.g. file scoped variables) to call a particular instance from that static function. This is about the best you can do when you can’t change the type signature of the C-style callback.
Let’s go through an example, this time using a standard C function rather than a static member function purely for illustration they are interchangeable:
(run this code at https://replit.com/@gbmhunter/c-callback-in-c-using-global-vars-and-funcs):
Static Variables, With Templating
A slightly more complicated but flexible approach to the above is to use templating,
std::function as shown in the below example:
If you have authorship of the library wanting to callback, then you can do even better than described above. What I would recommend here is to change the signature away from a C-style callback and use
std::function and lambdas instead. Rather than the library accepting a C-style callback in the format
int (callback*) (int num1, int num2), update the library to accept a
std::function<int(int, int)> callback. This allows you to pass in a lambda, which as you’ll see below, allows you to easily call a member function.
Rather than a lambda like in the example above, you can use
std::bind instead. I strongly recommend the lambda way of doing things, but for sake of completeness let’s introduce the
(run this code at https://replit.com/@gbmhunter/c-callback-in-cpp-using-std-function-bind#main.cpp):
C++ Callback Libraries
- Uses the signals and slots syntax
- Callbacks can be functions, member methods, virtual methods…
- Really easy to use syntax.
License: Apache License, Version 2.0
- Supports signals and slots.
- Many features.
- Uses advanced C++ compiler features.
- Somewhat complex to use
License: GNU Library General Public License
Vlpp is an open source C++ library which provides cross-platform replacements for
<functional> (among other std libraries). Sometimes, the
<functional> library provided by the C++ standard library will not work on embedded systems (I’ve had it work fine on some embedded platforms and not on others). Vlpp can be used as a substitute, allowing you to use
vl::Func<void(void)> to replace
std::function<void(void)> and implement callbacks in this manner on embedded platforms.
Callbacks In C++ Using Template Functors is a great page explaining and analysing all the different ways for implementing callbacks in C++. This includes the functional model, single rooted hierarchy, parameterize the caller, callee mix-in, and functors (which they promote).
The Type-safe Callbacks In C++ library on the Code Project gives a great, complete callback library for C++ which allows callbacks with 0 to 5 input arguments.
Functors to Encapsulate C and C++ Function Pointers is a short and simple tutorial on using functors.
This work is licensed under a Creative Commons Attribution 4.0 International License .