Carter's Merry Magic Link Time Registration of Callbacks.

John Carter john.carter@tait.co.nz
Fri Aug 6 09:32:00 GMT 2004


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@tait.co.nz
New Zealand

The universe is absolutely plastered with the dashed lines exactly one
space long.



More information about the Gcc-help mailing list