• No results found

Class descriptions essentially are static objects. They ought to exist as long as the

main program is active. This is normally accomplished by creating such objects as

global orstaticvariables and initializing them at compile time.

Our problem is that we need to call

Class_ctor()

and the other metaclass con-

structors to hide the details of inheritance when initializing a class description.

Function calls, however, can only happen at execution time.

The problem is known asstatic constructor calls— objects with a lifetime equal

to the main program must be constructed as soon asmain()is executed. There is

no difference between generating static and dynamic objects.

initPoint()and simi-

lar functions simplify the calling conventions and permit calls in any order, but the

actual work is in either case done bynew()and the constructors.

At first glance, the solution should be quite trivial. If we assume that every

class description linked into a program is really used we need to call every

init-

function at the beginning of

main(). Unfortunately, however, this is not just a

source text processing problem.

ooccannot help here because it does not know —

intentionally — how classes are put together for a program. Checking the source

code does not help because the linker might fetch classes from libraries.

Modern linkers such as

GNU

ld

permit a compiler to compose an array of

addresses where each object module can contribute elements as it is linked into a

program. In our case we could collect the addresses of allinit-funtions in such an

array and modifymain()to call each function in turn. However, this feature is only

available to compiler makers, not to compiler users.

Nevertheless, we should take the hint. We define an array

initializers[]

and

arrange things inmain()as follows:

void (* initializers [])(void) = { 0 };

int main ()

{ extern void (* initializers [])(void); void (** init)(void) = initializers; while (* init)

(** init ++)(); ...

All that remains is to specify every initialization function of our program as an ele-

ment ofinitializers[]. If there is a utility likenmwhich can print the symbol table of

a linked program we can use the following approach to generate the array automati-

cally:

$ cc —o task object... libooc.a

$ nm —p task | munch > initializers.c

$ cc —o task object... initializers.c libooc.a

We assume thatlibooc.a

is a library with a module

initializers.o

which defines the

array

initializers[]

as shown above containing only the trailing null pointer. The

library module is only used by the linker if the array has not been defined in a

module precedinglibooc.aon the command line invoking the compiler.

nm

prints the symbol table of the

task

resulting from the first compilation.

munchis a small program generating a new moduleinitializers.cwhich references

all

init-functions in

task. In the second compilation the linker uses this module

rather than the default module fromlibooc.a

to define the appropriateinitializers[]

fortask.

Rather than an array,

munch

could generate a function calling all initialization

functions. However, as we shall see in chapter 12, specifically a list of classes can

be put to other uses than just initialization.

The output fromnmgenerally depends on the brand of

UNIX

used. Luckily, the

option−pinstructs Berkeley-nmto print in symbol table order and System-V-nmto

produce a terse output format which happens to look almost like the output from

Berkeley-nm. Here ismunchfor both, implemented usingawk:

NF != 3 || $2 != "T" || $1 !˜ /ˆ[0—9a—fA—F]+$/ { next } $3 ˜ /ˆ_?init[A —Z][A—Za—z]+$/ { sub(/ˆ_/, "", $3) names[$3] = 1 } END { for (n in names)

printf "extern void %s (void);\n", n print "\nvoid (* initializers [])(void) = {" for (n in names)

printf "\t%s,\n", n print "0 };"

}

The first condition quickly rejects all symbol table entries except for those such as

00003ea8 T _initPoint

Assuming that a name beginning with

init

and a capital letter followed only by

letters refers to an initialization function, an optional initial underscore is stripped

(some compilers produce it, others do not) and the rest is saved as index of an

array

names[]. Once all names have been found,

munch

generates function

declarations and definesinitializers[].

106

___________________________________________________________________________

9 Static Construction — Self-Organization

The arraynames[]is used because each name must be emitted twice. Names

are stored as indices rather than element values to avoid duplication.*

munch

can

even be used to generate the default module for the library:

$ munch < /dev/null > initializers.c

munchis a kludge in many ways: it takes two runs of the linker to bind a task

correctly; it requires a symbol table dump likenmand it assumes a reasonable out-

put format; and, worst of all, it relies on a pattern to select the initialization func-

tions. However,munchis usually very easy to port and the selection pattern can be

adapted to a variety of static constructor problems. Not surprisingly, the AT&T C++

system has been implemented for some hosts with a (complicated) variant of

munch.