JMU
Templates in C++
An Introduction for Java Programmers


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Introduction
Function Templates
Class Templates
Organization of Class Templates/Function Templates
A Complete Example

Writing an Exception Class - The Specification

cppexamples/vector/IndexOutOfBoundsException.h
        #ifndef edu_jmu_cs_IndexOutOfBoundsException_h
#define edu_jmu_cs_IndexOutOfBoundsException_h

/**
 * An IndexOutOfBoundsException can be thrown by a function that is
 * asked to access an element of a Vector that is "out of bounds".
 *
 * @author  Prof. David Bernstein, James Madison University
 *
 */
class IndexOutOfBoundsException {
 public:
  // The index causing the problem
  int index;

  /**
   * Default Constructor
   */
  IndexOutOfBoundsException();

  /**
   * Explicit Value Constructor
   *
   * @param i   The index that cause the problem
   */
  explicit IndexOutOfBoundsException(int i);
};

#endif
        
A Complete Example (cont.)

Writing an Exception Class - The Implementation

cppexamples/vector/IndexOutOfBoundsException.cpp
        #include "IndexOutOfBoundsException.h"

IndexOutOfBoundsException::IndexOutOfBoundsException() {
  index = -1;
}

IndexOutOfBoundsException::IndexOutOfBoundsException(int i) {
  index = i;
}
        
A Complete Example (cont.)

A FixedSizeCollection Template

cppexamples/fixedsizevector/FixedSizeCollection.hpp
        #ifndef edu_jmu_cs_FixedSizeCollection_hpp
#define edu_jmu_cs_FixedSizeCollection_hpp

#define BLOCK_SIZE 100

#include "IndexOutOfBoundsException.h"
#include <stdlib.h>

/**
 * A collection that can't be re-sized
 *
 * Note: This is not a useful class.  It is, however,
 * an illustration of how to use templates.
 *
 * @author  Prof. David Bernstein, James Madison University
 */
template<class AnyObject>
class FixedSizeCollection {
 public:
  /**
   * Default Constructor
   */
  FixedSizeCollection(void);

  /**
   * Destructor
   */
  ~FixedSizeCollection(void);

  /**
   * Append an element to the end of the FixedSizeCollection
   *
   * @param e   The element to append
   */
  void append(const AnyObject e);

  /**
   * Access an element in the FixedSizeCollection
   *
   * @param  index   The index of the element of interest
   * @return         The element at the given index
   * @throws         IndexOutOfBoundsException if the index is out of bounds
   */
  AnyObject elementAt(const int index);

  /**
   * Get the number of used/actual elements
   */
  int length(void);

 private:
  AnyObject data[BLOCK_SIZE];
  int usedLength;

};

template<class AnyObject>
FixedSizeCollection<AnyObject>::FixedSizeCollection(void) {
  for (int i = 0; i < BLOCK_SIZE; i++) {
    data[i] = NULL;
  }
  usedLength = 0;
}

template<class AnyObject>
FixedSizeCollection<AnyObject>::~FixedSizeCollection(void) {
  // Nothing to do since no memory was allocated
}

template<class AnyObject>
void FixedSizeCollection<AnyObject>::append(const AnyObject e) {
  if (usedLength >= BLOCK_SIZE)
    throw IndexOutOfBoundsException(usedLength);

  data[usedLength] = e;
  usedLength++;
}

template<class AnyObject>
AnyObject FixedSizeCollection<AnyObject>::elementAt(const int index) {
  // Check the bounds of the index
  if ((index < 0) || (index >= usedLength))
    throw IndexOutOfBoundsException(index);

  return data[index];
}

template<class AnyObject>
int FixedSizeCollection<AnyObject>::length(void) {
  return usedLength;
}

#endif
        
A Complete Example (cont.)

Use

cppexamples/fixedsizevector/Driver.cpp
        /**
 * An example that uses a FixedSizeCollection.
 *
 * @author  Prof. David Bernstein, James Madison University
 */
#include "FixedSizeCollection.hpp"
#include <iostream>
#include "IndexOutOfBoundsException.h"
using namespace std;

/**
 * The entry point of the example
 */
int main(void) {
  int i, n;
  FixedSizeCollection<int> v;

  try {
    n = 3;
    for (i = 0; i < n; i++) {
      v.append(i);
    }

    for (i = 0; i < (v.length()); i++) {
      n = v.elementAt(i);
      cout << "Element " << i << " is: " << n << endl;
    }

    i = 5;
    n = v.elementAt(i);
    cout << "Element " << i << " is: " << n << endl;
  } catch (IndexOutOfBoundsException &be) {
    cout << "Index out of bounds: " << be.index << endl;
  }
}

        
Friend Functions in Templates
Friend Functions in Templates (cont.)
Friend Functions in Templates (cont.)

An Example

cppexamples/templates/Table.hpp
        #ifndef edu_jmu_cs_Table_hpp
#define edu_jmu_cs_Table_hpp

#include <stdexcept>
using namespace std;

// Prototype of the Table class 
// (so that it can be used in the friend prototypes)
template<int R, int C> class Table;

// Prototypes of friend functions 
template<int RT, int CTB, int RB>
Table<RT + RB, CTB> operator+(const Table<RT, CTB>& a, const Table<RB, CTB>& b);

/**
 * An encapsulation of a Table.
 *
 * @tparam ROWS The number of rows
 * @tparam COLUMNS  The number of columns
 */
template<int ROWS, int COLUMNS>
class Table {
 protected:
  double values[ROWS][COLUMNS];

  /**
   * Set the value of all elements in this Table to the given value.
   *
   * @param value  The value to assign to every element
   */
  void setValues(double value);

  /**
   * Set the value of all elements in this Table to the value
   * of the corresponding elements in another Table.
   *
   * @param other   The other Table
   */
  void setValues(const Table& other);

 public:
  /**
   * Default Constructor.
   */
  Table<ROWS, COLUMNS>();

  /**
   * A copy constructor.
   *
   * @param original  The Table to copy
   */
  Table<ROWS, COLUMNS>(const Table<ROWS, COLUMNS>& original);

  /**
   * Get a particular element of this Table.
   *
   * @param r   The row index
   * @param c   The column index
   * @throws    out_of_range if r or c are out of bounds
   * @return    The value of the element
   */
  double get(int r, int c) const;

  /**
   * Get a particular element of this Table if it contains
   * a single row.
   *
   * @param i   The index
   * @throws    out_of_range if i is out of bounds
   * @throws    length_error if this is neither a single row nor single column
   * @return    The value of the element
   */
  double get(int i) const;

  /**
   * Assign another Table to this Table.
   *
   * Note: This method is not void so that one can write x = y = z
   * (which first assigns z to y and then assigns the result of that
   * assignment to x). It returns the result by reference because there
   * is no concern that this will not refer to something.
   *
   * @param other   The Table to copy
   * @return        The Table referred to by this
   */
  Table<ROWS, COLUMNS>& operator=(const Table<ROWS, COLUMNS>& other);

  /**
   * Concatenate a and b (i.e., create a Table in which the
   * rows of a are "above" the rows of b)
   *
   * Note: The friend function operator+ is one-to-one with the class.
   * That is, there is an operator+ that corresponds to Table<2,2>, an
   * operator+ that corresponds to Table<3,3>, etc...
   *
   * @tparam RT The number of rows in the top
   * @tparam CTB The number of columns in both the top and bottom
   * @tparam RB The number of rows in the bottom
   * @param a  The "top" Table
   * @param b  The "bottom" Table
   * @return   The concatenation of a and b
   */
  template<int RT, int CTB, int RB>
  friend Table<RT + RB, CTB> operator+(const Table<RT, CTB>& a,
                                       const Table<RB, CTB>& b);
};

template<int R, int C>
Table<R, C>::Table() {
  setValues(0.0);
}

template<int R, int C>
Table<R, C>::Table(const Table<R, C>& original) {
  setValues(original);
}

template<int RT, int CTB, int RB>
Table<RT + RB, CTB> operator+(const Table<RT, CTB>& a,
                              const Table<RB, CTB>& b) {
  Table<RT + RB, CTB> result;

  // First copy the rows of a
  for (int r = 0; r < RT; r++) {
    for (int c = 0; c < CTB; c++) {
      result.values[r][c] = a.values[r][c];
    }
  }

  // Now copy the rows of b
  for (int r = 0; r < RB; r++) {
    for (int c = 0; c < CTB; c++) {
      result.values[r + RT][c] = b.values[r][c];
    }
  }

  return result;
}

template<int R, int C>
double Table<R, C>::get(int r, int c) const {
  if ((r < 0) || (r >= R))
    throw(out_of_range("r"));
  if ((c < 0) || (c >= C))
    throw(out_of_range("c"));

  return this->values[r][c];
}

template<int R, int C>
double Table<R, C>::get(int i) const {
  if (R != 1)
    throw(length_error("shape"));

  if ((i < 0) || (i >= C))
    throw(out_of_range("i"));
  return this->values[0][i];
}

template<int R, int C>
Table<R, C>& Table<R, C>::operator=(const Table<R, C>& other) {
  setValues(other);

  // Note: The result is the object referred to by this (which is returned
  // by reference). So, we must reference this.
  return *this;
}

template<int R, int C>
void Table<R, C>::setValues(double value) {
  for (int r = 0; r < R; r++) {
    for (int c = 0; c < C; c++) {
      this->values[r][c] = value;
    }
  }
}

template<int R, int C>
void Table<R, C>::setValues(const Table<R, C>& other) {
  // Don't self-assign! (Note: this is a reference; other is an object)
  if (this != &other) {
    for (int r = 0; r < R; r++) {
      for (int c = 0; c < C; c++) {
        this->values[r][c] = other.values[r][c];
      }
    }
  }
}

#endif
        
Specializing Templates
Specializing the Templates Themselves
Specializing the Templates Themselves (cont.)

An Example

cppexamples/templates/Row.hpp
        #ifndef edu_jmu_cs_Row_hpp
#define edu_jmu_cs_Row_hpp

#include "Table.hpp"

/**
 * A Row in a Table.
 *
 * @tparam C  The number of columns
 */
template<int C>
class Row : public Table<1, C> {
 public:
  /**
   * Default Constructor.
   *
   * @tparam C  The number of columns
   */
  Row<C>();

  /**
   * A copy constructor.
   *
   * @tparam The number of columns
   * @param original  The Row to copy
   */
  Row<C>(const Row<C>& original);

  /**
   * Assign a Table with one row to this Row.
   *
   * Note: This method is not void so that one can write x = y = z
   * (which first assigns z to y and then assigns the result of that
   * assignment to x). It returns the result by reference because there
   * is no concern that this will not refer to something.
   *
   * @tparam C      The number of columns
   * @param other   The Table to copy
   * @return        The Row referred to by this
   */
  Row<C>& operator=(const Table<1, C>& other);
};

template<int C>
Row<C>::Row() {
  this->setValues(0.0);
}

template<int C>
Row<C>::Row(const Row<C>& original) {
  this->setValues(original);
}

template<int C>
Row<C>& Row<C>::operator=(const Table<1, C>& other) {
  this->setValues(other);

  // Note: The result is the object referred to by this (which is returned
  // by reference). So, we must reference this.
  return *this;
}

#endif
        
Templates for Unit Testing
Templates for Unit Testing (cont.)

A Simple Home-Grown Harness/Framework

cppexamples/templates/UnitTest.hpp
        #ifndef edu_jmu_cs_UnitTest_hpp
#define edu_jmu_cs_UnitTest_hpp

#define  TOLERANCE 0.0001
#include <stdexcept>
#include <stdlib.h>
#include <string>

using namespace std;

/**
 * \file
 * A collection of functions that can be used in testing.
 */

// Prototypes
template<class T, class S>
void assertEquals(T expected, S actual, const string& message);

void assertEquals(double expected, double actual, const string& message);

void assertEquals(bool expected, bool actual, const string& message);

/**
 * Check to see if the actual value equals the expected value (using
 * the == operator).
 * 
 * Note: This function will work for any two classes that have an ==
 * operator.
 *
 * @tparam T the class of the expected value
 * @tparam S The class of the actual value
 * @param expected  The expected/correct value
 * @param actual    The actual/calculated value
 * @param message A message that describes the assetion
 * @throws          invalid_argument if actual does not equal expected
 */
template<class T, class S>
void assertEquals(T expected, S actual, const string& message) {
  // This logic is used in case there isn't a != operator
  if (expected == actual)
    return;
  throw(invalid_argument(message));
}

/**
 * Check to see if the expected double value equals the actual double
 * value within a defined TOLERANCE.
 *
 * @param expected  The expected/correct value
 * @param actual    The actual/calculated value
 * @param message   A message that describes the assetion
 * @throws          invalid_argument if expected does not equal actual
 */
void assertEquals(double expected, double actual, const string& message) {
  if (abs(expected - actual) > TOLERANCE)
    throw(invalid_argument(message));
}

/**
 * Check to see if the expected bool value equals the actual bool
 * value
 *
 * @param expected  The expected/correct value
 * @param actual    The actual/calculated value
 * @param message   A message that describes the assetion
 * @throws          invalid_argument if expected does not equal actual
 */
void assertEquals(bool expected, bool actual, const string& message) {
  if (expected != actual)
    throw(invalid_argument(message));
}

#endif