I want to define an array of things, like event handlers. The contents of this array is completely known at compile time, but is defined among multiple compilation units, distributed amongst multiple libraries that are fairly decoupled, at least until the final (static) link. I'd like to keep it that way too - so adding or deleting a compilation unit will also automatically manage the event handler without having to modify a central list of event handlers.
Here's an example of what I'd like to do (but does not work).
central.h:
typedef void (*callback_t)(void);
callback_t callbacks[];
central.c:
#include "central.h"
void do_callbacks(void) {
int i;
for (i = 0; i < sizeof(callbacks) / sizeof(*callbacks); ++i)
callbacks[i]();
}
foo.c:
#include "central.h"
void callback_foo(void) { }
callback_t callbacks[] = {
&callback_foo
};
bar.c:
#include "central.h"
void callback_bar(void) { }
callback_t callbacks[] = {
&callback_bar
};
What I'd like to happen is to get a single callbacks
array, which contains
two elements: &callback_foo
and &callback_bar
. With the code above, there's
obviously two problems:
- The
callbacks
array is defined multiple times. sizeof(callbacks)
isn't known when compilingcentral.c
.
It seems to me that the first point could be solved by having the linker merge
the two callbacks
symbols instead of throwing an error (possibly through some
attribute on the variable), but I'm not sure if there is something like that.
Even if there is, the sizeof problem should somehow also be solved.
I realize that a common solution to this problem is to just have a startup function or constructor that "registers" the callback. However, I can see only two ways to implement this:
- Use dynamic memory (realloc) for the callbacks array.
- Use static memory with a fixed (bigger than usually needed) size.
Since I'm running on a microcontroller platform (Arduino) with limited memory, neither of these approaches appeal to me. And given that the entire contents of the array is known at compile time, I'm hoping for a way to let the compiler also see this.
I've found this and this solution, but those require a custom linker script, which is not feasible in the compilation environment I'm running (especially not since this would require explicitely naming each of these special arrays in the linker script, so just having a single linker script addition doesn't work here).
This solution is the best I found so far. It uses a linked list that is filled at runtime, but uses memory allocated statically in each compile unit seperately (e.g. a next pointer is allocated with each function pointer). Still, the overhead of these next pointers should not be required - is there any better approach?
Perhaps having a dynamic solution combined with link-time optimization can somehow result in a static allocation?
Suggestions on alternative approaches are also welcome, though the required elements are having a static list of things, and memory efficiency.
Furthermore:
- Using C++ is fine, I just used some C code above for illustrating the problem, most Arduino code is C++ anyway.
- I'm using gcc / avr-gcc and though I'd prefer a portable solution, something that is gcc only is also ok.
- I have template support available, but not STL.
- In the Arduino environment that I use, I have not Makefile or other way to easily run some custom code at compiletime, so I'm looking for something that can be entirely implemented in the code.