C++ Callbacks (and giving member functions to C-style callbacks)
Callbacks are functions (or more generally --- any object that is callable) which are passed to other functions (or modules, libraries e.t.c) that then call the function at their choosing. They are useful for eliminating dependencies between modules, and are used for example for libraries to call your application code.
C++, being a strongly-typed object-oriented language, makes callbacks a tricker subject to deal with than say, in C (non-object oriented) or Javascript/Python (object oriented but NOT strongly typed). This complexity arises when you want to call non-static member functions as callbacks. The implicit this
pointer means just a standalone function pointer is not enough. You need to “capture” the instance you want to call the member function on. The type of a member function is also specific to that class, meaning type erasure is needed to opaquely call any member function.
Let’s explore the different ways to deal with callbacks in C++, with a special focus on using them in embedded systems. At the end we’ll also benchmark the different techniques to see how quickly they run.
Working code for all the example below can be found at https://github.com/gbmhunter/blog-cpp-callbacks.
Terminology
First, let’s get some terminology out of the way:
Term | Description |
---|---|
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). |
Functor | In the context of C++ and programming, a functor (a.k.a. function object) is an object which can be called like a function. This behaviour is achieved by creating a class and overloading the operator() . Unlike regular functions, they can contain state. Not to be confused with the category theory concept of a functor which is something entirely different.1 |
Method | A function that belongs to an class, and requires an instance of the class to run (i.e. the this pointer). Also called a member function. |
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
pointer!).
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:
#include <cstdio>
class MyClass {public: // This is our application callback handler for when a message is received, we will // pass this into the library which deals with message parsing void onMsg(int num1, int num2) { printf("onMsg() called with num1=%i, num2=%i\n", num1, num2); }};
class LibraryClass {public: // For the library class to call the onMsg, it has to be passed both an instance // of MyClass and a pointer to the member function to call // Note that MyClass has to be known here! This creates undesired coupling...in // reality your library should never have to know about MyClass void passACallbackToMe(MyClass* myClass, void (MyClass::* onMsg)(int num1, int num2)) { // Call the callback function (myClass->*onMsg)(1, 2); }};
int main() { MyClass myClass; LibraryClass libraryClass;
// Provide the instance and function to call libraryClass.passACallbackToMe(&myClass, &MyClass::onMsg);}
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):
#include <cstdio>
class MyClass {public: // This is our application callback handler for when a message is received, we will // pass this into the library which deals with message parsing // The "static" keyword makes it easy, as now this function does not // take a this pointer and has the same signature as a plain C function static void onMsg(int num1, int num2) { printf("onMsg() called with num1=%i, num2=%i\n", num1, num2); // NOTE: Can't call any non-static method functions here! }};
class LibraryClass {public: // For the library class to call the onMsg, it has to be passed both an instance // of MyClass and a pointer to the member function to call // Note that MyClass has to be known here! This creates undesired coupling...in // reality your library should never have to know about MyClass void passACallbackToMe(void (*onMsg)(int num1, int num2)) { // Call the callback function onMsg(1, 2); }};
int main() { MyClass myClass; LibraryClass libraryClass;
// Provide the instance and function to call libraryClass.passACallbackToMe(&myClass.onMsg);}
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.
Calling Member Functions Via Standard C Function
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 want to call a C++ class member function, but you have to provide a 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:
#include <cstdio>#include <functional>
class LibraryClass {public: void passACallbackToMe(int (*callback)(int num1, int num2)) { // Now invoke (call) the callback int o = callback(1, 2); printf("Value: %i\n", o); // We might be on an embedded system, use printf() and not std::cout }};
class MyClass {public: int methodToCallback(int num1, int num2) { return num1 + num2; }};
// Global pointer to an instance of our class so the C style callback// wrapper can invoke the callback on a particular instance (yuck!)MyClass * myClassPtr;int cStyleWrapper(int num1, int num2) { return myClassPtr->methodToCallback(num1, num2);}
int main(){ MyClass myClass; // Make the global variable point to our new instance. Obviously, this // way does not scale well, as you have to make global variable and C-style // function for every instance (and what if you don't know how many instances you will // need!?!) myClassPtr = &myClass;
LibraryClass libraryClass; libraryClass.passACallbackToMe(&cStyleWrapper);}
Templating the Member Function and Instance
If you have control over the module accepting the callback, one option you can use is to use templating on the passed in callback member function and instance. For example:
#include <cstdio>
template <typename T, typename U>class LibraryClass {public: LibraryClass(T callback, U& instance) : callback(callback), instance(instance) {}
void run() { // Take the instance and call it's member function (instance.*callback)(1, 2); }private: T callback; U& instance;};
class MyClass {public: void methodToCallback(int num1, int num2) { printf("methodToCallback() called with num1=%i, num2=%i\n", num1, num2); }};
int main() { MyClass myClass; LibraryClass libraryClass(&MyClass::methodToCallback, myClass); libraryClass.run();}
LibraryClass
is templated on two parameters, the member function pointer type T
and the type of the class U
. These are automatically deduced by the compiler when the LibraryClass
object is constructed.
Pros and cons of this approach are:
- PRO: No reliance on
std::function
andstd::bind
or lambdas. - CON: The library class has to be templated on all class types and member functions that will be used as callback. This is not so much of an issue for the basic example shown above, but could quickly get out of hand with a few different callbacks (especially so if you wanted to call member functions of different user classes!).
- CON: You can’t use a basic C function as a callback, this approach forces you to use a member function.
std:function
andfunction_ref
allow you to support almost any “callable” object. - CON: Although faster than
std::function
, it’s still slower thanfunction_ref
which is arguably better in every way (see the Benchmarking section below for more details).
Callback Interface Using Inheritance
Another approach if you have control over the module accepting the callback is to use an interface class to define the callback signature. For example:
#include <cstdio>
/** * This defines the interface of the "callbackable" class. */class CallbackInterface {public: virtual void methodToCallback(int num1, int num2) = 0;};
class LibraryClass {public: LibraryClass(CallbackInterface* callback) : callback(callback) {} void run() { // Call the callback function callback->methodToCallback(1, 2); }private: CallbackInterface* callback;};
/** * Our class inherits from the interface. */class MyClass : public CallbackInterface {public: void methodToCallback(int num1, int num2) { printf("onMsg() called with num1=%i, num2=%i\n", num1, num2); }};
int main() { MyClass myClass; LibraryClass libraryClass(&myClass); libraryClass.run();}
CallbackInterface
is a pure virtual class which defines an interface for the callback. LibraryClass
accepts a pointer to an instance of this interface and will call the methodToCallback
method. To create a callback, MyClass
inherits from CallbackInterface
and implements the methodToCallback
method.
- PRO: Simple to understand and implement (arguably easier to read than the “Templating the Member Function and Instance” approach).
- PRO: Fast! Even though there is a virtual function call (i.e. vtable lookup), this method out performed many others in the benchmarking tests (see below).
- CON: You can’t use a basic C function as a callback, this approach forces you to use a member function.
std:function
andfunction_ref
allow you to support almost any “callable” object. - CON: The library class has to define the callback interface and the exact names of the callback methods. This prevents you from arbitrarily providing any function as the callback and is a rather rigid approach.
- CON: With multiple callbacks, the library has two choices. Either define all the callbacks in a single interface, which restricts a single user class to implement all the callbacks, or define a new interface for each callback, which would mean multiple inheritance if you wanted the same class to handle multiple callbacks. Neither of these options are ideal.
std::function With std::bind
std::function
(provided by the <functional>
header) is a common type to use for a callback in modern C++. For this technique you need to have control over the caller so that you can use the std::function
type (if you can’t, and are stuck with a C-style callback type, see the above sections). There are two main ways to create a std::function
object from a member function, one is to use std::bind
and the other is to use a lambda. Let’s cover the std::bind
technique first.
The following code example shows how to do this. Rather than the callback type being int (callback*) (int num1, int num2)
, we now change it to std::function<int(int, int)>
. <int(int, int)>
defines the type signature of the callback, which is a function which takes two int
parameters and returns an int
.
std::bind
is used to pass a member function to this callback. Notice the syntax is quite verbose. It requires the following syntax: std::bind(<pointer-to-member-function>, <instance-of-class>, <input-1-placeholder>, <in>)
.
#include <cstdio>#include <functional>
class MyClass {public: int methodToCallback(int num1, int num2) { return num1 + num2; }};
void passACallbackToMe(std::function<int(int, int)> callback) { int o = callback(1, 2); printf("Value: %i\n", o);}
int main(){ MyClass myClass; passACallbackToMe(std::bind(&MyClass::methodToCallback, myClass, std::placeholders::_1, std::placeholders::_2));}
Although std::function
is nice because it can be used to call pretty much anything (static functions, member functions, lambdas, e.t.c.), I’m not a big fan of using std::bind
as it:
- Seems to always require dynamic memory allocation on creation of the
std::function
object (not when calling it though!). - Is really slow to call (see the benchmarking section below).
Because of that, I prefer to use lambdas with std::function
(see below).
std::function With Lambdas
An alternative to using std::bind
to pass your member function to the std::function
is to use a lambda instead. This approach may avoid the dynamic memory allocation of std::bind
for a small number of captured variables (such as just this
). Similarly to std::bind
, a downside is that you have to repeat the function arguments again in the definition of the lambda.
The following code example shows how to do this:
#include <cstdio>#include <functional>
class MyClass {public: int methodToCallback(int num1, int num2) { return num1 + num2; }};
void passACallbackToMe(std::function<int(int, int)> callback) { int o = callback(1, 2); printf("Value: %i\n", o);}
int main(){ MyClass myClass;
// Use a lambda to capture myClass and call the member method passACallbackToMe([&myClass](int num1, int num2) -> int { return myClass.methodToCallback(num1, num2); });}
In my eyes this is easier to read than the std::bind
technique above. It also happens to execute faster (see the benchmarking section below) and doesn’t normally require dynamic memory allocation (unlike std::bind
). The caveat here is that it still can! It depends on the number of captured variables. Due to small buffer optimization (SBO) if you only capture one or two things (e.g. the example above just captures the this
pointer) then it normally won’t (test on your architecture). More than that, and it needs to allocate memory on the heap to save the captured variables.
Impossibly Fast Delegates
There is a somewhat famous 2005 article called “The Impossibly Fast C++ Delegates” by Sergey Ryazanov that describes code for invoking pointer-to-member functions in a fast way that does not require any dynamic memory allocation.2 Then in 2012 there was a post on the Code Review Stack Exchange that modernised the code for C++11.3 Let’s see how this approach compares to std::function
!
I’m not going to post the library code here as it’s quite large, but you can get it from the links above, or GitHub repo I made to help generate this article.
This is how you use the library:
#include <cstdio>
#include "ImpossiblyFastDelegates.hpp"
class MyClass {public: // This is our application callback handler for when a message is received, we will // pass this into the library which deals with message parsing int methodToCallback(int num1, int num2) { printf("methodToCallback() called with num1=%i, num2=%i\n", num1, num2); return num1 + num2; }};
class LibraryClass {public: void passACallbackToMe(delegate<int(int, int)> callback) { // Call the callback function callback(1, 2); }};
int main() { MyClass myClass; LibraryClass libraryClass;
auto callback = delegate<int(int, int)>::from(&myClass, &MyClass::methodToCallback);
// Provide the instance and function to call libraryClass.passACallbackToMe(callback);}
You will notice it is very similar to the std::function
with std::bind
technique above, except you don’t have to specify placeholders for the function input arguments! Interesting, this approach performed faster than the std::function
techniques when optimizations were turned off, but about the same speed as the std::function
with lambdas technique when optimizations were turned on. See the Benchmarking section below for details.
function_ref
An faster, non dynamic memory allocation alternative to std::function
is function_ref
. I use the term function_ref
to refer to a concept in which there a number of implementations. The first standarized one is hitting the standard library in C++26, which as of mid-2025 is not widely supported by compilers (and good luck trying to get a company to adopt a standard on the bleeding edge). However, there are also 3rd party implementations that you can use today.
function_ref
is a non-owning function wrapper.
From cppreference.com:
Class template
std::function_ref
is a non-owning function wrapper.std::function_ref
objects can store and invoke reference to Callable target - functions, lambda expressions, bind expressions, or other function objects, but not pointers to member functions and pointers to member objects.4
The following code example uses the zhihaoy/nontype_functional
library.
#include <cstdio>
#include <std23/function_ref.h>
class MyClass {public: int methodToCallback(int num1, int num2) { return num1 + num2; }};
void passACallbackToMe(std23::function_ref<int(int, int)> callback) { int o = callback(1, 2); printf("Value: %i\n", o); // We might be on an embedded system, use printf() and not std::cout}
int main(){ MyClass myClass; passACallbackToMe({std23::nontype<&MyClass::methodToCallback>, &myClass});}
Note the callback is now of type std23::function_ref<int(int, int)>
. To pass a member function to this callback, we use std23::nontype
on the member function pointer and pass this along with the instance of the class we wish to call. This has an improvement over both std::function
techniques (std::bind
and lambdas) in that you don’t have to repeat the function arguments!
3rd Party Implementations
As of mid-2025, C++26 support is not widespread among the popular compilers. And good luck trying to get a company to adopt a standard on the bleeding edge. Luckily, there are 3rd party implementations of essentially the same thing as std::function_ref
.
zhihaoy/nontype_functional
The GitHub repo zhihaoy/nontype_functional contains a complete implementation of the std::function
, std::function_ref
, and std::move_only_function
equivalent to those in the C++26 <functional>
header.5
zhihaoy/nontype_functional can be easily included in your CMake based project using FetchContent
:
include(FetchContent)FetchContent_Declare( nontype_functional GIT_REPOSITORY https://github.com/zhihaoy/nontype_functional GIT_TAG bdb098751ccdba3e03b429064473c70e228a885e # v1.0.2)FetchContent_MakeAvailable(nontype_functional)
TartanLlama/function_ref
For a lighter implementation, the GitHub repo TartanLlama/function_ref provides just a tl::function_ref
implementation. It describes itself as “a lightweight non-owning reference to a callable”.
A working std::function_ref
example using zhihaoy/nontype_functional
can be found at https://github.com/gbmhunter/blog-cpp-callbacks/tree/main/using-std-function-ref.
abseil function_ref
Google’s Abseil library provides a function_ref
implementation. The header file can be found here. I couldn’t get this to work as I wanted as the function_ref
required the callable class to be a template parameter, so I’m guessing it does not perform the same type erasure as the other function_ref implementations.
Benchmarking
I benchmarked some of the different callback techniques above to compare how quickly they run. Most of the time you wouldn’t care about these performance differences, as the callbacks are not part of some critical execution path. However, in certain situations you will care! Some examples might be if you had a callback that fired on every received byte of a high bandwidth communication protocol, or a callback as part of some fast running control loop.
Benchmarking was performed with Google Benchmark. Let’s first see how they perform when optimizations are turned off (-O0
). For some reason turning optimization off caused the function_ref
test to segfault!?!, so that method is not included in the results. The benchmark results are shown in This is a placeholder for the reference: fig-callback-benchmarks-no-opt.
The sad (but in some sense hardly surprising) news is that if you need the absolute fastest performance (caveat: with optimizations off!), nothing can beat a C-style callback. If you want to call a member function, then calling a static function that then reaches out to the member function through a shared (e.g. global or file scoped) variable is the next best thing. On the other end of the spectrum, using std::function
with std::bind
gave the slowest performance, being about 10x slower than the C-style callback!
What if we let the compiler optimize the code? Does the relative performance of the different techniques change? Let’s find out! I compiled using GCC and the -O3
flag (optimize as much as possible for speed) and ran the benchmarks again. And this time the function_ref
test didn’t segfault! This is a placeholder for the reference: fig-callback-benchmarks-opt shows the results.
Oh how the game changes! Now for some reason the callback interface using inheritance is the fastest, even beating the basic C callback technique. I have no idea why this is? On the slow end of the spectrum, things didn’t change much. std::function
with std::bind
and static variables with templating (using std::bind
) are still the slowest. I’m quite surprised that templating the member function and instance was so slow. I would have thought it would perform almost as fast as the C style callback, since the only difference should be the one extra this
parameter that needs to be passed in.
Summary and Comparison of Callback Techniques
Unfortunately there is not just one “right” way to do callbacks in C++. Choosing the right callback technique in C++ depends heavily on your specific requirements, such as compatibility with C APIs, performance needs, desired flexibility, and whether you can use modern C++ features (like <functional>
) or external libraries.
Here’s a table summarizing the key characteristics of the methods discussed:
Technique | Performance* | Flexibility (Callable Types) | Ease of Use | Dependencies | C API Compatibility | Key Drawback(s) |
---|---|---|---|---|---|---|
Static/Non-Member (C-Style) | Excellent | Low (only free functions/static methods) | Easy | None | Yes (native) | Can’t directly call non-static members or easily manage instance state. |
Member via C Func (Global ptr) | Excellent | Low (tied to global/shared state) | Moderate (requires wrapper + state management) | None | Yes (via wrapper) | Uses global/shared state, not scalable, error-prone. |
Template Member/Instance | Good/Fair | Low (only member functions of specific type) | Moderate (template syntax) | None | No | Caller API requires templates, inflexible (caller tied to callee type), only member functions. |
Inheritance Interface | Excellent | Low (only specific member functions via inheritance) | Easy (standard OOP) | None | No | Rigid (inheritance required, fixed method names), potential multiple inheritance issues. |
std::function + std::bind | Poor | High (most callables) | Moderate (verbose std::bind syntax) | Standard (<functional>) | No | Performance overhead (type erasure, potential allocation), verbose syntax, requires <functional> (may not be available on some embedded systems). |
std::function + Lambda | Fair | High (most callables) | Moderate (lambda syntax clearer than bind) | Standard (<functional>) | No | Performance overhead (type erasure, possible SBO helps), requires <functional>. |
Impossibly Fast Delegates | Fair/good | High (most callables) | Easy | External Header Only Library | Yes | Requires external header only library. Not as fast as C-style callbacks or an inheritance interface. |
function_ref (e.g., std23/tl) | Excellent | High (most callables) | Moderate (specific library syntax or lambda) | External Lib (pre-C++26) / Standard (C++26+) | No | Non-owning (lifetime management is critical!), requires library/modern compiler. |
- Performance ratings are based on the results from the Benchmarking section. Actual performance can vary significantly based on compiler, optimizations, architecture, and specific usage.
Recommendations:
- For C API compatibility: Use C-style function pointers (Static/Non-Member or Member via C Func wrapper, being wary of the latter’s drawbacks).
- For maximum flexibility & modern C++:
std::function
(prefer lambda overstd::bind
) is versatile but has performance costs.function_ref
is a high-performance alternative if you can manage its non-owning nature and dependencies. - For performance & control over API (no C compat needed): Inheritance can be surprisingly fast but rigid.
function_ref
is often the best modern choice for performance without ownership. Templating the caller is an option but can make the caller’s API complex. - The “Member via C Func (Global ptr)” should be used cautiously due to scalability and safety issues.
Ultimately, analyze your project’s constraints and priorities to select the most suitable callback mechanism.
C++ Callback Libraries
cpgf
Website: http://www.cpgf.org/
- Uses the signals and slots syntax
- Callbacks can be functions, member methods, virtual methods…
- Really easy to use syntax.
- Powerful.
License: Apache License, Version 2.0
libsigc++
Website: http://libsigc.sourceforge.net/
- Supports signals and slots.
- Many features.
- Powerful.
- Uses advanced C++ compiler features.
- Somewhat complex to use
License: GNU Library General Public License
Naios/function2
Website: https://github.com/Naios/function2
“Drop-in replacement for std::function that supports move only types, multiple overloads and more.”6
- Similar interface to
std::function
. - Has the same small-buffer optimization as
std::function
, meaning no dynamic memory allocation for small functors. - Supports (and tested with) Visual Studio 2017+, Clang 3.8+, GCC 5.4+.
- Header only library.
- Built with CMake.
Vlpp
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.
slotmachine-cpp
Website: https://github.com/gbmhunter/slotmachine-cpp
This is an open-source C++ callback library I wrote many years ago!
External Resources
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, parametrize 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.
Footnotes
-
Wikipedia (2025, Apr 25). Functor [article]. Retrieved 2025-05-01, from https://en.wikipedia.org/wiki/Functor. ↩
-
Sergey Ryazanov (2005, Jul 18). The Impossibly Fast C++ Delegates [article]. Retrieved 2025-05-02, from https://www.codeproject.com/Articles/11015/The-Impossibly-Fast-C-Delegates. ↩
-
Code Review. Impossibly Fast Delegate in C++11 [forum post]. Retrieved 2025-05-02, from https://codereview.stackexchange.com/questions/14730/impossibly-fast-delegate-in-c11. ↩
-
cppreference.com. std::function_ref [documentation]. Retrieved 2025-04-25, from https://en.cppreference.com/w/cpp/utility/functional/function_ref. ↩
-
GitHub. zhihaoy/nontype_functional [repo]. Retrieved 2025-04-25, from https://github.com/zhihaoy/nontype_functional. ↩
-
GitHub. Naios/function2 [repo]. Retrieved 2025-05-01, from https://github.com/Naios/function2. ↩