490 likes | 676 Views
Exceptions. Exceptions allow error handling to be seperated from normal code They allow local detection but global handling of errors in C++ an exception can be any type You can throw an int or a Person object. #include <iostream> struct Domain_Error { int number;
E N D
Exceptions • Exceptions allow error handling to be seperated from normal code • They allow local detection but global handling of errors • in C++ an exception can be any type • You can throw an int or a Person object
#include <iostream> struct Domain_Error { int number; Domain_Error(int d) : number(d) {}; }; double square_root(int n) { if (n < 0) throw Domain_Error(n); // .. calculate and return square root } int main() { int num; while (std::cin >> num) { try { std::cout << square_root(num); } catch (Domain_Error e) { std::cerr << e.number << " is not valid\n"; } } }
Multiple Catches • It is possible to list a number of catch statements • The first one that matches is used • ... can be used to catch anything try { // .. something } catch (SomeType st) { // ... something } catch (SomeOtherType st) { // ... something else again } catch (...) { // ... catch anything else }
Rethrowing Exceptions • A catch block can rethrow the exception • The rest of the list of catches are ignored
#include <iostream> int main() { try { try { throw 10; } catch (int i) { std::cout << "catch " << i << endl; std::cout << "throw 20.0\n"; throw 20.0; } catch (...) { std::cout << "won't get called\n"; } } catch (int i) { std::cout << "An int : " << i << endl; } catch (double d) { std::cout << "A double : " << d << endl; throw; //rethrow the exception } catch (double d2) { std::cout << "This won't get called\n"; } }
Generic Code • We have a List class for integers • What if we want a List of strings? • Most of the code would be the same • We can reuse the code by using a template • Templates are very powerful and also complicated
Templates • Templates can apply to structs, classes and functions • They allow us to make a paramatise the types used
template <typename T> struct Node { T value; Node<T> *next; Node() : value(0), next(0) {}; Node(Node<T> &o) : value(o.value), next(o.next) {}; Node(Node<T> *o) : value(o->value), next(o->next) {}; Node(T v) : value(v), next(0) {}; };
template <typename T> class List { public : List(); List(const List<T>&); List& operator=(const List<T>&); ~List(); int length() const; void insert_at_front(T value); bool insert_after_current(T value); void delete_first(); void delete_after_current(); void set_current_to_front(); void advance_current(); T get_current_value() const; bool check_current_in_list() const;
private : Node<T> *head; Node<T> *current; friend ostream& operator<<<T>(ostream& os, const List<T> &list); };
insert_after_current template <typename T> bool List<T>::insert_after_current( T value) { if (current == 0) return false; Node<T> *node = new Node<T>(value); node->next = current->next; current->next = node; return true; }
What about Exceptions? • Exceptions would improve our List template • Much better than returning false or returning 0 • Some simple exception classes are needed first • class List_Exception {}; • class List_Current_Null : public List_Exception {}; • class List_Empty : public List_Exception {}; • Why so we create a heirarchy of classes?
insert_after_current template <typename T> bool List<T>::insert_after_current( T value) { if (current == 0) throw List_Current_Null(); Node<T> *node = new Node<T>(value); node->next = current->next; current->next = node; return true; }
We can nest classes, this allows us to reduce name pollution • We can move Node inside the class to be List::Node • We can also move the exception classes inside the class
template <typename T> class List { private: struct Node { T value; Node *next; Node() : value(0), next(0) {}; Node(Node &o) : value(o.value), next(o.next) {}; Node(Node *o) : value(o->value), next(o->next) {}; Node(T v) : value(v), next(0) {}; }; public : List(); List(const List<T>&); List& operator=(const List<T>&); ~List();
int length() const; void insert_at_front(int value); void insert_after_current(int value); void delete_first(); void delete_after_current(); void set_current_to_front(); void advance_current(); int get_current_value() const; bool check_current_in_list() const; private : Node *head; Node *current; class Exception {}; class Current_Null : public Exception {}; class Empty : public Exception {}; class NoSuchElement : public Exception {}; friend ostream& operator<<<T>(ostream& os, const List<T> &list); };
Namespace Pollution • Reducing namespace pollution is a good thing • The original version used the following names: • List • Node • List_Exception • List_Current_Null • List_Empty • List_NoSuchElement • The final version only uses • List
Namespaces • Along with classes C++ also provides namespaces • Namespaces provide a way to make logical grouping • The standard C++ libraries are placed into the std namespace • Namespaces allow the same class names to be reused
#include <iostream> namespace name_a { const char* function() {return "name_a";} } namespace name_b { const char* function(); } const char* name_b::function() { return "name_b"; } const char* function() {return ”global";} int main() { std::cout << name_a::function() << endl; std::cout << name_b::function() << endl; std::cout << function() << endl; }
Using Namespaces • It is possible to 'pull in' names from other namespaces #include <iostream> namespace A { int fred = 10; } int fred = 20; int main() { std::cout << fred << endl; using A::fred; std::cout << fred << endl; }
Unnames Namespaces • Namespaces can also be used to hide identifiers namespace { //note no name int number; void function() { /* ... */ } } //above can only be seen in this file • It is like namespace ??? {} using namespace ???;
Namespace Aliases • Namespaces are designed to reduce name clashes • As such you shouldn't use very short names • Too long names make code hard to read though • Aliases give the best of both #include <iostream> namespace A_Name_Which_Is_Much_Too_Long { int fred = 42; } namespace shorter = A_Name_Which_Is_Much_Too_Long; int main() { std::cout << shorter::fred << endl; }
Development Tools • We will be looking at a number of useful tools • Tools for compiling code • Tools for searching through code • Tools for keeping track of different versions of code • Tools for debugging • Tools for testing
Compiling Code • The most important tools is of course the compiler • By now you should know how it works well enough • The compiler is not exactly fast C++ supports seperate compilation and linking • This means you only have to recompile the bit you changed
make • make only compiles the files that have changed • All you need to do is create a Makefile • make already knows about C++ so it is easy • make is very fussy about white space
SRCS = String.cc Test.cc OBJS = String.o Test.o CXXFLAGS = -g -Wall stest:$(OBJS) $(CXX) $(CXXFLAGS) -o stest $(OBJS) clean: rm -f $(OBJS) depend: makedepend -Y $(SRCS)
makedepend • makedepend is a very useful program • It find the files a source file #includes • This causes make to recompile code when the headers change • It doesn’t always like C++ (it was designed for C)
Searching In Code • As code grows it becomes harder to keep track off • It is useful to: • Find where classes are used • Locate definitions • Find where an error message is output
grep • grep is a tool for searching files • It is simple to use and very useful • At its simplest prints all the lines that match a pattern • The very useful -n option also prints the line numbers
Revision Control • Revision control systems keep track of changes to code Not using them lead to: • Accidentally deleting all your code • Breaking the code and not being able to fix it again • Often also support group work • Ability to merge multiple edits back together • Some form of access control
PRCS • PRCS is an easy to set up and use system • It supports simple group work • It will make your like much easier
Debugging • Debugging code can be frustrating and time consuming • Debuggers are a big help • They can be hard to understand at first
gdb • gdb is the GNU debugger • It has a command line interface • It is reasonably complicated to use • A little knowledge can provide a lot of benefit • Learning to use the debugger will help greatly in finding bugs in your code
ddd and insight • ddd is a GUI wrapper around gdb (and other programs) • insight is a GUI version of gdb • They are a little easier to learn • They are also less flexible
Testing • Testing is a difficult task • Component testing often requires scaffolding • We will only consider input-ouput tests • Only testing whole programs • Scaffolding can turn a component into a program
Shell Redirection • Firstly we need to • Run a program with a certain input • Save the resulting output • ./program <test.in >test.out • Executes program • Gets the input from the file test.in • Stores the output in the file test.out
Comparing Files • Need to compare the output with the expected output • The cmp command will compare two files • The diff command will display the differences • diff provides ouput that might help find the error
Multiple Tests • Say we have five tests, how can we run then all? • The shell is a complete programming language • We can write a shell script to do it #!/bin/sh for test in test1 test2 test3 test4 test5 do ./program <$test.in >$test.out done
Shell Programming • The shell provides a high level of abstraction • The basic objects are files • The basic operations are performed on files • This makes it very effective when dealing with files
Which Shell? • There are a lot of different shells sh, bash, csh, tcsh, ksh, ash,… • As an interactive shell bash and ksh are popular • For writing a shell script though sh is preferred • Simply because it is available everywhere
The Bourne Shell • The original Unix shell • Not the most understandable language • perl and python have taken over a lot of sh's turf • The shell is still useful for file level manipulations • All the machine marking for this course is done in sh and perl
Variables • Shell variables are all strings • They can be exported to subshells • The shell simply substitutes values for variables foo="hello this is foo" echo $foo export foo
Exit Status • You may have noticed in C++ that we use int main() • That return value is called an exit status • A program returns 0 if it succeeds • A program returns some other number to indicate a problem • This means that in shell 0 is true, everything else is false • The variable $? holds last command's exit status
If • The shell’s if tests the result of a program • There is a command test that is used a lot for this • The shortcut if [ ... ] means if test ... if grep fred foo.txt >/dev/null then echo fred found in foo fi
Maths in sh • A command called expr does maths • It also does a lot of other stuff • Check the man page for details • Simplest usage is expr 2 + 3 • Which will output 5
While loop • An example is probably the best way to explain it count=1 while [ $count -le 10 ] do echo $count count=`expr $count + 1` done
sed • The stream editor • A sed command looks like: [<address>[,<adress>]] command • sed -e 10q will output the first 10 lines • sed -n -e 1,10p will output the first 10 lines • sed -n -e /<HTML>/,/\<\/HTML>/p will output the lines between HTML tags • sed -e s/foo/bar/g replace all foo's with bar's • See the man page for commands
awk • Extremely useful utility • Is yet another programming language • Can also be used for simple things • awk -F: '/comp 2000/ {print $1}’ /etc/passwd • Excels at extracting fields from a file
join and paste • join joins files together • It will create your final mark files in a few weeks • paste also joins files together • Paste is much simpler but not as useful • See the man pages for examples...