Here is an evil trick for you....
So we have a conundrum.
We want... a) A true layered architecture. b) We want to be able to build and run each layer by itself. c) We trying to trim startup time, so run time registration is not preferred. d) We want the information relating to each layer to be in the correct module. e) This is embedded system, so we prefer fixed sizes to unbounded heap structures.
Taraa! Enter Carter's Magical Mystical Link Time Callback Registration technique. (Thanks to JohnW for the suggestion of misusing "weak")
At the bottom most layer we have a header file....
==lower.h============================================================= #ifndef _LOWER_H
#ifdef __GNUC__ #define WEAK __attribute__ ((weak)) #else #error This code is non-portable. #endif
// Save parameter and notify all subscribers if new value is different // from the old value. void save( int);
#endif ======================================================================
Note evil non-portable bit. However, don't sulk too badly, it probably isn't too difficult to port or to convert to run time registration again.
====lower.c=========================================================== #include <stdio.h> #include "lower.h"
typedef void callback_t(int);
// Do nothing on callback. static void dummy(int i __attribute__ ((unused))) {}
// Just tell linker all unused slots are dummy void __attribute__ ((weak,alias("dummy"))) slot_1(int); void __attribute__ ((weak,alias("dummy"))) slot_2(int); void __attribute__ ((weak,alias("dummy"))) slot_3(int); void __attribute__ ((weak,alias("dummy"))) slot_4(int); void __attribute__ ((weak,alias("dummy"))) slot_5(int);
#define numSlots 5
static callback_t * const slots[numSlots] = {slot_1,slot_2,slot_3,slot_4,slot_5};
void save(int data) { static int data_store;
if( data != data_store) { data_store = data;
int i; for( i=0; i < numSlots; ++i) { (*(slots[i]))(data_store); } } }
int WEAK main() { save(10); save(10); save(20); save(20); save(10); return 0; } ======================================================================
Here we have a fixed number of slots, packed with the address of dummy routines.
Ooh. Looky it has it's own "main" routine. We can compile, link and run it like so... cc -Wall -W -O3 -c -o lower.o lower.c cc lower.o -o lower Run lower by itself lower
Yip, it ran OK.
So add one higher layer.... ==higher_a.h========================================================== #ifndef _HIGHER_A_H
void hit_a(int);
#endif ==higher_a.c========================================================== #include <stdio.h> #include "lower.h" #include "higher_a.h"
void slot_4(int i) { printf( "Module A has just been told that the new value is %d\n", i); }
void hit_a(int value) { printf( "Hit A with a %d\n", value); save( value); }
int WEAK main() { hit_a(-1); hit_a(1); hit_a(1); hit_a(2); return 0; } ======================================================================
This one uses slot_4 and ooh looky, it _also_ has a main. So we can compile, link and run it like so...
cc -Wall -W -O3 -c -o higher_a.o higher_a.c cc higher_a.o lower.o -o higher_a Run higher_a by itself higher_a Hit A with a -1 Module A has just been told that the new value is -1 Hit A with a 1 Module A has just been told that the new value is 1 Hit A with a 1 Hit A with a 2 Module A has just been told that the new value is 2
Ooh Looky! We get a call back whenever the data store has changed.
Note we didn't change a _single_ byte of lower.c or lower.h to create higher_a! We must be doing something right!
But what about a peer for higher_a? Does that stuff things up?
=higher_b.h=========================================================== #ifndef _HIGHER_B_H
void hit_b(int);
#endif =higher_b.c=========================================================== #include <stdio.h> #include "lower.h" #include "higher_b.h"
void slot_2(int i) { printf( "Module B has just been told that the new value is %d\n", i); }
void hit_b(int value) { printf( "Hit B with a %d\n", value); save( value); }
int WEAK main() { hit_b(-1); hit_b(1); hit_b(1); hit_b(2); return 0; } ======================================================================
This is just a proof of concept so higher_b is just the same as higher_a except I have used slot_2 this time. In reality is would be an entirely different module.
cc -Wall -W -O3 -c -o higher_b.o higher_b.c cc higher_b.o lower.o -o higher_b
Run higher_b by itself higher_b Hit B with a -1 Module B has just been told that the new value is -1 Hit B with a 1 Module B has just been told that the new value is 1 Hit B with a 1 Hit B with a 2 Module B has just been told that the new value is 2
Great, not a byte of lower.c, lower.h or higher_a.c change to create higher_b. We must be doing something right!
So can we have a super layer?
=====top_layer.c====================================================== #include "higher_a.h" #include "higher_b.h"
int main() { hit_a(10); hit_b(30); hit_a(30);
return 0; } ======================================================================
Compile and link it... cc -Wall -W -O3 -c -o top_layer.o top_layer.c cc top_layer.o higher_a.o higher_b.o lower.o -o top_layer
That was easy. We didn't change a single bit of lower.c, lower.h, higher_a.h, higher_b.h, higher_a.c, or higher_b.c
We _really_ must be doing something right.
So let's run it and see if it works... top_layer Hit A with a 10 Module B has just been told that the new value is 10 Module A has just been told that the new value is 10 Hit B with a 30 Module B has just been told that the new value is 30 Module A has just been told that the new value is 30 Hit A with a 30
Yup, it all just works!
And here is the Makefile.. ==Makefile============================================================
CFLAGS = -Wall -W -O3
all : lower higher_a higher_b top_layer @echo Run lower by itself lower @echo Run higher_a by itself higher_a @echo Run higher_b by itself higher_b @echo Run toplayer top_layer
lower : lower.o
lower.o : lower.c lower.h
higher_a : higher_a.o lower.o
higher_a.o : higher_a.c lower.h
higher_b : higher_b.o lower.o
higher_b.o : higher_b.c lower.h
top_layer.o : top_layer.c lower.h
top_layer : top_layer.o higher_a.o higher_b.o lower.o
.PHONY : clean
clean : -rm -v *.o lower higher_a higher_b top_layer ======================================================================
John Carter Phone : (64)(3) 358 6639 Tait Electronics Fax : (64)(3) 359 4632 PO Box 1645 Christchurch Email : john.carter@xxxxxxxxxx New Zealand
The universe is absolutely plastered with the dashed lines exactly one space long.