Exception Programming Assignment 4 Points Each 1- Code an exception object. Show the try, catch block and catch the exception. Create a main routine to showcase the exception, and catch it. Keep it simple. 2- Code up an unhandled exception handler and show its usage. Create a main routine to showcase the exception, and catch it. Keep it simple. 3- Add valid exception coding to the stack program ... and correct errors. Original Source to Stack Program (inclues errors to correct) template class Stack { public: Stack(); ~Stack(); Stack(const Stack&); Stack& operator=(const Stack&); unsigned count(); void push(T); T pop(); private: unsigned nelems; int top; T* v; }; template Stack::Stack() { top = -1; v = new T[nelems=10]; if ( v == 0 ) throw "out of memory"; } template Stack::Stack(const Stack& s) { v = new T[nelems = s.nelems]; if ( v == 0 ) throw "out of memory"; if ( s.top > -1 ){ for (top = 0; top <= s.top; top++) v[top] = s.v[top]; top--; } } template Stack::~Stack() { delete [ ] v; } template void Stack::push(T element) { top++; if ( top == nelems-1 ) { T* new_buffer = new T[nelems+=10]; if ( new_buffer == 0 ) throw "out of memory"; for (int i = 0; i < top; i++) new_buffer[i] = v[i]; delete [ ] v; v = new_buffer; } v[top] = element; } template T Stack::pop() { if ( top < 0 ) throw "pop on empty stack"; return v[top--]; } template unsigned Stack::count() { return top+1; } template Stack& Stack::operator=(const Stack& s) { delete [ ] v; v = new T[nelems=s.nelems]; if ( v == 0 ) throw "out of memory"; if ( s.top > -1 ){ for (top = 0; top <= s.top; top++) v[top] = s.v[top]; top--; } return *this; } int main () { // use the copy constructor to make a copy of an empty stack Stack y; Stack x = y; assert( y.count() == 0 ); printf( "%u\n", x.count() ); // use the assignment method Stack a, b; a.push(0); a = b; printf( "%u\n", a.count() ); // correct for memory exceptions // MAXIM: Only change the original object // when you know it is safe to do so. // Guarantee the original state of // the object upon an error! // Find all places where this maxim is // is broken and correct the code. } NOTE: There examples are given to help you see some application exceptions of working code. Examples of Exceptions // Input Example of Exception // Read in stream of numbers (float and int) and operators into a single queue and evaluate. // Limitation: Doesn't handle negative input. // uses dynamic cast, so must have RTTI turned on. #include "Token.h" int main() { const int MAXCHAR = 255; char charbuf[MAXCHAR]; const char * EQUATION_PROMPT = "Enter an equation of integers, floats and operators in any order:\n"; memset(charbuf,'\0',MAXCHAR); bool quit=false; bool float_flag=false; char yesno; while ( quit==false ) { cout << EQUATION_PROMPT; try { //declare inside try block for easy clean-up Token * temp, * left, * right; Operator * temp_o; My_Queue main_q, num_q; My_Queue op_q; while (cin.peek() != '\n') { int count = 0; cin >> charbuf; //build operator objects if (charbuf[0] == '+') temp = new Plus(charbuf[0]); else if (charbuf[0] == '-') temp = new Minus(charbuf[0]); else if (charbuf[0] == '*') temp = new Mult(charbuf[0]); else if (charbuf[0] == '/') temp = new Div(charbuf[0]); // find integers and floats else if (isdigit(charbuf[0]) || charbuf[0]=='.') { while (charbuf[i] != '\0') { if (charbuf[i] == '.') float_flag = true; else if ( !isdigit(charbuf[i]) ) //Check all data before building object throw("Bad Data Input\n"); count++; } //build float object if (float_flag == true) temp = new Fop(atof(charbuf)); //build int object else temp = new Iop(atoi(charbuf)); } else throw("Bad Data Input\n"); //reset for next loop main_q.enqueue(temp); float_flag = false; memset(charbuf,'\0',MAXCHAR); //eat up trailing spaces while ((yesno=cin.peek())!='\n' && isspace(yesno)) { cin.get(); } } cin.ignore(MAXCHAR,'\n'); // discard the carriage return if(main_q.howmany() < 3) throw("Not enough items to process\n"); // while main_q is not empty dequeue items and assign to temp while ((temp = main_q.dequeue())!=NULL) { //temp_o is an Operator pointer. If temp can be cast to temp_o, then temp is of type Operator. // Unfortunately, dynamic cast does not work well with polymorphism. // if((temp_o = dynamic_cast(temp))) // cout << typeid(temp->get_val()).name(); if ( (temp->get_type()) == "Operator" ) { temp_o = dynamic_cast(temp); // enqueue on operator's queue op_q.enqueue(temp_o); // enqueue on number's queue } else num_q.enqueue(temp); } //must be in this order to work with lines below while ((temp_o = op_q.dequeue()) !=NULL && (left = num_q.dequeue()) !=NULL && (right = num_q.peek()) !=NULL ) { right = temp_o->operation(left, right); } if (right == NULL && temp_o != NULL) throw("Error: Too many operators."); // this need to be done, because the while loop exits // before executing these commands, // leaving the values out of synch if (temp_o == NULL) { left = num_q.dequeue(); if ((right = num_q.peek()) != NULL) throw("Error: Too many operands."); } cout << "Equation evaluates to: " ; left->show(); cout << endl; } catch(const char * message) { cout << message << endl; } cout << "Quit? (y/n): "; cin >> yesno; if (yesno == 'Y' || yesno == 'y') quit = true; cin.ignore(MAXCHAR,'\n'); } } // Token.h #include #include #include using namespace std; /********************************************************************/ // Token class and derived classes class Token { public: Token() = 0{}; virtual ~Token(){}; virtual double get_val() { return 0.0; }; virtual void set_val(const double & value) {}; virtual void set_val(const int & value){}; virtual char * get_type() { return "Token"; }; virtual Token * operation(Token * left, Token * right) { return left; }; virtual void show(){}; }; class Int : public Token { public: Int(const int & my_int = 0) { number = my_int; cout << "Got an int, " << my_int << endl; }; Int(const Int & my_int) { number = my_int.number; cout << "Copied Int obj " << my_int.number <set_val(left->get_val() + right->get_val()); return right; }; virtual ~Plus(){}; }; class Minus : public Operator { public: Minus(const char ch) : Operator(ch) { cout << "Got a '-'\n"; }; Token * operation(Token * left,Token * right) { right->set_val(left->get_val() - right->get_val()); return right; }; virtual ~Minus(){}; }; class Mult : public Operator { public: Mult(const char ch):Operator(ch) { cout << "Got a '*'\n"; }; Token * operation(Token * left, Token * right) { right->set_val(left->get_val() * right->get_val()); return right; }; virtual ~Mult(){}; }; class Div : public Operator { public: Div(const char ch):Operator(ch) { cout << "Got a '/'\n"; }; Token * operation(Token * left,Token * right) { right->set_val(left->get_val() / right->get_val()); return right; }; virtual ~Div(){}; }; /*******************************************************/ /* Snode class */ template class Snode { public: Snode(const Type * item = NULL) { this->item = const_cast(item); next = NULL; }; void set_next(const Snode * sn) { next = const_cast(sn); }; Snode * get_next() const { return next; }; Type * get_data() { return item; }; virtual ~Snode() {}; private: Snode * next; Type * item; }; /*********************************************************/ //Queue class and supporting methods //Based off Queue, // templatized and has peek(), howmany() and a different destructor template class My_Queue { public: My_Queue(); bool enqueue(const Type * item); Type * dequeue(); Type * peek(); int howmany() { return items; }; virtual ~My_Queue(); private: Snode * first; Snode * last; int items; }; template My_Queue::~My_Queue() { cout << "Destroying queue\n"; Type * temp; while (items != 0) { temp = dequeue(); // destroy the data popped off the queue // ? is that a good idea ? delete temp; } } template My_Queue::My_Queue() { first = last = NULL; items = 0; } template bool My_Queue::enqueue(const Type * itm) { Snode * add = new Snode(itm); if ( add == NULL ) return false; if ( items == 0 ) first = add; else last->set_next(add); last = add; items++; return true; } template Type * My_Queue::dequeue() { if (items == 0) return NULL; Type * itm = first->get_data(); //extract data pointer from first items--; Snode * temp = first; first = first->get_next(); //first = first->next // deletes the pointers but does not delete the data they point to. // This is what we want in this case. delete temp; if (items == 0) last = NULL; return itm; } template Type * My_Queue::peek() { if (items == 0) return NULL; //extract data pointer from first Type * itm = first->get_data(); return itm; } #endif // Date Example of exception objects #ifndef DAYEXCEPTION_H_ #define DAYEXCEPTION_H_ #include #include // DayException Base Class // // This exception is thrown when there is an error // during the determination of the name of the weekday class DayException { public: DayException(const char * msg); virtual const char* what(); virtual ~DayException(){} private: std::string str; }; // DayRangeException derived class // // This exception is thrown when there is an error during the // determination of the name of the weekday. // this particular class is used when the number for the weekday is out of range class DayRangeException : public DayException { public: DayRangeException(const char * msg, int day); virtual int getDay() const; virtual ~DayRangeException(){} private: int day; }; #endif //DAYEXCEPTION_H_ // DayException.cpp #include "DayException.h" // constructor DayException::DayException(const char * msg) { str = msg; } // return a string with the cause of the exception const char* DayException::what() { return str.c_str(); } // DayRangeException derived class // constructor DayRangeException::DayRangeException(const char * msg, int wrongday) : DayException(msg) { day = wrongday; } // return the number for the day int DayRangeException::getDay() const { return day; } #ifndef WEEKDAYFINDER_H #define WEEKDAYFINDER_H #include "DayException.h" class WeekDayFinder { public: enum {NUM_WEEK_DAYS = 7}; static std::string dayNames[NUM_WEEK_DAYS]; WeekDayFinder() {} // default constructor // return the full name of the day of the week // given the first three characters of // the day name ("sun", "mon", "tue", "wed", "thu", "fri", "sat") std::string getFullDayName(std::string threeChars) throw (DayException); // return the full name of the day of the week // given the number of the day (1 to 7) std::string getFullDayName(int day) throw (DayRangeException); virtual ~WeekDayFinder() {} }; #endif //WEEKDAYFINDER_H // WeekDayFinder.cpp #include "WeekDayFinder.h" #include // initializing static class variables std::string WeekDayFinder::dayNames[WeekDayFinder::NUM_WEEK_DAYS] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; // return the full name of the day of the week // given the first three characters of // the day name ("sun", "mon", "tue", "wed", "thu", "fri", "sat") // This method throws DayException // std::string WeekDayFinder::getFullDayName(std::string threeChars) throw (DayException) { std::string str = threeChars; if ( strcmpi(str.c_str(), "sun") == 0 ) return dayNames[0]; else if ( strcmpi(str.c_str(), "mon") == 0 ) return dayNames[1]; else if ( strcmpi(str.c_str(), "tue") == 0 ) return dayNames[2]; else if ( strcmpi(str.c_str(), "wed") == 0 ) return dayNames[3]; else if ( strcmpi(str.c_str(), "thu") == 0 ) return dayNames[4]; else if ( strcmpi(str.c_str(), "fri") == 0 ) return dayNames[5]; else if ( strcmpi(str.c_str(), "sat") == 0 ) return dayNames[6]; // no match found, throw exception std::ostringstream result; result << "'" << threeChars << "' are not valid first three characters of a weekday name"; throw DayException(result.str().c_str()); return str; } // return the full name of the day of the week // given the number of the day (1 to 7) // This method throws DayRangeException // std::string WeekDayFinder::getFullDayName(int day) throw (DayRangeException) { // no match found, throw exception if ( (day < 1) || (day > NUM_WEEK_DAYS) ) { std::ostringstream result; result << day << " is out of range"; throw DayRangeException(result.str().c_str(), day); } return dayNames[day-1]; } // main.cpp, program to test DayException class // compile with dayexception.cpp, weekdayfinder.cpp, #include #include #include "WeekDayFinder.h" using namespace std; using std::endl; int main() { // intentionally putting "tu" instead of "tue" to create exception char * dayShortNames[7] = {"sun", "mon", "tu", "wed", "thu", "fri", "sat"}; WeekDayFinder finder; int day; // This section of code will test DayException when "tu" is passed to the getFullDayName method. // Find full name of day of week given the first three characters of the full day name cout << "This will test DayException" << endl << endl; for ( day = 0; day < 7; day++ ) { try { cout << dayShortNames[day] << " = " << finder.getFullDayName(dayShortNames[day]).c_str() << endl; } catch(DayException& ex) { cout << dayShortNames[day] << " = " << ex.what() << endl; } } // This section of code will test DayRangeException // when "8 is passed to the getFullDayName method // Find full name of day of week given the number // corresponding to the day (1 to 7) cout << endl << endl; cout << "This will test DayRangeException" << endl << endl; for ( day = 1; day <= 8; day++ ) { try { cout << day << " = " << finder.getFullDayName(day).c_str() << endl; } catch(DayRangeException& ex) { cout << day << " = " << ex.what() << " (Wrong Number = " << ex.getDay() <<")" << endl; } catch(DayException& ex) { cout << day << " = " << ex.what() << endl; } } return 0; }