Parameterised Types
Many times, when developing a program, you wish to perform some
operation on more than one data type. For example you may wish to
sort a list of integers, another list of floating point numbers, and a list of
alphabetic strings. It seems silly to have to write a separate sort function
for each of the three types when all three are sorted in the same logical
way. With parameterised types (also called
templates), you will be ableto write a single sort routine that is capable of sorting all three of the
lists.
This is already available in the Ada language as the generic package or
procedure. Because it is available in Ada, there is a software
components industry that provides programmers with pre-written and
thoroughly debugged software routines that work with many different
types. When this is generally available in C++, there will be a
components industry for C++ and precoded, debugged and efficient
source code will be available off the shelf to perform many of the
standard operations. These operations will include such things as sorts,
queues, stacks, lists, etc.
Borland International has included templates in version 3.0 of Borland
C++. The next three example programs illustrate the use of templates
with Borland's compiler, but may not work with other compilers.
#include <stdio.h>
template <class ANY_TYPE>
ANY_TYPE maximum(ANY_TYPE a, ANY_TYPE b)
{
return (a > b) ? a : b;
}
void main()
{
int x = 12, y = -7;
float real = 3.1415;
char ch = 'A';
printf("%8d\n", maximum(x, y));
printf("%8d\n", maximum(-34, y));
printf("%8.3f\n", maximum(real, float(y)));
printf("%8.3f\n", maximum(real, float(x)));
printf("%c\n", maximum(ch, 'X'));
}
The above program [TEMPLAT1.CPP] is the first example of the use
of a template. This program is so simple it seems silly to even bother
with it but it will illustrate the use of the parameterised type. The
template is given in lines 2 to 5 with the first line indicating that it is a
template with a single type to be replaced, the type ANY_TYPE. This
type can be replaced by any type which can be used in the comparison
operation in line 5. If you have defined a class, and you have
overloaded the operator ">", then this template can be used with
objects of your class. Thus, you do not have to write a maximum
function for each type or class in your program. This function is
included automatically for each type it is called with in the program, and
the code itself should be very easy to understand.
You may have realised that nearly the same effect can be achieved
through use of a macro, except that when a macro is used, the strict
type checking is not done. Because of this, and because of the
availability of the inline method capability in C++, the macros that were
covered in Module 813 are almost never used by experienced C++
programmers.
A class template
The next example program [TEMPLAT2.CPP] is a little more involved
since it provides a template for an entire class rather than a single
function. The template code is given in lines 3 to 12 and a little study
will show that this is an entire class definition. This is a very ‘weak’
stack class since there is nothing to prevent popping data from an
empty stack, and there is no indication of a full stack. Our aim,
however, is to illustrate the use of the parameterised type and to do so
using the simplest possible class.
#include <stdio.h>
const int MAXSIZE = 128;
template<class ANY_TYPE>
class stack
{
ANY_TYPE array[MAXSIZE];
int stack_pointer;
public:
stack(void) { stack_pointer = 0; };
void push(ANY_TYPE in_dat){ array[stack_pointer++] = in_dat;
};
ANY_TYPE pop(void { return array[--stack_pointer]; };
int empty(void) { return (stack_pointer == 0); };
}
char name[] = "John Herkimer Doe";
void main()
{
int x = 12, y = -7;
float real = 3.1415;
stack<int> int_stack;
stack<float> float_stack;
stack<char *> string_stack;
int_stack.push(x);
int_stack.push(y);
int_stack.push(77);
float_stack.push(real);
float_stack.push(-12.345);
float_stack.push(100.01);
string_stack.push("This is line 1");
string_stack.push("This is the second line");
string_stack.push("This is the third line");
string_stack.push(name);
printf("Integer stack ---> ");
printf("%8d ", int_stack.pop());
printf("%8d ", int_stack.pop());
printf("%8d\n", int_stack.pop());
printf(" Float stack ---> ");
printf("%8.3f ", float_stack.pop());
printf("%8.3f ", float_stack.pop());
printf("%8.3f\n", float_stack.pop());
printf("\n Strings\n");
do {
printf("%s\n", string_stack.pop());
} while (!string_stack.empty());
}
In the main program we create an object named int_stack in line 20
which will be a stack designed to store integers, and another object
named float_stack in line 21 which is designed to store float type
values. In both cases, we enclose the type we desire this object to work
with in "<>" brackets, and the system creates the object by first
replacing all instances of ANY_TYPE with the desired type, then
creating the object of that type. You will note that any type can be used
that has an assignment capability since lines 10 and 11 use the
assignment operator on the parameterised type.
Even though the strings are all of differing lengths, we can even use the
stack to store a stack of strings if we only store a pointer to the strings
and not the entire string. This is illustrated in the object named
string_stack declared in line 22 and used later in the program.
Reusing the stack class
The program below [TEMPLAT3.CPP] uses the same class with the
template as defined in the last program but in this case, it uses the date
class developed earlier as the stack members. More specifically, it uses
a pointer to the date class as the stack member.
#include <stdio.h>
#include "date.h"
const int MAXSIZE = 128;
template<class ANY_TYPE>
class stack
{
ANY_TYPE array[MAXSIZE];
int stack_pointer;
public:
stack(void) { stack_pointer = 0; };
void push(ANY_TYPE in_dat){ array[stack_pointer++] = in_dat;
};
ANY_TYPE pop(void) { return array[--stack_pointer]; };
int empty(void) { return (stack_pointer == 0); };
char name[] = "John Herkimer Doe";
void main()
{
stack<char *> string_stack;
stack<date *> class_stack;
date cow, pig, dog, extra;
class_stack.push(&cow);
class_stack.push(&pig);
class_stack.push(&dog);
class_stack.push(&extra);
string_stack.push("This is line 1");
string_stack.push("This is the second line");
string_stack.push("This is the third line");
string_stack.push(name);
for (int index = 0 ; index < 5 ; index++) {
extra = *class_stack.pop();
printf("Date = %d %d %d\n", extra.get_month(),
extra.get_day(), extra.get_year());
};
printf("\n Strings\n");
do {
printf("%s\n", string_stack.pop());
} while (!string_stack.empty());
}
Because class assignment is legal, you could also store the actual class
in the stack rather than just the pointer to it. To do so, however, would
be very inefficient since the entire class would be copied into the stack
each time it is pushed and the entire class would be copied out again
when it was popped. Use of the pointer is a little more general, so it
was illustrated here for your benefit.
All three of the previous programs can be compiled and executed if you
have Borland C++ version 3.0 (or later). Other compilers may not work
with these programs since parameterised types are not yet a part of the
C++ specification.
Exception handling
A future version of C++ will have some form of exception handling to
allow the programmer to trap errors and prevent the system from
completely shutting down when a fatal error occurs. The Ada language,
for example, allows the programmer to trap any errors that occur (even
system errors), execute some recovery code, and continue with
program execution in a well-defined way. AT&T and the ANSI-C++
committee have announced that some form of exception handling will
be implemented in the next C++ standard, but have not stated what
form it will take.
Links: Home C Programming Guide C++ programming Guide