<<

• Introduction • Vs. Function Templates

• Function Templates • Overloading Function Templates • Class Templates • Class And Non-Type Parameters

• Template And Inheritance •Template and Friends •Generic Functions •Applying Generic Functions •Generic Classes •Typename and Export Keyword •Power of Templates INTRODUCTION

++ templates • Allow very ‘general’ definitions for functions and classes • Type names are ‘parameters’ instead of actual types • Precise definition determined at run-time

C++ Function Templates

• Approaches for functions that implement identical tasks for different data types – Naïve Approach – Function Overloading – Function Template • Instantiating a Function Templates

6 Approach 1: Naïve Approach

• Create unique functions with unique names for each combination of data types – Difficult to keep track of multiple function names – lead to programming errors

7 Example void PrintInt( int n ) { cout << "***Debug" << endl; cout << "Value is " << n << endl; } void PrintChar( char ch ) { cout << "***Debug" << endl; cout << "Value is " << ch << endl; } void PrintFloat( float x ) To output the traced values, we { insert: … } PrintInt(sum); void PrintDouble( double ) { PrintChar(initial); … } PrintFloat(angle);

Approach 2: Function Overloading – Review • The use of the same name for different C++ functions, distinguished from each other by their parameter lists • Eliminates the need to come up with many different names for identical tasks. • Reduces the chance of unexpected results caused by using the wrong function name.

9 Example of Function Overloading void Print( int n ) { cout << "***Debug" << endl; cout << "Value is " << n << endl; } void Print( char ch ) { cout << "***Debug" << endl; cout << "Value is " << ch << endl; } To output the traced values, we void Print( float x ) insert: { Print(someInt); Print(someChar); Print(someFloat); }

Approach 3: Function Template

• A C++ language construct that allows the to generate multiple versions of a function by allowing parameterized data types.

FunctionTemplate

Template < TemplateParamList > FunctionDefinition

TemplateParamDeclaration: placeholder

class typeIdentifier typename variableIdentifier

11 Function Template Syntax

• Allow ‘swap values’ of any type variables: template void swapValues(T& var1, T& var2) { T temp; temp = var1; var1 = var2; var2 = temp; } • First line called ‘template prefix’ • Tells compiler what’s coming is ‘template’ • And that T is a type parameter Template Prefix

• Recall: template • In this usage, ‘class’ means ‘type’, or ‘classification’ • Can be confused with other ‘known’ use of word ‘class’! – C++ allows keyword ‘typename’ in place of keyword ‘class’ here – But most use ‘class’ anyway Template Prefix

• Again: template • T can be replaced by any type – Predefined or user-defined (like a C++ class type) • In function definition body: – T used like any other type • Note: can use other than ‘T’, but T is ‘traditional’ usage Calling a Function Template

• Consider following call: swapValues(int1, int2); • C++ compiler ‘generates’ function definition for two int parameters using template • Likewise for all other types • Needn’t do anything ‘special’ in call • Required definition automatically generated Instantiating a Function Template

• When the compiler instantiates a template, it substitutes the template argument for the template parameter throughout the function template.

TemplateFunction Call

Function < TemplateArgList > (FunctionArgList)

16 Example of a Function Template

Template parameter template (class, user defined void Print( SomeType val ) type, built-in types) { cout << "***Debug" << endl; cout << "Value is " << val << endl; }

Template To output the traced values, we insert: argument Print(sum); Print(initial); Print(angle);

Another Function Template

• Declaration/prototype: template void showStuff(int stuff1, T stuff2, T stuff3); • Definition: template void showStuff(int stuff1, T stuff2, T stuff3) { cout << stuff1 << endl << stuff2 << endl << stuff3 << endl; } showStuff Call

• Consider function call: showStuff(2, 3.3, 4.4); • Compiler generates function definition • Replaces T with double • Since second parameter is type double • Displays: 2 3.3 4.4 Function Templates vs. Overloading

