home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Elysian Archive
/
AmigaElysianArchive.iso
/
prog
/
c
/
cpp.lha
/
paper.mss
< prev
next >
Wrap
Text File
|
1991-04-15
|
33KB
|
692 lines
.nr PS 10
.nr VS 12
.nr PD .7v
.nr LL 6.5i
.TL
A Portable Implementation of Parameterized Templates
Using A Sophisticated C++ Macro Facility
.AU
Mary Fontana
LaMott Oren
.AI
Texas Instruments Incorporated
Computer Science Center
Dallas, Texas, 75265
.AU
Martin Neath
.AI
Texas Instruments Incorporated
Information Technology Group
Austin, Texas, 78759
.AB
The Texas Instruments C++ Object-Oriented Library (COOL) is a collection of
classes, templates and macros for use by C++ programmers writing complex
applications. Parameterized types, symbolic computing and exception handling
are significant features of COOL which improve the development capabilities
available to the programmer. These features are implemented in COOL with a
sophisticated C++ macro facility. This paper describes the COOL macro
facility, discusses how support for parameterized templates is built upon it,
and provides details of two programmer interfaces (both implemented) for easy
use of parameterized templates in application programs.
.AE
.NH 1
Introduction
.LP
The Texas Instruments C++ Object-Oriented Library (COOL) is a
system-independent software platform consisting of classes, templates and
macros for use by C++ programmers writing complex applications. It is designed
to raise the level of abstraction for the programmer in order to facilitate
concentration on the problem domain, not on implementing basic data structures,
macros, and classes. Parameterized templates, symbolic computing, and
exception handling are significant features of COOL that substantially improve
the development capabilities available. We wished to provide these facilities
in a compiler- and machine-independent manner across several hardware
platforms. We examined the macro language found in standard C-preprocessors
and determined that it was insufficient for implementing these features. As a
result, we developed the COOL macro facility to allow the programmer to define
powerful extensions to the C++ language in a seamless and unobtrusive manner.
This macro facility is implemented as an extension to a C preprocessor [1].
The modifications made to the preprocessor are both portable and compiler
independent. This paper describes this enhanced macro facility, discusses how
parameterized templates is built upon it, and provides details of two
programmer interfaces (both implemented) for easy use of parameterized
templates. For an overview of COOL see the paper,
.I
COOL - A C++ Object-Oriented Library
.R
[2].
For complete details, see the reference document,
.I
COOL User's Manual
.R
[3].
.FS
The authors may be reached via electronic mail at fontana@csc.ti.com,
oren@csc.ti.com, and neath@itg.ti.com.
.FE
.NH 1
The COOL Preprocessor and the defmacro Keyword
.LP
Many C++ language implementations separate the preprocessor and compiler
functions into two separate programs. Others combine the preprocessor and
compiler into one step. Since we needed a portable utility to massage C++
source code that works under both scenarios and executes after include files
and standard preprocessor directives have been expanded, but before the C++
compiler itself begins parsing, we decided to modify a C-preprocessor to
support the COOL C++ language extensions. Thus, the COOL preprocessor is
derived from and based upon a public domain C-preprocessor (the DECUS C
preprocessor) made available by the DEC User's group and supplied on the X11R3
source tape from MIT. It has been modified to comply with the draft ANSI C
specification with the exception that trigraph sequences are not supported.
The draft-proposed ANSI C standard indicates that extensions and changes to the
language or features implemented in a preprocessor or compiler should be made
by using the \fB#pragma\fR keyword. The COOL preprocessor recognizes a
\fB#pragma defmacro\fR declaration and is the single hook through which
features such as the class macro, parameterized templates, and polymorphic
enhancements have been implemented. The \fBdefmacro\fR keyword provides a way
to define and execute arbitrary filter programs or macro expanders on C++ code
fragments. The syntax of the \fBdefmacro\fR declaration is:
.DS I
\fB#pragma \fBdefmacro \fIname \fB"\fIprogram\fB"\fI options\fR
\fB#pragma \fBdefmacro \fIname \fB<\fIprogram\fB>\fI options\fR
.DE
where \fIname\fR is the name of the macro, \fIprogram\fR is either the name of
an executable file or the name of an internal preprocessor function which
implements the macro expansion, and \fIoptions\fR are any combination of the
following optional parameters:
.DS I
\fBrecursive\fR - the macro may be recursively expanded.
\fBexpanding\fR - the input to the macro is expanded.
\fBdelimiter= \fIx\fR - the default delimiter (semi-colon) is replaced with \fIx\fR.
.DE
Unknown \fIoptions\fR are passed as arguments to the macro expander named
\fIprogram\fR. This provides the necessary handle through which COOL functions
and language extensions can be identified. For example, the \fBMACRO\fR
and \fBtemplate\fR keywords are defined in a top-level header file with:
.DS I
#pragma defmacro MACRO "macro" delimiter=} recursive
#pragma defmacro template "template" delimiter=}
.DE
The implementation of the macro expander program may be either external or
internal to the preprocessor. Fundamental COOL macro-expanders are implemented
internal to the COOL preprocessor for the sole reason of providing a more
efficient execution profile to reduce compile time for the application
programmer. When the preprocessor encounters a \fBdefmacro\fR declaration in
the source code, it searches first for an executable file named \fIprogram\fR
on the same search path as that used for include files. If a match is not
found, it then searches for \fIprogram\fR in an internal preprocessor function
table. If a match is still not found, an error is reported indicating that the
macro expander could not be found. This search order allows an internal
preprocessor definition to be overridden by an external one.
When a \fBdefmacro\fR name is successfully recognized, the name and all
following characters upto and including the delimiter character (including all
matching and nested levels of \fB{} [] () <> "" ''\fR and comments found along
the way) are piped into the standard input of the macro expander program. The
expander program performs whatever function(s) is appropriate and the resulting
massaged character stream is piped back onto the standard output of the macro
expander. This output stream is scanned as new input by the preprocessor for
any further processing that might be necessary. The original text in the source
file is replaced with the preprocessor output before being sent onto the C++
compiler. The expansion replacing a \fBdefmacro\fR name in the source code is
C++ 2.0 syntax acceptable to any conforming C++ translator or compiler [4].
.NH 1
The MACRO Keyword
.LP
The COOL \fBMACRO\fR keyword provides a powerful and flexible macro
capability used to implement and simplify many of the features and functions
contained in the library. A \fBdefmacro\fR named \fBMACRO\fR (all uppercase)
provides an enhanced \fB#define\fR macro that supports multi-line, arbitrary
length, nested macros and cpp-directives with positional, optional, optional
keyword, required keyword, rest, and body arguments. \fBMACRO\fR has the
following syntax:
.DS I
\fBMACRO \fIname\fR \fB( \fIparmlist\fR\fB ) { \fIbody\fR \fB}\fR
\fIparmlist\fR := [\fBKEY:\fR | \fBREST:\fR | \fBBODY:\fR] \fIidentifier\fR [\fB=\fR \fIidentifier\fR] [\fB,\fR \fIparmlist\fR]
.DE
where \fIname\fR is the name of the macro, \fIparmlist\fR is a list of
parameters separated by commas, and \fIbody\fR is the code which replaces the
\fBMACRO\fR reference. The \fIparmlist\fR specification allows positional,
keyword, rest, and body parameters to be identified by the programmer. The
positional and keyword parameters may be required or optional. Optional
parameters are supported by use of an equal sign followed by an
\fIidentifier\fR that specifies the default value. All the optional
positional parameters must follow all of the required positional ones.
When \fBKEY:\fR is specified in the \fIparmlist\fR, all parameters which follow
are keyword parameters. Keyword parameters are position-independent
parameters. A keyword parameter is provided a value in an argument list by
supplying the keyword name followed by an equal sign and the argument value.
\fBREST:\fR in the \fIparmlist\fR indicates that the remaining parameters are
referenced by one named \fIidentifier\fR. An optional equal sign followed by
an \fIidentifier\fR sets the \fIidentifier\fR after the equal sign to the
number of arguments remaining. Finally, \fBBODY:\fR in the \fIparmlist\fR
indicates that the parameter which follows is expanded to include all the
source code within the braces after the \fBMACRO\fR call. This is useful for
passing a source code fragment onto other nested \fBMACRO\fRs. Examples of these
three types of arguments are given below.
.NH 2
MACRO Examples
.LP
The following examples show some of the power and flexibility of \fBMACRO\fR.
This first example uses both positional parameters and keyword parameters.
.DS I
MACRO set_val (size, value=NULL, KEY: low=0, high) {
init (size, value, low, (high-low))
}
.DE
\fBset_val\fR has three parameters: \fIsize\fR is a required positional
parameter, \fIvalue\fR is an optional positional parameter that if not
specified in a particular call has a default value of \fBNULL\fR, \fIlow\fR is
an optional keyword parameter with a default value of \fB0\fR, and \fIhigh\fR is
a required keyword parameter. In this example, the expansion calls the
function \fBinit\fR with four arguments. The following shows several expansions
of \fBset_val\fR.
.DS I
set_val (0, high=20) ----> init (0, NULL, 0, (20-0))
set_val (0, low=5, high=15) ----> init (0, NULL, 5, (15-5))
set_val (1, 2, high=25) ----> init (1, 2, 0, (25-0))
.DE
The next example uses the \fBREST:\fR parameter. Note that there are two
\fBMACRO\fRs defined: \fBbuild_table\fR calls \fBbuild_table_internal\fR
to do most of the work.
.DS I
MACRO build_table (name, REST: rest) {
char* name[] = { build_table_internal(rest) NULL}
}
MACRO build_table_internal (first, REST: rest=count) {
#first,
#if count
build_table_internal (rest)
#endif
}
.DE
\fBbuild_table\fR has two parameters: \fIname\fR is the name of the table of
\fBchar*\fR's and \fIrest\fR refers to all the remaining arguments.
\fBbuild_table\fR calls \fBbuild_table_internal\fR passing its \fIrest\fR
argument. Note that this call is embedded within the initialization braces of
the table and is followed by a \fBNULL\fR. In \fBbuild_table_internal\fR,
\fIfirst\fR is set to the first argument of the \fIrest\fR argument list in the
invoking macro call, and the remaining \fIcount\fR arguments are left in
\fIrest\fR. \fBbuild_table_internal\fR uses the ANSI \fB#\fR character on
\fIfirst\fR to double quote the value. A conditional clause tests \fIcount\fR
to see if there are remaining arguments. If \fIcount\fR is non-zero, the macro
is called recursively with the remaining arguments. When there are no more
arguments, \fBbuild_table\fR regains control and appends the \fBNULL\fR
character and closing brace to the result of \fBbuild_table_internal\fR.
A sample use of \fBbuild_table\fR is shown below to illustrate the construction
of a \fBNULL\fR-terminated table containing character strings. The first line
shows the macro call and the second shows the resulting expansion.
.DS I
build_table (table, 1,2,3,4,5,6,7);
.DE
expands to:
.DS I
char* table[] = {"1", "2", "3", "4", "5", "6", "7", NULL};
.DE
This last example uses the \fBBODY:\fR parameter and also takes advantage of
the current position feature found in the COOL container classes [2]. This is
used to implement a general purpose loop macro similar to that found in Common
LISP [5].
.DS I
MACRO LOOP (type, identifier, object, BODY: body) {
{ type identifier;
for ( object.reset(); object.next(); ) {
identifier = object.value();
body
}
}
}
.DE
\fBLOOP\fR has four parameters: \fItype\fR is the type of each element in a
container class (such as, \fBint\fR), \fIidentifier\fR is the name of a
variable to be declared of the given type, \fIobject\fR is the name of a
container class instance, and \fIbody\fR is the body of code to apply on each
element in the container object. A specific example for the parameterized
\fBList<int>\fR class is shown below.
.DS I
extern List<int> list1;
LOOP (int, var1, list1) { cout << var1; }
.DE
expands to:
.DS I
extern List<int> list1;
{ int var1;
for ( list1.reset(); list1.next(); ) {
var1 = list1.value();
cout << var1;
}
}
.DE
In this example, \fBlist1\fR is an instance of \fBList<int>\fR which is a
container class representing a list of integers. \fBLOOP\fR takes this list
object and iterates through the elements, assigning each to a temporary integer
variable \fBvar1\fR and printing its value. The net result will print all
elements in the list.
.NH 1
COOL Parameterized Templates
.LP
One of the main uses of the COOL macro facility is the implementation
of \fBtemplate\fR, \fBDECLARE\fR and \fBIMPLEMENT\fR for supporting
parameterized templates. The syntax of the \fBtemplate\fR grammar is that as
specified by Stroustrup in his paper,
.I
Parameterized Types for C++
.R
[6].
COOL fully implements this functionality such that there will be minimal source
code conversion necessary when this feature is finally implemented in the C++
language. COOL provides templates for a number of parameterized classes (such
as, \fBRange\fR and \fBIterator\fR) and container classes (such as,
\fBVector\fR, \fB List\fR, \fBBinary_Tree\fR and \fBHash_Table\fR) which are
described in
.I
COOL User's Guide
.R
[3].
.NH 2
The template Keyword
.LP
The \fBtemplate\fR keyword provides a mechanism for defining parameterized
classes. A parameterized class is a type-independent class. A typical use is
a container class where the type of the contained object is specified at
compile-time. For example, vectors can be declared to hold a specific type
of element, such as, a vector of integers or a vector of doubles,
from a single parameterized class, \fBVector<\fItype\fB>\fR.
A \fBtemplate\fR is divided into the declarative part and the implementation
part of a class. The declarative part may occur many times in an application
and is analogous to including a header file for a class which contains the
class definition and its inline member functions. The implementation part is
analogous to the file that contains the source code implementing the member and
friend functions of the class. COOL provides four variations of \fBtemplate\fR
for these two parts:
.DS I
\fBtemplate\fR <\fBclass\fR \fItype\fR [, \fIparms\fR]> \fBclass\fR \fIname\fR<\fItype\fR> { \fIclass_description\fR };
.RS
Defines the class template for the declarative part of the \fIname\fR class.
.RE
\fBtemplate\fR <\fBclass\fR \fItype\fR [, \fIparms\fR]> \fBinline\fR \fIresult name\fR<\fItype\fR>::\fIfunction\fR { ... };
.RS
Defines an inline member function for the declarative part of the \fIname\fR
class.
.RE
\fBtemplate\fR <\fBclass\fR \fItype\fR [, \fIparms\fR]> \fIresult\fR \fIname\fR<\fItype\fR>::\fIfunction\fR { ... };
.RS
Defines a member function for the implementation part of the \fIname\fR class.
.RE
\fBtemplate\fR <\fBclass\fR \fItype\fR [, \fIparms\fR]> \fIname\fR { \fIanything\fR };
.RS
Defines anything else associated with a template for the \fIname\fR class.
.RE
.DE
This last form is used to define such things as \fBtypedef\fRs or friend
functions of a parameterized class. When this form is found before the class
template, the contents are expanded before the class definition. When this
form is found after the class template, the contents are expanded as part of
the class implementation. Note that this form is not part of the parameterized
type syntax described by Stroustrup [6]. Rather, it is something we found
lacking in the original proposal and found very useful in several COOL
container classes for defining predicate types for the class under C++ 2.0.
Another use of this form is to provide automatic declarations of nested
parameterized classes, that is, to declare a parameterized class for a class
template which is itself derived from another parameterized class template.
Each variation of \fBtemplate\fR allows additional optional parameters
with the following syntax:
.DS I
\fIparms\fR ::= \fItype name\fR [\fB=\fR \fIvalue\fR] [\fB,\fR \fIparms\fR]
.DE
where \fItype\fR is the type of the parameter, (such as, \fBclass\fR or
\fBint\fR); \fIname\fR is the name of the parameter that is substituted when
\fBtemplate\fR is expanded; and \fIvalue\fR is the default value of parameter
\fIname\fR.
The following is an example of \fBtemplate\fR for the class,
\fBVector<\fItype\fB>\fB.
.DS I
template <class Type> Vector { // predicate functions
typedef int (*Vector_##Type##_Predicate) (const Type&, const Type&);
typedef Boolean (*Vector_##Type##_Compare) (const Type&, const Type&);
};
.DE
.DS I
template <class Type> class Vector<Type> { // Parameterized Vector class
private:
Type* v; // Vector of pointer to Type
int num_elements; // Element count
int size; // Size of vector object
public:
Vector<Type> (); // Empty constructor
Vector<Type> (int); // Constructor with size
Vector<Type> (const Vector<Type>&); // Constructor with reference
~Vector<Type> (); // Destructor
inline Type& operator[](int n); // Operator[] overload for Type
.DE
.DS I
... // ... other member functions ...
};
.DE
.DS I
template <class Type> // Overload operator []
inline Type& Vector<Type>::operator[] (int n) {
return this->v[n];
}
.DE
.DS I
template <class Type> // Constructor with size
Vector<Type>::Vector<Type> (int n) {
this->v = new Type[n];
this->size = n;
this->num_elements = 0;
}
... // ... other member functions ...
.DE
.NH 2
An Initial Programmer Interface: DECLARE and IMPLEMENT
.LP
As stated earlier, a \fBtemplate\fR for a parameterized class is divided into a
declarative part and an implementation part. In our first attempt at
implementing parameterized template support, the programmer creates instances
of a parameterized class using \fBDECLARE\fR to expand the declarative part and
\fBIMPLEMENT\fR to expand the implementation part. \fBDECLARE\fR defines the
parameterized class for a specific type and \fBIMPLEMENT\fR generates the
member functions supporting this type-specific class. \fBDECLARE\fR must be
used in every file that includes or makes use of the parameterized class.
\fBIMPLEMENT\fR must be used only once in the application for each type over
which the class is parameterized; otherwise the linker will generate errors
about multiple versions of the same member functions. For example, to create a
vector of doubles, the following would be used:
.DS I
#include <Vector.h>
DECLARE Vector<double>;
IMPLEMENT Vector<double>;
Vector<double> vs(30);
.DE
\fBDECLARE\fR expands to code which defines a vector class of doubles and its
associated inline member functions. \fBIMPLEMENT\fR causes a class definition
with its associated member functions to be generated and expanded in the file.
When compiled, this causes the class \fBVector_double\fR to be declared and
defined. One drawback of the use of \fBIMPLEMENT\fR, however, is the fact that
the \fIentire\fR class with all its member functions is generated and linked
into the program image, even if the programmer only requires the use of two or
three member functions. This problem can be avoided by the use of the COOL C++
Control Program (\fBCCC\fR) discussed below. Continuing with the example above,
the \fBtemplate\fR for the \fBVector<\fItype\fB>\fR class for doubles would
expand to the following code:
.DS I
// predicate functions
typedef int (*Vector_double_Predicate) (const double&, const double&);
typedef Boolean (*Vector_double_Compare) (const double&, const double&);
.DE
.DS I
class Vector_double { // Parameterized Vector class
private:
double* v; // Vector of pointer to double
int num_elements; // Element count
int size; // Size of vector object
public:
Vector_double (); // Empty constructor
Vector_double (int); // Constructor with size
Vector_double (const Vector_double&); // Constructor with reference
~Vector_double (); // Destructor
inline double& operator[](int n); // Operator[] overload for double
.DE
.DS I
... // ... other member functions ...
};
.DE
.DS I
// Overload operator []
inline double& Vector_double::operator[] (int n) {
return this->v[n];
}
.DE
.DS I
// Constructor with size
Vector_double::Vector_double (int n) {
this->v = new double[n];
this->size = n;
this->num_elements = 0;
}
... // ... other member functions ...
Vector_double vs(30);
.DE
Declarations of nested parameterized types and the use of non-type arguments in
a template definition are also supported. For example, it is possible to
declare a vector of vectors of ints with \fBVector<Vector<int>>\fR. In
addition, a class template derived from another class template is supported,
that is, a type parameter in one template class can be used to declare another
class template of that type. For example, the COOL
\fBAssociation<\fIT1\fR,\fIT2\fB>\fR class is
a parameterized container class that takes two type arguments, \fIT1\fR and
\fIT2\fR. The header file for this class has the following templates.
.DS I
template <class T1, T2> Association {
DECLARE Pair<T1, T2>; // Declare Pair object type
DECLARE Vector<Pair<T1, T2>>; // Declare Vector of Pairs
}
... // ...
// Association<T1,T2> class definition here
.DE
.DS I
template <class T1, T2> Association {
IMPLEMENT Pair<T1, T2>;
IMPLEMENT Vector<Pair<T1, T2>>;
}
.DE
By using \fBtemplate\fR in this manner, \fBDECLARE\fR for the
\fBAssociation<\fIT1\fR,\fIT2\fB>\fR class invokes \fBDECLARE\fR on the correct
types for the \fBPair<\fIT1\fR,\fIT2\fB>\fR and \fBVector<\fIType\fB>\fR
classes. Likewise, \fBIMPLEMENT\fR for the
\fBAssociation<\fIT1\fR,\fIT2\fB>\fR class invokes \fBIMPLEMENT\fR for the
\fBPair<\fIT1\fR,\fIT2\fB>\fR and \fBVector<\fIType\fB>\fR classes.
Non-type arguments as template parameters are used to provide guidelines to be
used when a template is expanded. For example, the
\fBN_Tree<\fINode\fR,\fIType\fR,\fInchild\fB>\fR class in COOL takes as
arguments a node type (either static or dynamic), a type specifying the
value-type each node will hold, and an argument that specifies the number of
initial subtrees (or children) each node is to have. The node argument is
itself the name of a parameterized class and a nested parameterized template
definition is automatically generated based upon the supplied type and number
arguments. As such, a single template can be used to generate several different
classes with different behaviors and features.
.NH 2
A Revised Programmer Interface: COOL C++ Control Program (CCC)
.LP
The \fBDECLARE\fR and \fBIMPLEMENT\fR macros discussed above were the first
programmer interface implemented for parameterized template support. We soon
discovered, however, that this macro expansion mechanism had two serious
problems. First, the type over which a class was parameterized would have to
support all operators used in the template, even if not applicable or needed.
For example, the COOL \fBList<\fIType\fB>\fR class has several member functions
that use \fBoperator<\fR. However, if what the programmer needs is a list of
window objects and does not ever use \fBList<\fIType\fB>\fR member functions
that require \fBoperator<\fR, compile-time errors from the offending functions
that got macro-expanded are nevertheless generated. Second, with the simplistic
linkers available on many operating systems today, an application gets all of
these member functions linked into the executable image. Typically, an
application uses only a small percentage of the member functions of a
parameterized class. The remaining unused member functions are useless
overhead, increasing program size and memory requirements.
A revised programmer interface for parameterized templates was implemented to
resolve these problems and centers around a new program to be used as the main
interface between the user and the preprocessor/compiler in a make file. This
program, the COOL C++ Control program (\fBCCC\fR), augments the standard
\fBCC\fR script. For most operations, user options and command line arguments
are passed straight through to the underlying CC program. However, when the
\fB-X\fR option is specified, the \fBCCC\fR program goes to work in the
following manner. As Stroustrup[6] suggested, \fB-X"Foo<Bar>"\fR is used on
the command line to indicate that the programmer wants to parameterize class
\fBFoo<\fIType\fB>\fR over some type \fBBar\fR. Additional options for include
file search path and a user-defined library archive are required as described
below. \fBCCC\fR finds the header file(s) implementing class Foo and type Bar,
then proceeds to define that type for the compiler. It then \fIfractures\fR
the implementation of this new type along template boundaries, placing each
non-inline member function in a separate source file, compiling it, and putting
the resulting object file in a user-specified library archive. If a particular
operator is not defined for the type over which the class is parameterized (as
with the example of \fBoperator<\fR above), a compile time error for that one
file is generated. However, the remaining member functions, one in each
fractured template, are still compiled and added to the user library.
For each parameterized class in an application, \fBCCC\fR fractures the
parameterized class definition along template boundaries, causing each
\fBtemplate\fR specifying a member function of the parameterized class to be
compiled into a separate object file. These separate object files are then
added to an application-specific object library. Since each member function is
in its own object module in the library, only those member functions actually
used in the application are linked into the final executable image. To use
\fBCCC\fR, the programmer specifies a library name, one or more header files
containing templates, and specific parameterized classes as command line
arguments to \fBCCC\fR. Other arguments are passed on unchanged to the C++
compiler and system linker. A single invocation of \fBCCC\fR can either
process a parameterized class type or compile a C++ source file, but not both.
For example,
.DS I
CCC -lapp -c List.h String.h -X "List<String>"
.DE
expands the template for a list of strings. The resulting object files from
the fractured parameterized \fBList<\fIType\fB>\fR class are stored in
the library, \fBlibapp.a\fR. The \fB-c\fR option is passed to the compiler to
indicate that it should not continue with the link phase. The library archive
\fBlibapp.a\fR is added to the list of libraries specified in the make file
to be searched during the link step.
The net result is a library archive containing object files, each implementing
one member function for the parameterized class and type. This process solves
the two problems identified above with the use of the macros \fBDECLARE\fR and
\fBIMPLEMENT\fR. First, operators not defined for a type cause compile-time
errors on that one file. Once a parameterized class has been implemented and
provided in a library, compile errors will only occur when a type is selected
that does not have all operators implemented. The user of the class will see
these, and if the member function in question is required, s/he can add that
necessary operator to the type class. Second, only member functions actually
used in an application are linked into the final executable image.
.NH 2
Future Improvements to CCC
.LP
\fBCCC\fR essentially provides a more sophisticated version of the
\fBIMPLEMENT\fR macro discussed above. However, the programmer is still
required to place the \fBDECLARE\fR macro in the appropriate files. One
option under consideration to resolve this problem is the use of a command line
switch similar to the +e0/+e1 switchs on the AT&T cfront translator. Under this
scenario, the equivalent of the +e0 option would be used to declare the type for
a parameterized class and generate the inline member functions (as the
\fBDECLARE\fR macro does) but not to generate the remaining member functions.
The programmer would use the equivalent of the +e1 option on one source file to
cause the remaining non-inline member functions to be generated and placed in a
library archive.
A second problem with \fBCCC\fR concerns the specification of nested
parameterized classes. A programmer should be able to use
\fB-X"Vector<List<int>>"\fR on the command line to specify creation of nested
parameterized classes. Currently, \fBCCC\fR does not handle this case
appropriately. A more sophisticated command line parser should be able to
recognize and implement nested types before trying to expand the outer most
parameterized class.
.NH 1
Conclusion
.LP
The COOL macro facility provides a mechanism to implement significant language
features and extensions for C++ that are unavailable with current language
implementations. The macro facility is implemented in an enhanced preprocessor
that is both efficient and portable, thus allowing for delivery of enhanced
language features on many platforms. This macro extension is at the heart of
the parameterized templates functionality. \fBCCC\fR is used in place of the
normal procedure for controlling the compilation process. It provides all of
the functionality of the original \fBCC\fR program with additional support for
the COOL preprocessor and parameterized type expansion. Finally, the
preprocessor provides an ideal mechanism for quickly prototyping and testing
additional language functions and syntax without requiring access to or
modification of a compiler.
.NH 1
Status of COOL
.LP
Texas Instruments has been using the enhanced macro facility and the
implementation of parameterized templates internally on several projects for
the last year. Many classes and programs have been successfully designed and
implemented, taking full advantage of the power of parameterized templates and
the enhanced macro facility. In particular, we have found that the use of a
class library supplying many basic parameterized container classes
significantly increases the productivity of the programmer, enabling
applications to be prototyped in a shorter time period than might otherwise be
possible. COOL is currently up and running on a Sun SPARCstation 1 (TM)
running SunOS (TM) 4.x,
.FS
SunOS and SPARCstation 1 are trademarks of Sun Microsystems, Inc.
.FE
a PS/2 (TM)
.FS
PS/2 is a trademark of International Business Machines Corporation.
.FE
model 70 running SCO XENIX\(rg
.FS
XENIX is a registered trademark of Microsoft Corporation.
.FE
2.3, a PS/2 model 70 running OS/2 1.2, and a MIPS running RISC/os 4.0. The
SPARC and MIPS ports utilize the AT&T C++ translator (cfront) version 2.1 and
the XENIX and OS/2 ports utilize the Glockenspiel translator 2.0a with the
Microsoft C 6.0 compiler.
The COOL preprocessor source code is available in compressed tar(1) format in
the file /pub/cpp.tar.Z via anonymous FTP from CSC.TI.COM (128.247.159.141).
Permission is granted to any individual or institution to use, copy, modify,
and distribute this software, provided that all copyright statements and
permission notices are maintained, intact, in all copies and supporting
documentation. Texas Instruments Incorporated provides this software "as is"
without express or implied warranty.
.NH 1
References
.IP [1]
Brian Kernighan and Dennis Richie,
.I
The C Programming Language,
.R
Second Edition, Printice-Hill, Englewood Cliffs, NJ, 1988.
.IP [2]
Mary Fontana, Martin Neath and Lamott Oren,
.I
COOL - A C++ Object-Oriented Library,
.R
Information Technology Group, Austin, TX, TI Internal Document, Original Issue January 1990.
.IP [3]
Texas Instruments Incorporated,
.I
C++ Object-Oriented Library User's Manual,
.R
Information Technology Group, Austin, TX, TI Internal Document, Original Issue January 1990.
.IP [4]
AT&T Incorporated,
.I
C++ Language System Release 2.0,
.R
AT&T Product Reference Manual Select Code 307-146, 1989.
.IP [5]
Guy L. Steele Jr,
.I
Common LISP: The Language,
.R
Second Edition, 1990.
.IP [6]
Bjarne Stroustrup,
.I
Parameterized Types for C++,
.R
Proceedings of the USENIX C++ Conference, Denver, CO, October 17-21, 1988,
pp. 1-18.