We have defined an ADT int_stack
. Now assume that we also want a stack of doubles, of (pointers to) some type T
etc. How can we use the int_stack
code that we have already written to obtain, say, T_stack
?
The straight-forward way, of course is to copy and modify. It is not that difficult to take an existing piece of code and alter it in a stylized fashion to obtain an ADT that operates on other types. The problem comes later, when one of the two (or more) copies is altered to fix a bug or to add an enhancement. We now have to keep track of which ADT implementations are related, and patch them all.
This is the problem that mechanisms like C++
templates were developed to address. C
has no such equivalent. Instead, what I use is could be called "poor-mans template". It is not much more than an automated way of doing copy and modify.
To start with, I would create a file called stack.h.templ
which would look like:
#ifndef H_@type_stack
#define H_@type_stack
#include "@type_stack-private.h"
typedef struct @type_stack * @type_stack;
@type_stack make_@type_stack(void);
void push_@type_stack(@type_stack, @type);
#define FOR_@TYPE_STACK(_stack,_i) X_FOR_@TYPE_STACK(_stack,_i)
#define END_@TYPE_STACK X_END_@TYPE_STACK
#endif
The @type
and @TYPE
are strings that will need to be modified after copying. To produce an int_stack.h
from a stack.h.templ
, we define a file called int_stack.table
that controls how the strings will be modified after copying.
@type int
@TYPE INT
For a double_stack
, double_stack.table
would contain:
@type double
@TYPE DOUBLE
We have to come up with a tool that can automatically modify a template using a table to produce the actual code. This can be written any number of ways. I have used combinations of sed
and gawk
in the past. Currently I am using a python
script. An abbreviated version of the Expand.py
script is:
import sys
table_file = sys.argv[1]
input_file = sys.argv[2]
output_file = sys.argv[3]
# read the table
table = {}
while(1):
line = table_file.readline()
if( line == "" ):
break
words = line.split(None, 1)
table[words[0]] = words[1]
# do the modification
while(1):
line = input_file.readline()
if( line == "" ):
break
for old in table.keys():
line = line.replace(old, table[old])
output_file.write(line)
To produce int_stack.h
from stack.h.templ
, we would invoke the script as:
python Expand.py int_stack.table stack.h.templ int_stack.h
Of course, all of these invocations are best put in a Makefile
as:
int_stack.h: int_stack.table stack.h.templ
python Expand.py int_stack.table stack.h.templ int_stack.h
int_stack-private.h: int_stack.table stack-private.h.templ
python Expand.py int_stack.table stack-private.h.templ int_stack-private.h
int_stack.c: int_stack.table stack.c.templ
python Expand.py int_stack.table stack.c.templ int_stack.c