• Could overload function for char’s: void swapValues(char& var1, char& var2) { char temp; temp = var1; var1 = var2; var2 = temp; } • But notice: code is nearly identical! • Only difference is type used in 3 places Multiple Type Parameters

• Can have: template • Not typical • Usually only need one ‘replaceable’ type • Cannot have ‘unused’ template parameters • Each must be ‘used’ in definition • Error otherwise! Another Template Example: passing two parameters

template class Stack {... T buf[size]; non-type parameter };

Stack mystack; Summary of Three Approaches

Naïve Approach Function Overloading Different Function Definitions Different Function Definitions Different Function Names Same Function Name

Template Functions One Function Definition (a function template) Compiler Generates Individual Functions Class Templates

• Can also ‘generalize’ classes template • Can also apply to class definition • All instances of ‘T’ in class definition replaced by type parameter • Just like for function templates! • Once template defined, can declare objects of the class Class Templates Syntax

• For class template declaration: template class_declaration; • For the implementation of the methods outside the class, the syntax is: template return_type className::methodName(parameter-list){ ……} • For the implementation of the constructors outside the class, the syntax is: template className:: className(parameter-list){……} Class Template Definition

• template class Pair { public: Pair() { }; Pair(T firstVal, T secondVal); void setFirst(T newVal); void setSecond(T newVal); T getFirst() const; T getSecond() const; private: T first; T second; }; Class Template Definition

• template Pair::Pair(T firstVal, T secondVal) { first = firstVal; second = secondVal; }

Class Template Definition

template void Pair::setFirst(T newVal) { first = newVal; } template void Pair::setSecond(T newVal) { second = newVal; } Class Template Definition

template void Pair::getFirst() const { return first; } template void Pair::getSecond() const { return second; } Class Template Definition void main() { Pair score; Pair seats; score.setFirst(3); score.setSecond(0); seats.setFirst(‘a’); seats.setSecond(‘b’); int r1 = score.getFirst( ); char r2 = seats.getFirst(); } Member Function Templates

• Notice in member function definitions:

• Each definition is itself a ‘template’

• Requires template prefix before each definition

• Class name before :: is ‘Pair

• Not just ‘Pair’ Generic programming • Generalize – Sometimes called “lifting an

• The aim (for the end user) is – Increased correctness • Through better specification

– Greater range of uses • Possibilities for re-use

– Better performance • Through wider use of tuned libraries • Unnecessarily slow code will eventually be thrown away

• Go from the concrete to the more abstract Templates and Inheritance A class template can be derived from a non-template class

A template class can be derived from a class template

A non-template class can be derived from a class template Overloading Function Templates • You overload functions when you create functions with the same name but with different argument lists • You can overload function templates, as long as each version of the function takes different arguments, allowing the compiler to distinguish between them

1 A main() Function that Uses the Overloaded invert() Function Template Overloading Function Ex10-3.cpp Templates

• Overload the displaySmallest() function template to accept two as well as three arguments

3 Templates and Inheritance A class template can be derived from a non-template class

A template class can be derived from a class template

A non-template class can be derived from a class template A class template can be derived from a non-template class

• For Example : #include class base { protected : int x; public : base( ) { } base(int val) { x = val;} }; A class template can be derived from a non-template class template class derived : public base { T y; public : derived( ) { } derived(T val1, int val2) { base(val2); y = val1; } T add( ) ; }; A class template can be derived from a non-template class template T derived : : add( ) { return x + y; } int main( ) { derived ob(10,20); cout<<“Addition is : “ <

• For Example : #include template class base { protected : T x; public : base( ) { } base(T val) { x = val;} }; A template class can be derived from a class template template class derived : public base { T y; public : derived( ) { } derived(T val1, T val2) { base::base(val2); y = val1; } T add( ) ; }; A template class can be derived from a class template template T derived : : add( ) { return x + y; } int main( ) { derived ob(10,20); cout<<“Addition is : “ <

