I have recently decided to build an event library for the Arduino/ESP microcontrollers, the idea is simple - if an action happens, it invokes a function. In the code it requires holding a vector of function pointers in each Event object. Since the microcontrollers are limited in RAM, especially Arduino, I thought about ways of memory management and one of the ideas that I think maybe useful is to create function groups - objects that will wrap an array of function pointers (also they will be immutable) and will have an execute method that will run over each function and call it with the parameters needed.
My first attempt looked like this:
template<typename... Args>
class StaticFunctionGroup {
private:
using function = action<Args...>; // action is a pointer for a function that returns 'void'
public:
template<uint8_t Size>
constexpr StaticFunctionGroup(function(functions)[Size])
: m_functions(functions), m_size(Size)
{
}
void operator () (Args... args) {
for (uint8_t i = 0; i < m_size; i++)
m_functions[i](args...);
}
private:
uint8_t m_size;
function* m_functions;
};
In main() function it could be used as following:
int main()
{
StaticFunctionGroup<> group({
f1, f2,f3, f4 // f1-f4 are void functions without parameters
});
group();
}
The problem is that there is essentially a memory waste as I see it, because first I use a pointer to an array and I am not storing it in the object, and secondly I have to save the size of the array (for simplicity I used one byte sized number). So on the Arduino board this result in 3 bytes wasted for each such group (1 byte - size, 2 bytes the pointer).
Another change that I thought might be helpful was written like this:
template<uint8_t Size, typename... Args>
class StaticFunctionGroup {
private:
using function = action<Args...>;
public:
StaticFunctionGroup(function(&& functions)[Size])
{
for (uint8_t i = 0; i < Size; i++)
m_functions[i] = functions[i];
}
void operator () (Args... args) {
for (uint8_t i = 0; i < Size; i++)
m_functions[i](args...);
}
private:
function m_functions[Size];
};
template<uint8_t Size>
class StaticFunctionGroup<Size, void>
{
};
This variation eliminates the 3 wasted bytes but the size of the inner array cannot be deduced. so in main()
or in any other place I have to declare the size by hand, this is ok from one side, but since I may need to change the functions in many places or add/remove them, this becomes a serious issue.
int main()
{
StaticFunctionGroup<4> group({
f1, f2,f3, f4
});
}
The Question is what version is considered better? or none of them are good and there is another option for all this?