Background
I am working on a modular architecture for embedded devices where different abstraction layers have to communicate with each others. The current approach is to have plenty of functions, variables and defines mangled with the module name they belong to.
This approach is a bit painful and I would like to define some kind of common interfaces for the API. The key idea is to get a better understanding of what modules share the same HAL interface.
Proposal
I would like to use a OOP inspired architecture where I use structures as interfaces. All these structures are populated statically.
This solution looks nice but I might waste a lot of memory because the compiler doesn't know how to chop off structures and only keep what it really needs to.
The following example can be built with or without -DDIRECT
and the behavior should be exactly the same.
Example
Source (test.c)
#include <stdlib.h>
int do_foo(int a) {
return 42 * a;
}
#ifdef DIRECT
int (*foo)(int) = do_foo;
int (*bar)(int);
int storage;
#else
struct foo_ts {
int (*do_foo)(int);
int (*do_bar)(int);
int storage;
} foo = {.do_foo = do_foo};
#endif
int main(char argc) {
#ifdef DIRECT
return foo(argc);
#else
return foo.do_foo(argc);
#endif
}
Makefile
CFLAGS=-O2 -mthumb -mcpu=cortex-m3 -g --specs=nosys.specs
CC=arm-none-eabi-gcc
upper=$(shell echo $(1) | tr a-z A-Z)
EXEC=direct.out with_struct.out
all: $(EXEC)
%.out:test.c
$(CC) $(CFLAGS) -D$(call upper,$(basename $@)) -o $@ test.c
size $@
Output
One can notice that the memory footprint used with the struct
variant is bigger because the compiler doesn't allow itself to remove the unused members.
arm-none-eabi-gcc -O2 -mthumb -mcpu=cortex-m3 -g \
--specs=nosys.specs -DDIRECT -o direct.out test.c
size direct.out
text data bss dec hex filename
992 1092 36 2120 848 direct.out
arm-none-eabi-gcc -O2 -mthumb -mcpu=cortex-m3 -g \
--specs=nosys.specs -DWITH_STRUCT -o with_struct.out test.c
size with_struct.out
text data bss dec hex filename
992 1100 28 2120 848 with_struct.out
Question
With this example I demonstrate that using a structure is good for readability and modularity, but it could decrease efficiency and it increases the memory usage.
Is there a way to get the advantages of both solutions? Said differently, is there a way to tell the compiler to be smarter?
OOP?
Following the comments on this question, one suggestion is to use C++ instead. Unfortunately, the same issue would occur because a Class with unused members will never be simplified by the compiler. So I am falling in the same trap with both languages.
Another raised point was the reason for unused members in structures. To address this question we can imagine a generic 3-axes accelerometer used in an application where only 1 axis is used. The HAL for this accelerometer could have the methods read_x_acc
, read_y_acc
and read_z_acc
while only read_x_acc
is used by the application.
If I declare a class in C++ or a structure in C the function pointers for unused methods/functions will still consume memory for nothing.