• For Example : #include template class base { protected : T x; public : base( ) { } base(T val) { x = val;} }; A non-template class can be derived from a class template class derived : public base { int y; public : derived( ) { } derived(int val1, T val2) { base::base(val2); y = val1; } int add( ) ; }; A template class can be derived from a class template int derived : : add( ) { return x + y; } int main( ) { derived ob(10,20); cout<<“Addition is : “ <

•catch •throw

•Multiple exceptions

•Exception with arguments Introduction

• Exception: “An abnormal condition that arises in a code sequence at run time”.

• An exception is a run-time error.

• Exception handling allows us to manage run-time errors in an orderly fashion.

Introduction

#include using namespace std; int main() { double a, b, c;

// Request two numbers from the user cout << "Please provide two numbers\n"; cout << "First Number: "; cin >> a; cout << "Second Number: "; cin >> b;

// Multiply the numbers and display the result c = a * b;

cout << "\n" << a << " * " << b << " = " << c << "\n\n"; return 0; } Introduction • When it comes up, the user is asked to simply type two numbers; the program would use them to perform a multiplication and display the result. • Imagine that a user, decides to type the name of a country or somebody’s telephone number as one of the requested values. • Since a program such as this one is not prepared to multiply two strings or one number to a string it would not know what to do. Introduction

• The only alternative the compiler would have is to send the problem to the operating system, hoping that the OS would know what to do. • Whenever the compiler is handed a task, it would try to perform the assignment. If it can’t perform the assignment, for any reason it is not prepared for, it would throw an error. • As a programmer, if you can anticipate the type of error that could occur in your program, you can catch the error yourself and deal with it by telling the compiler what to do when this type of error occurs. Introduction

• An exception is a situation that would be unusual for the program that is being processed. • An error result or an unpredictable behavior on your program not caused by the operating system but that occurs in your program is called an exception. • The ability to deal with a program’s eventual abnormal behavior is called exception handling. Introduction

• Using exception handling, our program can automatically invoke an error-handling routine when an error occurs.

• C++ exception handling is built upon three keywords: try, catch, and throw. The Basics

• try identifies a code where exception can occur • throw causes an exception to be raised (thrown) • catch identifies a code block where the exception will be handled (caught)

try { … throw an exception … } catch the exception 9

