Programmers' guide to the acedb - graph interface
Overview
This document describes the changes made to the graph package
(i.e. code in the 'w2' source directory) as part of a restructuring
of acedb code to introduce independent packages or libraries of code.
In the case of graph this meant removing acedb-specific
code (e.g. lexNNN functions etc) from the package so that an independent
graph library could be compiled. This has been achieved by allowing the
registering of callback functions with the graph package that allow the
acedb specific routines to be called from the graph code without introducing
link-time dependencies. The rest of this document describes how graph was
restructured to do this.
Then and now.
How things used to be....
Until now the graph package was included in an application by compiling in the
graph source code. If the application was an acedb application the macro ACEDB
was defined for the compilation to make sure that acedb specific code in the
graph source code was included. This meant no single graph library could be
built which could be used by both acedb and non-acedb applications, it also
meant that the source code was 'polymorhpic' in a number of ways: some
functions had different signatures according to whether ACEDB was defined, some
routines had different logic flows, some data structures had different members
and so on.
How things are now....
For many reasons it was seen as important to remove this 'polymorphism'
and produce a single coherent graph package/library which could be used by acedb
and non-acedb applications. This single graph package is built into a single
library which can be used by all applications. All acedb specific code has
been moved to a single module separate from the graph package which contains
all the routines and function calls required by acedb to interface
to the standard graph library.
How the restructuring was done
The new graph library has no acedb-specific code in it, it provides the
'bare bones' graphical routines with no acedb database element. acedb
applications are then allowed to alter the behaviour of the graph
package to allow calling of database routines to set specific types
of windows/boxes etc.
The underlying mechanism allowing removal of the ACEDB defined sections of
code is the definition of a number of function pointers in the graph library
which can be used by an acedb application to register its acedb-specific
routines. The graph code initialises these pointers to NULL and so can later
test them to see if an acedb function has been registered and call it if
it has.
The following fictious example shows the replacement of some ACEDB defined code
with such a function call, the original code:
for (i = 0 ; i < limit ; i++)
{
some_code ;
#ifdef ACEDB
lexNNNN(i, key, something) ;
#endif
some_more_code
}
is replaced with:
for (i = 0 ; i < limit ; i++)
{
some_code ;
if (getACEDBLexFunction() != NULL) (getACEDBLexFunction())(i, key, something) ;
some_more_code ;
}
Hence some 'hard-coded' acedb specific code has been replaced with a dynamic
function call thus removing the dependency of the graph code on the acedb code.
NOTE that, because this was a fairly large change, all alterations to the code
have been marked with C comments with the following format:
/* ACEDB-GRAPH INTERFACE: ....description of change made.... */
Hence the source tree can easily be searched to find all the changes made.
Design decisions
In an ideal world a generalised callback interface could have been introduced into the
graph package which would support programs other than acedb. In practice there is not
really any justification for this, the only
requirement for this interface is that it supports existing acedb functions.
A new module was introduced into the graph package called graphAcedbInterface.c which
contains all of the function pointers and code required to support the graph interface
for acedb code. The exported functions of this interface are declared in a new header,
graphAcedbInterface.h, the internal non-static functions are declared in graphs internal
header, graph_.h.
All of the acedb specific code from graph was collected together in one module, acedbgraph.c,
and placed in the w4 directory which contains the main module initialisation code for
acedb applications. A function was provided in this module that can be called from an acedb
application to intialise graph and the graph-acedb interface. This avoids the need for extensive
recoding of existing graphical acedb applications. This function which provides the acedb-graph
interface is declared in the header acedbgraph.h.
This crude diagram summarises this new interface:
ACEDB * GRAPH
*
*
(acedbgraph.h) * (graph_.h)
| * |
V * V
acedb_app.c ---> acedbgraph.c --------*--> graphAcedbInterface.c <--- rest of
| A * graph
| | * package
| (graphAcedbInterface.h) * A
| * |
| * |
| * |
| (graph.h) * |
| | * |
| V * |
----------------------------------*---------------------------------
*
*
*
The major design decisions were:
- to use a function callback mechanism to replace the ACEDB defines allowing
the new code to execute the acedb specific code in the same way as it
was executed when included by #define.
- to provide an acedb specific set of functions in a single graph package
module (graphAcedbInterface.c) to allow the registering of data and the
callback functions.
- to collect all the acedb specific code from the graph package into
a single module (acedbgraph.c) outside of the graph package.
Future changes
There should now be no need to include acedb specific code in the graph package
with #defines. All new code should follow this model of function callback interface.
Ideally, new acedb requirements for the graph package should be dealt with by
introducing more generalised 'callback' functions into the graph package rather than
by expanding this interface. The intention is to limit this interface to its current
size, it is simply an expedient bit of coding to produce a stand-alone graph package
and more importantly stand-alone graph library.
Source/Interface/Functions
Source files:
Reflecting the division of function into 'acedb' or 'graph' code, the
source files are divided into two sets:
Graph package:
- graph.h
- does not contain any of this interface
- graphAcedbInterface.h
- declares all public functions of the interface
- graph_.h
- declares all non-static private functions used internally in graph to support the
interface.
- graphAcedbInterface.c
- contains all graph package code to support the interface
Acedb code:
- acedbgraph.h
- declares all public functions for acedb to initialise its interface to the graph
graph package.
- acedbgraph.c
- contains all functions of the interface
Note that non-acedb applications can simply include graph.h while acedb applications
include just graphAcedbInterface.h (which include graph.h).
Interface:
The public interface comprises the following elements:
- in acedbgraph.c:
- acedbAppGraphInit
- this function is called once from an acedb application soon after it
starts up, it initialises both the graph package and the graph/acedb
interface. For most acedb applications this is the only function call required
to intialise the interface.
- acedbGraphInit
- this function initialises the graph/acedb interface and would normally be called
by acedbAppGraphInit, applications that required more control can initialise the
graph package and the graph/acedb interface separately using graphInit and this
call.
See acedbGraph.h for the complete prototypes.
- in graphAcedbInterface.c:
- setGraphAcedbNNNN
- this set of functions can be called to initialise the function pointers and
any data required by the graph interface code. There is approximately one
setGraphAcedbNNNN routine for each component of the graph package that contained
acedb specific code (e.g. graphps.c has one, graphprint.c has one etc.).
See graphAcedbInterface.h for a complete list and full prototypes.
The private interface used only within the graph package comprises:
- in graphAcedbInterface.c:
- getGraphAcedbNNNN
- this set of functions can be called to find out if a function has been
registered by acedb and then to get the pointer to that function
if one has. Since all function pointers for each graph package component
are registered with a single setGraphAcedbNNNN, there are many more
getGraphAcedbNNNN functions than setGraphAcedbNNNN routines, one for each
function registered.
See the acedb/graph interface section of graph_.h for a complete list and full prototypes.
Global variables
none