Templates in C++ and Java
Download
Report
Transcript Templates in C++ and Java
Templates/Generics in
C++ and Java
By:
Dariusz Gawdzik
1
Some Motivation
Suppose you want to develop a general
container such as stack in C++ or Java.
One option is to build one container for a
specific data type (i.e.. One for integers, one
for doubles, one for booleans ).
The following is the implementation of stack
that holds integers (parts in red font are due
to our choice of the stack’s data type on
which it operates ).
2
IntegerStack.h
#pragma once
class IntegerStack
{
public:
IntegerStack(int capacity);
~IntegerStack(void);
void Push(int i);
int Pop();
int Peek() const;
bool Empty() const;
bool Full() const;
void Print() const;
private:
int top;
int capacity;
int *array;
};
3
IntegerStack.cpp
#include "integerstack.h"
IntegerStack::IntegerStack(int capacity)
{
assert(capacity > 0);
top = 0;
this->capacity = capacity;
array = new int[capacity];
assert(Empty());
}
IntegerStack::~IntegerStack(void)
{
delete [] array;
}
bool
IntegerStack::Empty() const{
return top == 0;
}
4
IntegerStack.cpp (continued )
bool
IntegerStack::Full() const{
return top == capacity;
}
int
IntegerStack::Peek() const{
return array[top-1];
}
void
IntegerStack::Push(int i) {
assert(! Full());
array[top++] = i;
assert( ! Empty() );
assert( Peek() == i );
}
int
IntegerStack::Pop(){
assert(! Empty());
return array[--top];
}
5
How About Stack of
Doubles?
We can just modify the stack we already
built for integers changing the
definitions in red on previous slides from
int to double.
6
How About Stack of
Doubles?
Doing it this way would create a lot of stacks
one for every data type. Not really a good
solution especially if in the future we want to
modify the way the stack is implemented.
This is a violation of single choice principle
which states that a software system that
supports a set of alternatives should limit the
knowledge of the alternatives to only one
module.
7
How About Stack of
Doubles?
But we can do better in C++. We can create
a stack that operates on a structure that has
as one of its variables union of all the
primitive data types available in C++. Also,
as one of the fields of the union we add a
variable of type void * (this way we can insert
any pointer into the structure without explicit
cast). So, in fact, we can insert any object we
care to create. This is what we want, at least
at first sight.
8
GenericStack.h
#pragma once
enum type {INTEGER, CHARACTER, BOOLEAN, DOUBLE_PRECISION,
SINGLE_PRECISION, VOID_POINTER };
struct item {
type data_type;
union{
int integer;
//more primitive data types ommited...
char character;
bool boolean;
double double_precision;
float single_precision;
void *void_pointer;
}data;
};
9
GenericStack.h (continued )
class GenericStack
{
public:
GenericStack(int capacity);
~GenericStack(void);
void Push(item i);
item Pop();
item Peek() const;
bool Empty() const;
bool Full() const;
void Print() const;
friend ostream & operator << (ostream &, const item &);
private:
int top;
int capacity;
item *array;
};
10
GenericStack.cpp
#include "genericstack.h"
GenericStack::GenericStack(int capacity)
{
assert(capacity > 0);
top = 0;
this->capacity = capacity;
array = new item[capacity];
assert(Empty());
}
GenericStack::~GenericStack(void)
{
delete [] array;
}
11
GenericStack.cpp ( continued )
bool
GenericStack::Empty()const{
return top == 0;
}
bool
GenericStack::Full() const{
return top == capacity;
}
item
GenericStack::Peek() const{
return array[top-1];
}
12
GenericStack.cpp ( continued )
void
GenericStack::Push(item i) {
assert(! Full());
array[top++] = i;
assert( ! Empty() );
}
item
GenericStack::Pop(){
assert(! Empty());
return array[--top];
}
void
GenericStack::Print() const{
cout << "Generic Stack content: " << endl;
cout << "|-----------------|"<<endl;
for(int i = top-1; i >= 0; i--){
cout << " " << array[i] <<"
" << endl;
cout << "|-----------------|"<<endl;
}
}
13
GenericStack.cpp (continued )
ostream & operator << (ostream & os, const item & x)
{
switch (x.data_type ) {
case BOOLEAN :
return os <<x.data.boolean;
break;
case INTEGER:
return os <<x.data.integer;
break;
case CHARACTER:
return os <<x.data.character;
break;
case DOUBLE_PRECISION:
return os <<x.data.double_precision;
break;
case SINGLE_PRECISION:
return os <<x.data.single_precision;
break;
case VOID_POINTER:
return os <<x.data.void_pointer;
break;
default:
return os << "unknown data type";
}
}
14
Is the GenericStack the
Best We Can Do in C++?
The generic stack seems to solve our
problem. After all, we have only one stack for
all of the data types and the single choice
principle is embraced.
But in C++ we can implement a generic stack
using templates. Templates are basically
classes that require parameter(s) in a similar
fashion like functions.
15
Templates in C++
To take the metaphor a little further. Classes
that are not templates are like functions that
do not take any parameters.
The parameter for template is provided at a
time the template is instantiated.
The parameter resolves ambiguity as to what
data type the class/template operates on.
This is best understood by using an example
(how about stack ).
16
TemplateStack.h
template <class G>
class TemplateStack
{
public:
TemplateStack(int capacity);
~TemplateStack(void);
void Push(G i);
G Pop();
G Peek() const;
bool Empty() const;
bool Full() const;
void Print() const;
private:
int top;
int capacity;
G *array;
};
#include "TemplateStack.cpp"
17
TemplateStack.cpp
#include <iostream.h>
#include <assert.h>
template <class G>
TemplateStack<G>::TemplateStack(int capacity)
{
assert(capacity > 0);
top = 0;
this->capacity = capacity;
array = new G[capacity];
assert(Empty());
}
template <class G>
TemplateStack<G>::~TemplateStack(void)
{
delete [] array;
}
template <class G>
bool
TemplateStack<G>::Empty()const{
return top == 0;
}
18
TemplateStack.cpp ( continued )
template <class G>
bool
TemplateStack<G>::Full() const{
return top == capacity;
}
template <class G>
G
TemplateStack<G>::Peek() const{
return array[top-1];
}
template <class G>
void
TemplateStack<G>::Push(G i) {
assert(! Full());
array[top++] = i;
assert( ! Empty() );
}
19
TemplateStack.cpp (continued )
template <class G>
G
TemplateStack<G>::Pop(){
assert(! Empty());
return array[--top];
}
template <class G>
void
TemplateStack<G>::Print() const{
cout << "Generic Stack content: " << endl;
cout << "|-----------------|"<<endl;
for(int i = top-1; i >= 0; i--){
cout << " " << array[i] <<"
" << endl;
cout << "|-----------------|"<<endl;
}
}
20
The syntax of template
Declaration
The class declaration in .h file is
preceded with keyword:
template <class G>
The alternate keyword supported by some
compilers is template <typedef G>
21
The G is the formal generic parameter
in which place we will put a data type
when we instantiate the class.
The class keyword in a misnomer the
actual parameter need not be a class
but can in fact be a primitive type as
well; hence the better name typedef
supported by some compilers.
We need to place the
#include “TemplateStack.cpp”
At the end of TemplateStack.h file.
22
In our class we put the generic
parameter G where we regularly would
put a data type on which stack
operates.
We can instantiate the template stack
as we would a normal class except we
provide the generic parameter.
23
#include <stdio.h>
#include "TemplateStack.h"
int main(int argc, char* argv[])
{
TemplateStack<double> *tStack = new TemplateStack<double>(20);
tStack->Push(13.2);
tStack->Push(2.0);
tStack->Push(1.12);
tStack->Print();
TemplateStack<char *> *chStack = new TemplateStack<char *>(20);
chStack->Push("nice");
chStack->Push("are");
chStack->Push("templates");
chStack->Print();
return 0;
}
24
Script started on Mon Mar 22 16:41:02 2004
2;red:/cs/home/cs973269/cs4301/A3/C++/build 301 build % main
Generic Stack content:
|-----------------|
1.12
|-----------------|
2
|-----------------|
13.2
|-----------------|
Generic Stack content:
|-----------------|
templates
|-----------------|
are
|-----------------|
nice
|-----------------|
302 build % exit exit
script done on Mon Mar 22 16:41:11 2004
25
How about Java?
If you think that Java is a much better
language than C++ you may say that all
the complexities of templates can be
avoided, if as in Java, we have a class
hierarchy where Object is the ultimate
ancestor of all classes.
In Java collections such as stack are
implemented to operate on Objects.
26
Java’s Approach
Let’s have a look at Java collections
and see if we can find some problems
with this approach.
First of all, we can only insert Objects
into Java collections which leaves
primitive types and arrays out of the
loop.
27
Java’s Approach
Yes, we can use wrapper classes for primitive
types but this provides for slower executing
programs and gives programmers another
thing to take care of.
But the biggest problem is with static typing.
Let me illustrate what I mean by the previous
statement.
28
import java.util.Stack;
class Main
{
public static void main(String[] args)
{
Stack st = new Stack();
st.push(new Integer(1));
st.push(new String("good"));
Integer i = (Integer) st.pop();
System.out.println(st);
}
}
%
java Main Exception in thread "main"
java.lang.ClassCastException
at Main.main(Main.java:12)
29
Java and Static typing
The pervious program had a semantic
bug in it. I suppose the programmer
wanted to use the stack for integers and
by mistake inserted the string.
The exception was raised by JVM at run
time and, in this case, it caused the
entire system to crush.
30
Java and static typing
In this case no damage done, but
imagine you are writing application for
auto pilot in an air plane.
You don’t want this kind of semantic bug
to crush the plane nor do you want to
provide for any imaginable possibility in
your exception handler.
31
Java and static typing
The fact is, the semantic bugs are the
hardest to find and to recover from. On
many occasions you will not be able to
provided for recovery in the exception
handler.
Ideally we would want this kind of bug to
be caught at compile time.
32
Java versus C++
Java is an improvement over C++ in
many respects but not in all respects
unfortunately.
The following code is the equivalent of
Java code that caused our system to
crush. It uses our TemplateStack.
33
#include "TemplateStack.h"
int main(int argc, char* argv[])
{
//testing template stack
TemplateStack<int> *iStack = new TemplateStack<int>(20);
iStack->Push(1);
iStack->Push(2);
iStack->Push("some string");
iStack->Print();
return 0;
}
../prog/Main.cpp: In function `int main(int, char **)':
../prog/Main.cpp:17: no matching function for call to `TemplateStack<int>::Push (char[12])'
../lib/TemplateStack.cpp:41: candidates are: void TemplateStack<int>::Push(int)
make: *** [Main.o] Error 1
34
Java versus C++
So the error that caused our system to
crush miserably in Java was detected in
C++ through the use of template class
at compile time (before we released the
auto pilot module that caused the plane
to crush and us to get fired ).
Can we do anything to correct that Java
deficiency?
35
Java and templates
According to Sun Microsystems Inc. adding
Generics (templates) to Java is one of the
most requested extensions to the language:
“This feature is one of the most frequently
requested language extensions on the JDC
(no. 7 on the bug parade - no. 2 among
language extensions). “
(http://java.sun.com/developer/techDocs/New
sletters/2002/nl1113.html)
36
Java 1.5
With Java 1.5, still in Beta version, we
get the support for Generics.
Lets have a look at the basic syntax
which is somewhat similar to the syntax
of C++.
37
public class GenericStack<G> {
/* Public Part */
GenericStack(int capacity){
assert(capacity > 0);
top = 0;
this.capacity = capacity;
array = new Object[capacity];
assert(Empty());
}
public boolean Empty(){
return top == 0;
}
public boolean Full() {
return top == capacity;
}
public G Peek() {
return (G) array[top-1];
}
38
public void Push(G i) {
assert(! Full());
array[top++] = i;
assert( ! Empty() );
}
public G Pop(){
assert(! Empty());
return (G) array[--top];
}
public void Print(){
System.out.println("Generic Stack content: ");
System.out.println("|-----------------|");
for(int i = top-1; i >= 0; i--){
System.out.println(" " + array[i] +"
System.out.println("|-----------------|");
}
}
/* Private Part */
private int top;
private int capacity;
private Object[] array;
}
");
39
Some points
First the java syntax is shorter we just need to
add <G> to the class definition.
There is some deficiency of implementation:
we can’t define G[] array like we could in C++.
This results in defining array of Objects for
private implementation and then casting to
(G) whenever we return value from that array.
When compiling we need to add –source 1.5
switch to javac otherwise the new syntax will
create compile time errors.
40
class Main {
public static void main(String[] args) {
GenericStack<Integer> st = new GenericStack<Integer>(20);
st.Push(1);
st.Push(2);
st.Push(3);
st.Print();
GenericStack<String> s = new GenericStack<String>(20);
s.Push("Java 1.5");
s.Push("in");
s.Push("Generics");
s.Print();
}
}
41
Generic Stack content:
|-----------------|
3
|-----------------|
2
|-----------------|
1
|-----------------|
Generic Stack content:
|-----------------|
Generics
|-----------------|
in
|-----------------|
Java 1.5
|-----------------|
42
Java and generics
The introduction of generics allows as to
define homogeneous containers of
which until now array was the only
representative.
But what happens when we want to
define a container that is not
homogeneous.
43
Java and Generics
Of course there is a simple solution
create Stack<Object>
How about inheritance and generics can
we do this:
List<String> s_list = new ArrayList<String>();
List<Object> o_list = s_list; // is a list of string a
// list of object?
44
Java and Generics
As it turns out we can’t the compiler will
issue incompatible types error.
If we could then this code would
generate error as well.
o_list.add(new Object());
String s = o_list.get(0);
45
Java and Generics
What if we want to specify that our method
takes a stack that has a generic actual
parameter that can be a base or derived
class of certain type (doing
Stack<SomeType> will not work as you saw
previously since Stack<SomeType> is not a
parent of Stack<Child of SomeType> )
void method( Stack<“what goes here?”> arg) {….}
46
Java and Generics
Two solutions:
Define it as:
void method(Stack<? extends Type> arg ){..}
Define it as
void method(Stack<?> arg){...}
where ? Is a wild card which specifies any
type (so method can operate on any stack
what so ever).
47
Java 1.5
Java 1.5 to be soon released makes major
changes to the language.
Besides adding generics it introduces
boxing/unboxing for primitive types so that we
no longer have to have wrapper classes.
It introduces new for loop construct
It introduces variable arguments to the
functions.
It finally introduces enum data type.
48
Short preview
Variable arguments
//args is of type Object[]
void argtest(Object ... args) {
for (int i=0;i <args.length; i++) {
System.out.println(args[i] );
}
}
argtest("test", "data");
49
Short preview
Enumerations
public enum StopLight { red, amber, green };
50
Short preview
New for loop for iterators
Before:
ArrayList<Integer> list = new ArrayList<Integer>();
for (Iterator i = list.iterator(); i.hasNext();) {
Integer value=(Integer)i.next();
}
After:
ArrayList<Integer> list = new ArrayList<Integer>();
for (Integer i : list) {
Integer value=(Integer)i.next();
}
51
Some Theory Behind
Generics
Generics allow to abstract over types.
In object oriented programming class is
viewed as a merge of the notion of module
and type. So, if we don’t have genericity then
every class is a module that is also a type.
With generics a class defined as Stack<G> is
a type pattern covering an infinite set of
possible types which are obtained by
providing the actual generic parameter G.
52
References:
http://java.sun.com/j2se/1.5/pdf/Tigerlang.pdf
Object-Oriented Software Construction
(2nd ed) by Bertrand Meyer. (Bible for
OOP).
http://java.sun.com/j2se/1.5/pdf/generic
s-tutorial.pdf
53
Thank You
The End.
54