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 able

to 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

Contact Me