Skip to content

C++ Callbacks (and giving member functions to C-style callbacks)

Published On:
Jan 17, 2014
Last Updated:
Aug 21, 2022

Callbacks are functions which are passed to other functions (or modules, libraries e.t.c) that then call the function at their choosing.

C++, being a strongly-typed object-orientated language, makes callbacks a tricker subject to deal with than say, in C (non-object orientated) or Javascript (object orientated but NOT strongly typed). This is especially true for embedded systems where you cannot always rely on having newer C++ standard library header files such as <functional> at your disposal (it’s a mixed bag, some embedded C++ environments I’ve used do support <functional>, others do not).

Terminology

First, let’s get some terminology out of the way:

TermDescription
CalleeA function/method/object which gets called by the caller.
CallerAn object which gets passes a callback function, and then calls (executes) it.
FunctionA basic function that does not require an instance of a class to run (e.g. standard C style functions, or static member functions).
MethodA function that belongs to an class, and requires an instance of that class to run.
SignalsTerm used for “events” in an event/listener system.
SlotsTerm 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:

(run this code at https://replit.com/@gbmhunter/pure-cpp-method-callback#main.cpp):

#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):

(run this code at https://replit.com/@gbmhunter/c-callback-in-cpp-using-static-method#main.cpp):

#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.

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):

#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);
}

Static Variables, With Templating

A slightly more complicated but flexible approach to the above is to use templating, std::bind and std::function as shown in the below example:

(run this code at https://replit.com/@gbmhunter/c-callback-in-cpp-using-templating-functional-bind#main.cpp):

#include <stdio.h>
#include <functional>
template <typename T>
struct Callback;
template <typename Ret, typename... Params>
struct Callback<Ret(Params...)> {
template <typename... Args>
static Ret callback(Args... args) {
return func(args...);
}
static std::function<Ret(Params...)> func;
};
template <typename Ret, typename... Params>
std::function<Ret(Params...)> Callback<Ret(Params...)>::func;
// C-style API which just wants a standard function for callback
void c_function_which_wants_callback(int (*func)(int num1, int num2)) {
int o = func(1, 2);
printf("Value: %i\n", o);
}
class ClassWithCallback {
public:
int method_to_callback(int num1, int num2) {
return num1 + num2;
}
};
typedef int (*callback_t)(int,int);
int main() {
ClassWithCallback my_class;
Callback<int(int,int)>::func = std::bind(&ClassWithCallback::method_to_callback, &my_class, std::placeholders::_1, std::placeholders::_2);
callback_t func = static_cast<callback_t>(Callback<int(int,int)>::callback);
// Now we can pass this function to a C API which just wants a standard function callback
c_function_which_wants_callback(func);
}

Using std::function

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 https://replit.com/@gbmhunter/c-callback-in-cpp-using-std-function-lambda#main.cpp):

#include <cstdio>
#include <functional>
class LibraryClass {
public:
void passACallbackToMe(std::function<int(int, int)> callback) {
// 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;
}
};
int main()
{
MyClass myClass;
LibraryClass libraryClass;
// Use a lambda to capture myClass and call the member method
libraryClass.passACallbackToMe([&myClass](int num1, int num2) -> int {
return myClass.methodToCallback(num1, num2);
});
}

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 https://replit.com/@gbmhunter/c-callback-in-cpp-using-std-function-bind#main.cpp):

#include <cstdio>
#include <functional>
class LibraryClass {
public:
void passACallbackToMe(std::function<int(int, int)> callback) {
// 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;
}
};
int main()
{
MyClass myClass;
LibraryClass libraryClass;
// Alternate way to using a lambda, use std::bind instead. However I recommend the lambda way.
libraryClass.passACallbackToMe(std::bind(&MyClass::methodToCallback, myClass, std::placeholders::_1, std::placeholders::_2));
}

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

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.

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, 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.