luni
Well-known member
Inspired by this Thread https://forum.pjrc.com/threads/70986-Lightweight-C-callbacks, I tried how difficult it would be to implement a more modern callback API for hardware interrupts and user classes.
The simplest and usually recommended solution is to use std::function. While std::function is very efficient, it has a large memory footprint (10s of kB) and it uses dynamic memory allocation. Both can be considered problematic, especially for smaller boards (e.g. T3.2, TLC).
It turned out that a much simpler implementation can be done with surprisingly little effort. It probably doesn't cover all edge cases but it definitely works fine for the standard use cases. I.e., callbacks of type
I packed all the template stuff in the helper class CallbackHelper (https://github.com/luni64/CallbackHelper) which can be used in user libraries. Usage is quite simple, no template wizardry required. To transform any of types from the list above to a callback, basically all one needs to do is the following:
Here a working example showing some possibilities:
The repository (https://github.com/luni64/CallbackHelper) contains more usage examples and a proof of concept implementation of a PIT timer using the CallbackHelper. Additionally, I switched my IntervalTimerEx from an std::function API to the CallbackHelper. (https://github.com/luni64/IntervalTimerEx).
Both, IntervalTimerEx and CallbackHelper are listed in the Arduino and PIO library managers
I'll switch my other libraries (TeensyTimerTool, EncoderTool) to the CallbackHelper later.
Hope this is of any use and hasn't too much bugs
The simplest and usually recommended solution is to use std::function. While std::function is very efficient, it has a large memory footprint (10s of kB) and it uses dynamic memory allocation. Both can be considered problematic, especially for smaller boards (e.g. T3.2, TLC).
It turned out that a much simpler implementation can be done with surprisingly little effort. It probably doesn't cover all edge cases but it definitely works fine for the standard use cases. I.e., callbacks of type
- Free function
- static member function
- non static member function
- functors
- capturing and non capturing lambda expressions
- callbacks which pass variables to the caller (i.e. callbacks of types like void(*cb)(int x, double f) etc. of course this also works for lambdas and functors)
- No dynamic memory allocation and a really lightweight footprint (on a LC the IntervalTimerEx example adds some 2kB flash and some 340 bytes RAM, after replacing the printf's with println's)
I packed all the template stuff in the helper class CallbackHelper (https://github.com/luni64/CallbackHelper) which can be used in user libraries. Usage is quite simple, no template wizardry required. To transform any of types from the list above to a callback, basically all one needs to do is the following:
Code:
//...
callbackHelper_t cbh; // construct a callbackHelper
//...
callback_t* callback = cbh.makeCallback(...); // ... is anything callable
//...
callback->invoke(); // invoke the callback later, e.g. from a hardware ISR.
Here a working example showing some possibilities:
Code:
#include "CallbackHelper.h"
// setup the callback helper to handle up to 5 void(*)(void) callbacks
using callbackHelper_t = CallbackHelper<void(void), 5>;
using callback_t = callbackHelper_t::callback_t;
callbackHelper_t cbh; // construct the callback helper
callback_t* callback; // storage for a pointer to the generated callback
void freeFunction()
{
Serial.println("Free function callback");
}
void setup()
{
// generate a callback from freeFunction, store it in slot 0 (out of 5) and
// store a pointer to it in callback:
callback = cbh.makeCallback(freeFunction, 0);
// use a lambda instead:
//callback = cbh.makeCallback([] { Serial.println("lambda"); }, 0);
}
void loop()
{
callback->invoke();
delay(100);
}
The repository (https://github.com/luni64/CallbackHelper) contains more usage examples and a proof of concept implementation of a PIT timer using the CallbackHelper. Additionally, I switched my IntervalTimerEx from an std::function API to the CallbackHelper. (https://github.com/luni64/IntervalTimerEx).
Both, IntervalTimerEx and CallbackHelper are listed in the Arduino and PIO library managers
I'll switch my other libraries (TeensyTimerTool, EncoderTool) to the CallbackHelper later.
Hope this is of any use and hasn't too much bugs
Last edited: