Example 1: Throwing C++ Standard Exceptions


#include <stdexcept>

using namespace std;

// logic_error and runtime_error are classes
// that are derived from the exception class

void f(int x) throw(runtime_error)
{

   if ( x < 0 ) {
      throw runtime_error("f requires argument >=0");
   }
   // normal processing here

}

Note 1: The throw clause in the function prototype is legal C++ and
serves as documentation that the function may throw the listed
exception(s). If a function can throw more than one exception, they can
all be listed separated by commas.

Note 2: C++ does not require that the throw clause in the
function header. However, if the throw clause is used, the
list should contain all the exception types that the function
may throw. 

Example: Catching C++ Standard Exceptions



// 2a:                            // 2b:
int main()                        int main()                       
{				  {                                
				                                   
   try {			     try {                         
   				                                   
       f(-4);			         f(-4);                    
   } catch( runtime_error e) {	     } catch( exception& e) {   
      cout << e.what() << endl;	        cout << e.what() << endl;  
      exit(0);			        exit(0);                   
   }				     }                             
                                                                   
   
Note 1: Example 2b on the right uses catch by reference
instead of catch by value used by Example 2a. Since
runtime_error is a derived class of exception, the catch block on the
right will also catch exceptions of specific type
runtime_error. 

Note 2: If f were to also throw an exception of
specific type logic_error, the catch block in Example 2b would
still catch it, but the catch block of Example 2a would NOT. A
logic_error is a kind of exception, but a
logic_error is NOT a kind of runtime_error. 

C++ Standard Exceptions



class exception
{
public:
  exception() throw();  // throws no exceptions
  exception(const exception&) throw();
  exception& operator=(const exception&) throw();
  virtual ~exception() throw();

  virtual const char* what() const throw();

};




exception
  logic_error
     length_error
     domain_error
     out_of_range
     invalid_argument
  run_time_error
     range_error
     overflow_error
     underflow_error

All of these classes descended from exception have constructors that
take a string. That string will be returned by the what()
method. 

Template Example

#include <iostream>

using namespace std;

template <class T, class S>
void printPair(const pair<T,S>& pr)
{
  cout << "(" << pr.first << "," << pr.second << ")" << endl;
}

int main()
{
  pair<int, int> p;
  p.first = 5;
  p.second = 10;

  pair<char, string> q('A', "hello");

  printPair(p);
  printPair(q);
  printPair(pair<string, double>("pi", 3.141592));

  return 0;
}

Output:
(5,10)
(A,hello)
(pi,3.14159)

VisualC++, Templates and Pragmas

When using templates with VisualC++, you may get errors like this:

c:\program files\microsoft visual studio\vc98\include\deque(208) :
warning C4786:
std::reverse_iterator<std::deque<std::pair<Position,Position::Iterator>,
std::allocator<std::pair<Position,Position::Iterator>  >
>::const_iterator,std::pair<Position,Position::Iterator>,
std::pair<Position,Position::Iterator>
const &,std::pair<Position,Position::Iterator> const *,int> :

identifier was truncated to '255' characters in the debug information 

c:\program files\microsoft visual studio\vc98\include\deque(207) : 

while compiling class-template member function 

__thiscall std::deque<std::pair<Position,Position::Iterator>,std::allocator<std::pair<Position,Position::Iterator> > >::~std::
deque<std::pair<Position,Position::Iterator>,std::allocator<std::pair<Position,Position::Iterator>
> >(void)

c:\program files\microsoft visual studio\vc98\include\deque(208) :
<b>warning C4786:</b>

'std::reverse_iterator<std::deque<std::pair<Position,Position::Iterator>,std::allocator<std::pair<Position,Position::Iterator>
> >::iterator,std::pair<Position,Positio 
n::Iterator>,std::pair<Position,Position::Iterator>
&,std::pair<Position,Position::Iterator> *,int>' : 

identifier was truncated to '255' characters in the debug information


c:\program files\microsoft visual studio\vc98\include\deque(207) :
while compiling class-template member function 

'__thiscall std::deque<std::pair<Position,Position::Iterator>,std::allocator<std::pair<Position,Position::Iterator> > >::~std::
deque<std::pair<Position,Position::Iterator>,std::allocator<std::pair<Position,Position::Iterator>
> >(void)'

Maze.cpp

c:\program files\microsoft visual studio\vc98\include\stack(22) :
warning C4786:

'std::stack<std::pair<Position,Position::Iterator>,
std::deque<std::pair<Position,Position::Iterator>, 
std::allocator<std::pair<Position,
Position::Iterator> > > >::stack< std::pair<Position,
Position::Iterator>,std::deque<std::pair<Position,
Position::Iterator>,std::allocator<std::pair<Position,Position::Iterator>
> > >' : 

identifier was truncated to '255' characters in the debug
information 
MazeApp.cpp
Linking...

knight.exe - 0 error(s), 3 warning(s)


Stack Example

#include <stack>

using namespace std;

int main()
{
  stack< pair<int,int> > s;

  for(int i = 1; i <= 5; i++) {
    s.push( pair<int,int>(i, i * i) );
  }

  while( !s.empty() ) {
    cout << s.top().first << " squared is " << s.top().second << endl;
    s.pop();
  }
  return 0;
}

Output

5 squared is 25
4 squared is 16
3 squared is 9
2 squared is 4
1 squared is 1


Balanced Parentheses

This is the application is mentioned on pages 165-166 of the text as an application using a stack. The C++ STL stack class may be used for this problem.

  1. Write a class ParseBP and a main method that will parse an input file to check that it has properly balanced curly braces, square brackets, and parentheses - '{', '}', '[', ']', '(', and ')'.

    Your program should prompt for the input file name.

    If the file test1.txt contains:

    This file has balanced curly braces, square brackets
    and parentheses: {}, [] and (). Note that these
    characters are not required to balance inside 
    quoted strings. E.g., "){[[]".
    

    a sample run might be:

    ParseBP 
    Input file name: test1.txt
    
     01. This file has balanced curly braces, square brackets
     02. and parentheses: {}, [] and (). Note that these
     03. characters are not required to balance inside 
     04. quoted strings. E.g., "){[[]".
    
    Input is balanced.
    
    

    In addition to reporting a valid input file like the one above, your program should check for and report the following errors:

    1. A closing character - '}', ']', or ')' - with the preceding text already balanced.
      File test2.txt:
      [This ({line} is balanced) ],
      but this character ] is not.
      --------------------------------
      ParseBP 
      Input filename: test2.txt
      
       01. [This ({line} is balanced) ],
       02. but this character ]
      Error: Line 2. Closing character ']', with no matching opening character.
      
      
    2. A closing character which does not match the most recent unmatched opening character.
      File test3.txt:
      (This left parenthesis
      is not "closed"! [Balanced
      square brackets], then a } 
      at the end.
      -----------------------------
      ParseBP 
      Input filename: test3.txt
      
       01. (This left parenthesis
       02. is not "closed"! [Balanced
       03. square brackets], then a }
      Error: Line 3. Symbol '}' is the wrong closing symbol  for char = '(', line 1
      
    3. End of file reached but with an opening character still not matched.
      File test4.txt:
      Opening square
      bracket, [, is not matched
      when end of file reached.
      --------------------------
      ParseBP 
      Input filename: test4.txt
      
       01. Opening square
       02. bracket, [, is not matched
       03. when end of file reached.
      
      Error: At end of file, no closing symbol found for char = '[', line 2
      

    Implementation Hints

    - You will probably want to use a istream method. However, instead of using the cin.getline(buf, N), it will probably be better to use the get() method. This method returns the int -1 at end of file. Otherwise, the return value can be cast to a char.

    - Use a stack to keep track of the opening symbols - '{', '[', '('. You might want to push an object that has both the opening symbol and the line number where it occurred.

    - When a character is read, it can also be printed to the output.

    - When the characer '\n' is read, a line count can be incremented.

Backtracking and Stacks



7 13
 0  0  0 -1  0  0 -1 -1 -1  0  0  0  0
 0 -1  0  0  0 -1  0  0  0  0  0 -1  0
 0 -1 -1 -1  0 -1  0 -1  0 -1  0 -1  0
 0 -1 -1 -1  0  0  0 -1  0 -1  0  0  0
 0  0  0  0  0 -1 -1 -1 -1  0 -1 -1 -1
-1 -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1 -1
-1 -1 -1 -1  0  0  0  0  0  0  0  0  0

Examle Output

  1  2  3  X  0  0  X  X  X  0  0  0  0
  0  X  4  5  6  X  0  0  0  0  0  X  0
  0  X  X  X  7  X  0  X  0  X  0  X  0
  0  X  X  X  8  0  0  X  0  X  0  0  0
  0  0  0  0  9  X  X  X  X  0  X  X  X
  X  X  X  X 10  X  X  X  X  X  X  X  X
  X  X  X  X 11 12 13 14 15 16 17 18 19

Maze Application

The book discusses backtracking and gives some framework code in section 4.5 where recursion is being discussed.

However, the main part of the algorithm can be written without recursion by using a stack instead.

What is BackTracking

Backtracking is appropriate when a solution to a problem is made up of
incremental steps where at each step there may be several choices to
be made. Backtracking is a systematic way of exploring all
possibilities until a solution is found or else having exhausted all
the possibilities, discovering that no solution exists.

Maze Class

#ifndef MAZE_H
#define MAZE_H

#ifdef WIN32
#pragma warning( disable: 4786)
#endif

#include <iostream>
#include <iomanip>
#include <sstream>
#include <cstdlib>
#include <string>
#include <stdexcept>

using namespace std;

#include "Position.h"

using namespace std;

class Maze
{
  int **grid;
  int rows;
  int cols;
  int move;
 public:
  static const int TRIED;
  static const int FREE;
  static const int WALL;

  Maze()  : rows(0), cols(0), move(0) {}
  Position generateInitialState();
  void readData(istream& is);
  void setData(int m[], int r, int c) {
    grid = new int *[r];
    for(int i = 0; i < r; i++) {
      grid[i] = new int[c];
      for(int j = 0; j < c; j++) {
	grid[i][j] = m[c*i + j];
      }
    }
    rows = r;
    cols = c;
  }
  bool valid(const Position& pos);
  void record(const Position& pos);
  bool done(const Position& pos);
  void undo(const Position& pos);
  string toString() const;
};

ostream& operator<<(ostream& os, const Maze& a);

#endif

Position Class


#ifndef POSITION_H
#define POSITION_H

#ifdef WIN32
#pragma warning( disable: 4786)
#endif

#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdlib>
#include <string>
#include <stdexcept>

using namespace std;

class Position
{
  int row;
  int col;
public:
  class Iterator
  {
    friend class Position;
    const Position *basepos;
    int dir;
  public:
    Iterator() : basepos(0), dir(0) {}
    Iterator operator++(int); //Exercise
    Position operator*() const;
    bool operator!=(const Iterator& p) const;
  private:
    Iterator(const Position* p, int d = 0) : basepos(p), dir(d)  {}
  };

  Position() : row(-1), col(-1) {}
  Position(int r, int c) : row(r), col(c) {}
  Iterator begin() const;
  Iterator end() const;
  void setPosition(int r, int c);
  int getRow() const;
  int getCol() const;
};

#endif

BackTrack Class

#ifndef BACKTRACK_H
#define BACKTRACK_H

#ifdef WIN32
#pragma warning( disable: 4786)
#endif

#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdlib>
#include <string>
#include <stdexcept>
#include <stack>
#include "Maze.h"
#include "Position.h"

using namespace std;

class BackTrack
{
public: 
  BackTrack(const Maze& a) : app(a) {}
  bool tryToSolve(Position pos);
protected:
  Maze app;
  
};
#endif

BackTrack implementation

#include "BackTrack.h"
#ifdef WIN32
#pragma warning( disable: 4786)
#endif

using namespace std;


bool BackTrack::tryToSolve(Position pos)
{
  bool success = false;

  stack< pair<Position, Position::Iterator> > s;
  Position::Iterator p;

  if (!app.valid(pos)) {
    return false;
  } else {
    app.record(pos);
  }

  s.push(pair<Position, Position::Iterator>(pos, pos.begin()));

  while( !success && !s.empty() ) {
    pos = s.top().first;
    p = s.top().second;

    if ( app.done(pos) ) {
      success = true;
    } else if ( p != pos.end() ) {
      if ( app.valid(*p) ) {
	pos = *p;
	app.record(pos);
	s.push(pair<Position, Position::Iterator>(pos, pos.begin()));
      } else {
	s.top().second++;
      }
    } else {
      app.undo(pos);
      s.pop();
    }
  }
  return success;

}

Position implementation

#include "Position.h"
#ifdef WIN32
#pragma warning( disable: 4786)
#endif

Position::Iterator Position::Iterator::operator++(int) //Exercise
{
  Position::Iterator it(*this);
  if ( dir == 4 ) {
    throw invalid_argument("Operator++ is invalid; Iterator is at end");
  }
  dir++;
  return it;
}
Position Position::Iterator::operator*() const
{
  int r, c;
  r = basepos->getRow();
  c = basepos->getCol();
  switch(dir) {
  case 0: // up
    r--;
    break;
  case 1: // right
    c++;
    break;
  case 2: // down
    r++;
    break;
  case 3: // left
    c--;
    break;
  default:
    throw out_of_range("Direction has out of range value");
  }
  return Position(r,c);
}

bool Position::Iterator::operator!=(const Position::Iterator& p) const
{
  return basepos != p.basepos || dir != p.dir;
}
Position::Iterator Position::begin() const
{
  return Position::Iterator(this);
}

Position::Iterator Position::end() const
{
  return Position::Iterator(this, 4);
}

void Position::setPosition(int r, int c)
{
  row = r;
  col = c;
}

int Position::getRow() const
{
  return row;
}

int Position::getCol() const
{
  return col;
}

Maze implementation

#include "Maze.h"
#ifdef WIN32
#pragma warning( disable: 4786)
#endif

const int Maze::TRIED = -2;
const int Maze::FREE = 0;
const int Maze::WALL = -1;

Position Maze::generateInitialState()
{
  return Position(0,0);
}

void Maze::readData(istream& is)
{
  int r, c;
  is >> r >> c;
  grid = new int *[r];
  for(int i = 0; i < r; i++) {
    grid[i] = new int[c];
    for(int j = 0; j < c; j++) {
      is >> grid[i][j];
    }
  }
  rows = r;
  cols = c;
  
}

bool Maze::valid(const Position& pos)
{
  int r = pos.getRow();
  int c = pos.getCol();
  return ( 0 <= r && r < rows && 0 <= c && c < cols &&
       grid[r][c] == Maze::FREE  );
}

void Maze::record(const Position& pos)
{
  int r = pos.getRow();
  int c = pos.getCol();
  grid[r][c] = ++move;
}

bool Maze::done(const Position& pos)
{
  int r = pos.getRow();
  int c = pos.getCol();
  return (r == rows - 1) && (c == cols - 1);
}

void Maze::undo(const Position& pos)
{
  move--;
  grid[pos.getRow()][pos.getCol()] = Maze::TRIED;
}

string Maze::toString() const
{
  stringstream s;
  for (int i = 0; i < rows; i++) {
    for(int j = 0; j < cols; j++) {
      s.width(3);
      if ( grid[i][j] == Maze::TRIED ) {
	s << 0;
      } else if ( grid[i][j] == Maze::WALL ) {
	s << 'X';
      } else {
	s << grid[i][j];
      }
    }
    s << endl;
  }
  return s.str();
}

ostream& operator<<(ostream& os, const Maze& a)
{
  os << a.toString();
  return os;
}

Main function


#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <cstdlib>
#include <string>
#include <stdexcept>
#include "Maze.h"
#include "BackTrack.h"

using namespace std;
#ifdef WIN32
#pragma warning( disable: 4786)
#endif

int main()
{
  
  int g[4*6] = 
  {
    0, 0, 0, -1, 0, 0 ,
    0, -1, -1, 0, 0, 0 ,
    0, -1, 0, 0, -1, 0 ,
    0, 0, 0, -1, -1, 0 
  };
  Maze m;
  m.setData(g, 4, 6);
  BackTrack bt(m);
  
  Position pos = m.generateInitialState();
  if ( bt.tryToSolve(pos) ) {
    cout << m << endl;
  } else {
    cout << "No solution" << endl;
  }

  ifstream ifs;
  ifs.open("m1.txt");
  if (!ifs) {
	 cout << "Can't open m1.txt" << endl;
  }

  m.readData(ifs);
  BackTrack bt2(m);

  pos = m.generateInitialState();
  if ( bt2.tryToSolve(pos) ) {
    cout << m << endl;
  } else {
    cout << "No solution" << endl;
  }

  return 0;
}