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 (run this code at
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.
(run this code at <a href=https://replit.com/@gbmhunter/c-callback-in-cpp-using-std-function-lambda#main.cpp" target="_blank">https://replit.com/@gbmhunter/c-callback-in-cpp-using-std-function-lambda#main.cpp)
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
std::bind technique (run this code at
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). Typically, the
<functional> library provided by the C++ standard library will not work on embedded systems (simple test: try and include it and use
std::function to see if your platform supports it). 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 .