reducing external identifier collisions
Doug Gwyn
gwyn at smoke.brl.mil
Sat Nov 10 18:19:20 AEST 1990
In article <24964 at adm.BRL.MIL> nick at spider.co.uk (Nick Felisiak) writes:
>Ideally, the only thing visible by name is the streamtab structure which
>defines the entry points to the driver. In practice, if a driver is
>constructed from more than one source file, a potentially large number
>of names remain visible simply because there is no type of name which
>disappears after an 'ld -r' used to produce the single driver object file.
This is a nice example of a real-world problem with the single, flat
external identifier name space. The "ld"/COFF hackery attempts to
solve these problems by in effect introducing levels into the name
space. There is another approach, using standard C facilities, that
we have used to good effect in a large project where tight control
over name space was a major software engineering consideration.
A project-wide header file describes the SOLE allowed externally-
visible object for any of numerous potential "methods" (each developed
according to project rules, but without knowledge of other "methods"):
/*
<Dd.h> -- MUVES "Dd" (data dependencies) package definitions
This header is part of the "universal" part of the project,
available for use by all developers of methods.
DdEntry is a definition for the structure type used to package
the sole entry point to a method.
*/
/* ... */
typedef struct
{
pointer method; /* -> private method data */
int info_needed; /* request flags for RrShoot() */
bool ctx_dependent; /* FRF depends on context */
float default_frf; /* unhit component's FRF value */
bool (*meth_init)( const char *, const char * );
bool (*view_init)( DqNode * );
bool (*shot_init)( const RrRay * );
bool (*cont_init)( const char *, const char * );
void (*meth_term)( void );
void (*view_term)( void );
void (*shot_term)( void );
void (*cont_term)( void );
} DdEntry;
/*
DdApprox is a copy of the entry-point structure that contains
method-specific initialization/termination function pointers
and globally-accessible data for the method.
It is set to the proper value at run time after the user has
specified which method to use. This is used by MUVES code
outside all method implementations when making standard
"requests" to the currently selected method.
*/
extern DdEntry DdApprox;
Then, each "method" is allowed to use the ONE global symbol "DdXXX", where
"XXX" is actually replaced by the method's name.
/*
compart.h -- "compart" method private definitions
*/
#include <Dd.h> /* for DdEntry */
/* ... */
/*
Private external functions and data must be accessed via the
sole external hook for the "compart" method, Ddcompart, which
contains a method-specific pointer as its first member. In
the "compart" method's case, this is a pointer to an XxEntry
structure, which contains XxBypass and pointers to functions
defined in the method's support module compart.c. (There are
numerous other separate modules implementing the bulk of the
method. They communicate with each other solely through the
hooks provided in Ddcompart.)
Note that in code for this method we can refer directly to
Ddcompart; no need to go through the copy in DdApprox since we
KNOW what method must be in effect for all code using this
"compart" method-private header.
*/
typedef struct
{
bool bypass; /* special testing flag */
/* The following are used by various "compart" modules: */
bool (*hit)( RrTrace *trace );
/* ... */
} XxEntry;
/*
Ddcompart is the structure that contains shared globally-
accessible data and entry points for the "compart" method.
*/
extern DdEntry Ddcompart;
/*
bool XxBypass
XxBypass is a flag that is set to true during the module test
programs, in order to disable use of "Dd" package functions.
*/
#define XxBypass ((XxEntry*)Ddcompart.method)->bypass
#ifndef XxNOFNDEFS /* (define XxNOFNDEFS for compart.c only) */
/*
bool XxHit( RrTrace *trace )
XxHit() records a true hit flag for the current component
(indicating that the component was present on the
threat path and was perforated) and returns true
upon succcess. If XxHit() fails, false is returned.
*/
#define XxHit ((XxEntry*)Ddcompart.method)->hit
/*...*/
#endif /* XxNOFNDEFS */
Identifiers beginning with "Xx" are guaranteed to not be used by any of the
standard project headers (apart from private method-specific headers), so we
can be sure that symbols starting with "Xx" will not collide with anything
within a single translation unit, as used here. Note that there are NO
external identifiers starting with "Xx".
The "compart" method's support functions corresponding to the preceding header
are all contained in:
/*
compart.c -- "compart" method shared data and functions
*/
#include <Dd.h>
/* ... */
#define XxNOFNDEFS /* permit definition of "Xx" functions below */
#include "compart.h"
#undef XxNOFNDEFS
/*
XxHit() records a true hit flag for the current component
(indicating that the component was present on the
threat path and was perforated) and returns true
upon succcess. If XxHit() fails, false is returned.
*/
static bool
XxHit( RrTrace *trace )
{
/* ... */
return true;
}
/* Dd-invoked global initialization and termination functions: */
/*
XxContInit() is the per-context Dd init function which is
called by AtUtility(). This function returns true unless
an error occurs.
*/
static bool
XxContInit( const char *mission, const char *envir )
{
/* ... */
return true;
}
/* ... */
/*
Private external functions and data must be accessed via the
sole external hook for the approximation method, Ddcompart,
which contains a method-specific pointer as its first member.
In our case, this is a pointer to an XxEntry structure, which
contains XxBypass and pointers to functions defined in
compart.c.
*/
static XxEntry XxItems =
{
false, /* XxBypass lives here! */
XxHit,
/* ... */
};
/*
Ddcompart is the structure that contains globally-accessible
data and entry points for the "compart" method, as described
previously.
*/
DdEntry Ddcompart = /* "compart"-specific hook for DdApprox */
{
(pointer)&XxItems, /* method's private pointer */
RrLOCATION | RrNORMAL, /* flags for RrShoot() */
true, /* FRF depends on context */
1.0, /* FRF for unhit component (LOF=0) */
XxMethInit, /* approx-method initialization func */
NULL, /* per-view initialization func */
NULL, /* per-shot initialization func */
XxContInit, /* per-context initialization func */
XxMethTerm, /* approx-method termination func */
NULL, /* per-view termination func */
NULL, /* per-shot termination func */
NULL /* per-context termination func */
};
The actual MUVES code is considerably more elaborate that the streamlined
extracts I have provided here. My point in posting this is to illustrate
a practical application of the idea of encapsulating multiple items within
a single (possibly hierachically-structured, as in this example) externally-
visible object, so that a large number of items can be accessed without
using up a large number of external identifiers. A MUVES "method" may be
implemented as many thousands of lines of source code split among hundreds
of separate source files, yet only a single external identifier is needed
for the entire method.
More information about the Comp.lang.c
mailing list