Exception Handling Fundamentals ..try • Error prone program statements that we may want to monitor for generation of exceptions are contained in a try block. • Syntax: try { // try block } Exception Handling Fundamentals..try • When an exception is thrown, the remaining code in the try block is skipped, just as in the case of the return statement in a function, and every auto object created after the try block is entered, is destroyed automatically

• The thrown object is caught by the catch block where the execution continues

• Then, the execution continues with the next statement after the catch block

Exception Handling Fundamentals …throw • If an exception (i.e., an error) occurs within the try block, then that exception is thrown using throw. • Syntax: throw exception; • If an exception is to be caught, then throw must be executed either from within a try block or from any function called from within the try block (directly or indirectly). Exception Handling Fundamentals …catch • The thrown exception is caught, using catch block and processed.

• Syntax: catch (type argument) { // catch block }

Catching Exceptions • You must supply at least one catch block for a try block • Otherwise compiler error (let's see it in ExceptionSample1.cpp). int main() { int height; cin >> height; try { if (height > 300) throw "height exceeds maximum"; if (height < 30) throw "height below minimum"; cout << "Person is " <

• Catch blocks must immediately follow the try block without any program code between them. – Otherwise, compiler error (let's see it in ExceptionSample1.cpp). int main()

{ int height; cin >> height; try { if (height > 300) throw "height exceeds maximum"; if (height < 30) throw "height below minimum"; cout << "Person is " <

cout << "Program Stops " << endl;

return 0; } try {

Program statements requires monitor for exceptions

} catch( type argument ) {

Program statements handles for Exception

} try {

Program statements requires monitor for exceptions

} catch( type1 argument ) { catch( type2 argument ) { catch( typen argument ) Program statements{ handles Program for Exception statements handles for ExceptionProgram statements } handles for Exception } } Using try & catch try { Arithmetic int d = 0; int a = 30 / d; throws Exception

}

catch(int e ) {

printf("Division by zero.");

}

Syntax for Exception Handling Code : try-catch Combined with the try block, the syntax of an exception would be: try { ……….. throw exception; } catch(Argument) { // Catch the exception } Exception Handling Fundamentals NOTE: Throwing an unhandled exception causes the standard function terminate() to be invoked. By default, terminate() calls abort() to stop your program.

Code : try- catch-throw

#include using namespace std; int main() { int StudentAge; cout << “Enter Student Age: "; cin >> StudentAge; try { if(StudentAge < 0) throw; cout << "\nStudent Age: " << StudentAge << "\n\n"; }

Code : try- catch-throw catch (... ) is used to catch(...) catch any unhandled { } exception cout << "\n"; return 0; }

Output

• If positive integer is given as input Output : Enter Student Age: 1 Student Age: 1 • If negative integer is given as input Output : Enter Student Age: -1 Program will give an abnormal termination error

Code : try- catch-throw

#include using namespace std; int main() { double Number1, Number2, Result;

// Request two numbers from the user cout << "Please provide two numbers\n";

Code : try- catch-throw

try { cout << "First Number: "; cin >> Number1; cout << "Second Number: "; cin >> Number2; if( Number2 == 0 ) throw (Number2); // Perform a division and display the result Result = Number1 / Number2; cout << "\n" << Number1 << " / " << Number2 << " = " << Result << "\n\n"; }

Code : try- catch-throw

catch(int i) { cout<<“Exception caught : x = “<

return 0;

}

Output • Please provide two numbers First Number: 126.45 Second Number: 5.52 126.45 / 5.52 = 22.9076

Number1 126.45 Number2: 5.52 Result 22.9076

Output • Please provide two numbers First Number: 126.45 Second Number: 0

Exception caught : x=0

Example: Handling an Attempt to Divide by Zero in C++ #include using namespace std; class DivEx { const char *msg; public : DivEx() : msg(“Attempted Divide by Zero Error”) { }; //class ends const char * what( ) const { return msg; } }; Example: Handling an Attempt to Divide by Zero in C++ double quotient(int numerator , int denominator) { if(denominator==0) throw DivEx(); return static_cast(numerator) / denominator; } int main( ) { int num1,num2; double result;

Example: Handling an Attempt to Divide by Zero in C++

cout<>num1>>num2) { try { result = quotient(num1,num2); cout<

}//try ends Example: Handling an Attempt to Divide by Zero in C++

catch(DivEx & obj) { cout<<“Error caught : ”obj.what(); }//catch ends cout<<“Enter two integer numbers (Ctrl – C to end) “ ; }//while ends return 0; }

Throwing an Exception to be Caught by the Calling void Func3() { Code

try { void Func4() Function { Func4(); call

} if ( error ) Normal throw ErrType(); catch ( ErrType ) return { }

}

} Return from thrown exception Exception Handling Stack unwinding

Occurs when a thrown exception is not caught in a particular scope

Unwinding a Function terminates that function

• All local variables of the function are destroyed • Invokes destructors • Control returns to point where function was invoked Attempts are made to catch the exception in outer try…catch blocks

If the exception is never caught, the function terminate is called Stack unwinding

If exception is thrown in a try block (or in a function that is called from a try block or in a function that is called from a function that is called from a try block and so on), all local objects allocated from the runtime stack after the try block was entered are released (go out of scope) and their destructors are called. This process is called stack unwinding.

This process guarantees that when we try to recover from an error, there are no inconsistent data in the runtime stack and there is no memory leak Stack unwinding Example  If error occurs in the function f2, the int main() { //.... destructors for objects myA, myB and try { myC are called and those objects are Aclass myA; deleted from the stack in the reverse f1(); //.... order of creation and before the } exception is caught. //catch come here } void f1() {  Note that this would also return any Bclass myB; dynamically allocated memory used in f2(); //.... those objects (myA, myB, myC), } through their properly implemented void f2() { destructors. Cclass myC; //.... throw "Exception"; } Another example (problematic)

• Sometimes stack unwinding does not suffice since it does not return the dynamic memory to heap. try { int * myarr; myarr = new int [LARGECLASS]; Process(myarr); //suppose an exception is thrown in Process function } catch (...) { //Need to free the heap memory pointed by myarr //But there is a problem – cannot refer myarr cout << "Exception caught" << endl; }

Another example (acceptable, but questionable solution) • Move myarr definition outside the try block so that catch can refer.

• But this time you cannot force myarr pointer to go out of scope by stack unwinding. int * myarr; //moved outside of try try { myarr = new int [LARGECLASS]; Process(myarr); //suppose an exception is thrown in Process function } catch (...) { delete [] myarr; cout << "Exception caught" << endl; }

Another example (best solution)

• Pure object oriented approach.

• Define a class for the array and let the class destructor to handle delete.

• Destructor of local objects are automatically called when exception is thrown but before it is caught (this is what stack unwinding is) • CAUTION: You cannot refer to myarr in the catch block, not only due to scope rules, but also due to the fact it has been destructed when the exception is thrown. try { ArrayClass myarr; myarr.Init(); //allocates memory, etc. myarr.Process(); //suppose an exception is thrown in Process function } catch (...) { cout << "Exception caught" << endl; }

Rethrowing an Exception • Rethrowing exceptions – Used when an exception handler cannot process an exception – Rethrow exception with the statement: throw; • No arguments • If no exception thrown in first place, calls terminate – Handler can always rethrow exception, even if it performed some processing – Rethrown exception detected by next enclosing try block #include #include using std::cout; using std::endl; using std::exception; • 1. Load void throwException() header {

// Throw an exception and immediately catch it. try { • 1.1 cout << "Function throwException\n"; Function throw exception(); // generate exception } prototype catch(exception e) { cout<<“Exception handled in function throw Exception\n”;

}

throw; // rethrow exception for further processing } cout<< “This should not print\n”; }

int main() { try { • 2. Function throwException( ); call cout<< “This should not print\n”; } catch(exception e) • 3. Output { cout<<“Exception handled in main\n”; } cout<<“Program control continues after catch in main\n”; return 0; }

Function throwException Exception handled in function throwException • Program Exception handled in main Output Program control continues after catch in main

Exception Specifications

Exception specification (throw list) means listed exceptions canbe thrown by a function Example: int g( double h ) throw ( a, b, c ) { // function body } Function can throw only listed exceptions or derived types If other type thrown, function unexpected called throw() (i.e., no throw list) states that function will not throw any exceptions In reality, function can still throw exceptions, but calls unexpected (more later) If no throw list specified, function can throw any exception

Exception Specifications

• int someFunction( double value ) throw ( int, double) { ... }

The above example means the someFunction() can throw only int and double type of exception…. If others are thrown it will generate unexpected error. Exception Specifications Example

// Example 1(a) int Func(); // can throw anything\

// Example 1(b) int Gunc() throw(); // will throw nothing int Hunc() throw(int , float); // can only throw int or double Exception Specifications Example

#include int main() { #include set_unexpected(myunexcepted) #include f( ); class X {}; } class Y {}; class Z : public X {}; class W {}; void myunexpected() { cerr<<“Unexpected called”; throw “Hello”;} void f() throw(X, Y) { int n = 0; if (n) throw X(); // OK if (n) throw Z( ); // also OK throw W(); // will call std::unexpected() } Nesting try Blocks • Exceptions are always handled by closest matching handler try { try { throw 5; } catch (int x)‏ { cout << x << endl; // exception will be caught here } } catch (int x)‏ { cout << x-5 << endl; }

Processing Unexpected Exceptions • C++ has following functions to process unexpected exceptions that are not caught.

1) terminate() 2) abort()

Processing Unexpected Exceptions • Function unexpected – Calls the function specified with set_unexpected • Default: terminate • Function terminate – Calls function specified with set_terminate • Default: abort • set_terminate and set_unexpected – Prototypes in – Take pointers to functions (i.E., Function name) • Function must return void and take no arguments – Returns pointer to last function called by terminate or unexpected

Processing Unexpected Exceptions

#include #include class one {}; class two {}; void main() { try { cout<<“\n An Uncaught Exception”; throw two( ); } Here the program terminates as catch(one) no Match is found for catch two, { cout<<“Excpetion for one”; hence abort( ) function is called, } implicitly

} Solution to the above problem is using :

• set_terminate and set_unexpected – Prototypes in – Take pointers to functions which must be called to handle the exception (i.E., Function name) • Function must return void and take no arguments – Returns pointer to last function called by terminate or unexpected

Processing Unexpected Exceptions #include #include class one {}; class two {}; void skip() { cout<<“\n Function skip invoked”; } Here the program instead of void main( ) terminating when no Match is found { set_terminate(skip); for catch two, calls the function skip try { which is associated with cout<<“\nThrowing Exception”; set_terminate( ) function. throw two();

} catch(one) { cout<<“Exception for one”; } } ..At this point function skip is invoked Exception Handling

Constructors, Destructors and Exception Handling

If an exception is thrown during construction of an object consisting of subobjects or array elements, destructors are only called for those subobjects or array elements successfully constructed before the exception was thrown. A destructor for a local static object will only be called if the object was successfully constructed. Constructors, Destructors and Exception Handling

Example  If during stack unwinding a destructor throws an exception and that exception #include is not handled, the terminate() using namespace std; function is called. The following class E { example demonstrates this: const char* message; E(const char* arg) : message(arg) { } }; void my_terminate() { cout << "Call to my_terminate" << endl; };

Constructors, Destructors and Exception Handling

Example class A { A( ) { cout << "In constructor of A" << endl; } ~A() { cout << "In destructor of A" << endl; throw E("Exception thrown in ~A()"); } }; class B { B() { cout << "In constructor of B" << endl; } ~B() { cout << "In destructor of B" << endl; } }; Constructors, Destructors and Exception Handling int main() { The output of this example: set_terminate(my_terminate); try { In try block In constructor of A cout << "In try block" << endl; In constructor of B A a; In destructor of B B b; In destructor of A Call to my_terminate throw E("Exception thrown in try block of main()"); } catch (const char* e) { cout << "Exception: " << e << endl; } catch (...) { cout << "Some exception caught in main()" << endl; } cout << "Resume execution of main()" << endl; }

Exception Handling and Inheritance if we put base class catch first before derived class catch then the derived class catch block will never be reached. if we change the order of catch statements then both catch statements become reachable.

#include using namespace std; if we put base class first then the class Base {}; class Derived: public Base {}; derived class catch block will int main() { never be reached. Derived d; For example, following C++ code // some other stuff try { prints “Caught Base Exception” // Some monitored code throw d; } catch(Base b) { cout<<"Caught Base Exception"; } catch(Derived d) { //This catch block is NEVER executed cout<<"Caught Derived Exception"; } getchar(); return 0; } #include using namespace std; In the above C++ code, if we change class Base {}; class Derived: public Base {}; the order of catch statements int main() { then both catch statements Derived d; become reachable. // some other stuff try { Following is the modifed program // Some monitored code And throw d; } it prints “Caught Derived Exception” catch(Derived d) { cout<<"Caught Derived Exception"; } catch(Base b) { cout<<"Caught Base Exception"; } getchar(); return 0; }