Download as pdf or txt
Download as pdf or txt
You are on page 1of 9

Exception handling in CPP

Exception/error is an event when it occurs in our program then our program terminated abnormally and rest of the code from
where error occurred are not executed. CPP provides a mechanism to handle error so that our program terminate normally.

Why errors are occurred

There are some reason to occurred error in our program that are coding errors made by the programmer, error due to wrong
input or other unforeseeable thing.

For exception handling in CPP library there are three keywords are available – throw, try and catch.
Throw  this keyword is used to throw an error. Generally it is used to define custom error by programmer.
For example –
double func(double num){
if(isless(num, 0.0){
cout << “square root of negative value can not be calculate “<< endl;
throw num;
}
else{
cout << sqrt(num) << endl;
}
}
Here, we use throw keyword to throw the error. To use throw statement, simply use the throw keyword followed by a value
of any data type.

In c++ programming, for error handling try and catch block are used. Inside try block that statements are written in which
might be error occurred. If error occurred inside try block statement the program’s control move into catch block to handle
the error and execute the statements inside catch block.
Due to this program terminate normally.
#include <iostream>
#include <cmath>
using namespace std;
void func(double num){
if(isless(num, 0.0)){
cout << "square root of negative value can not be calculate."<< endl;
throw num;
}
else{
cout << sqrt(num) << endl;
}

int main(){

try{

func(144);
}
catch(exception &error){
cout << error.what() << endl;
}
cout << "Program terminates normally";
return 0;
}
In this program inside try block func() is invoked with an argument. Func() function invoked inside try block because, whenever
we pass negative number to func() function then if throw error there it is invoked from try block. Because, inside try block we
write such statements in which might be error occurred. Once error raised inside try block then catch block is executed. This
program terminate normally.

We try to understand with the help of another example –

#include <iostream>
using namespace std;

int division(int a , int b){


if(b == 0){
cout << "denominator should not be zero or in negative";
throw b;
}
else{
return a/b;
}
}

int main(){
int num1, num2;
cout << endl << "Enter base : ";
cin >> num1;
cout << "enter denominator : " << endl;
cin >> num2;
try{
cout << division(num1, num2) << endl;
}
catch(int temp){
cout << temp;
}
return 0;
}

In this example, if we input value for num2 is 0(zero) then division() throw error. Therefore It is written inside try block. Once it
throw error from a particular line the next line does not execute and control move into catch block and execute statements.
After executing catch block’s statement the remaining statements are executed.

The main purpose of exception handling is to handle exception so that the normal of flow of execution maintain and execute
statements after occurred error.

All the exception classes in C++ derived from std::exception classes. Following diagram show the parent-class hierarchy.
User defined exception

By overriding and inheriting std::exception class functionality, we can defined new exception. Let’s see an example

#include <iostream>
#include <excpetion>
using namespace std;

class MyException : public exception{


public:
const char *what(){
return "Attempt to divide by zero!\n";
}
};

void division(int x, int y){


if(y == 0){
MyException z;
throw z;
}
else{
cout << "Division : " << x/y;
}
}

int main(){
int x, y;
cout << "Enter two number : " << endl;
cin >> x >> y;
try{
division(x,y);
}
catch(exception &error){
cout << error.what() << endl;
}
return 0;
}
In this example we defined custom exception (MyException), It throw by division method when value b is zero(0).

errno  It is pre-processor macro used for error indication. It is available inside <cerrno> header file. It acts like an integer
variable. A value is stored in errno by certain library functions when they detect errors. At program startup, the value stored is
zero. Library functions store only values greater than zero. Once the value of errno is changed from zero to non-zero then no
other function in the C++ standard library can change it value to zero.

Most functions indicate that they detected an error by returning a special value, typically, NULL for functions that return
pointers, and -1 for the functions that return integers.

Let’s understand with the help of example –

#include <iostream>
#include <cerrno>
#include <cstring>
using namespace std;

void catFile(char *filename);

char *programName;

int main(int argc, char *argv[]){


programName = argv[0];
if(argc>0)
catFile(*++argv);
}

void catFile(char *filename){


FILE *fd;
if((fd = fopen(filename,"r")) == NULL){
cout << errno;
}
}
In this program if we not provide filename as argument to open by program then we will get the output of errno.

Library functions fall into the following categories:

- Those that set errno and return an out-of-band error indicator


- Those that set errno and return an in-band error indicator
- Those that do no promise to set errno
- Those with differing standards documentation
1. Those that set errno and return an out-of-band error indicator  under this category of function first we need to verify
the out-of-band error indicator return by functions. For example – errno should not be checked after calling ftell()
because, ftell() set errno positive number. Therefore without verifying out-of-band error indicator by function we cannot
write compliant code. Here, ftell() function return -1L when error occurred.
Example –
#include <iostream>
#include <cerrno>
using namespace std;
int main(){
FILE *file;
file = fopen("test.txt","r");
char string[200];
fscanf(file, "%s", string);
if(ftell(file) ==-1){
perror("ftell");
}
else{
cout << "file position : " << ftell(file);
}
return 0;
}
2. Those that set errno and return in-band error indicator under this category, function returns a valid value when error
occurred as well as it also set the errno. So to handle error for such type of function we need to take of both errno and
return value by function. For example – strtoul() this function return long int value after converting string into long int.
But, when error occurred this function return LONG_MAX and it sets errno to ERANGE. Example –
// strtoul()
#include <iostream>
#include <cerrno>
using namespace std;

void func(const char *str){


unsigned long int value;
char *end;
errno =0;
value = strtoul(str, &end, 0);
if((str == end) || (value == ULONG_MAX && errno == ERANGE)){ // here, we check for error
cout << "error occurred";
}
else{
cout << value << endl;
}
}

int main(){
func("123abc");
return 0;
}

For error handling purpose with this category of functions, errno must be set to 0 before calling the function and then
inspect errno value after calling the function as well as also need to take care of return value by function as shown in the above
example.

3. Those function that do not promise to set the errno  in cpp standard library there are some functions that do not
promise to set errno when error occurred. At this situation, we need to take care about the return value by functions. For
example setlocale(). Setlocale() do not promise to set the errno when error occurred but this function return null pointer
when error occurred. So for error handling we check return value by function is telling about the error or not.
4. Library functions with differing standards documentation  In C++ there are some function which definition are different
in different –different standard documentation. For example – fopen() . when fopen() encounter error then it returns null
pointer and no mention about the errno. But, according to POSIX fopen() returns null pointer when it encounters an error
and set errno to value indicating an error. At this situation, we need to be careful and find the common thing among the
documentation then write code for error handling. For example – in all standard documentation fopen() function return
null pointer if it encounters error. Therefore, we write code for error handling to keep in mind this thing. Let’s understand
with the help of example –
// fopen()
#include <iostream>
using namespace std;

int main(){
FILE *file;
if((file = fopen("test.txt", "r")) == NULL){
cout << "error occurred !";
}
return 0;
}
In this example, we do not take care about the errno. Because, about fopen() function different standard document
defined in different way regarding errno.

Abort()  it is available in <cstdlib> header file. This function is used to terminate the programs abnormally. When this
function is invoked then programs terminates abnormally and control returns to the host environment. Abort() function raise
a signal SIGABRT. If SIGABRT signal is handled by signal handler then it is executed automatically when abort() function is
called. It does not call any function that are registered by atexit() or at_quick_exit().
Example –
// fopen()
#include <iostream>
#include <cstdlib>
using namespace std;

int main(){
FILE *file;
if((file = fopen("test.txt", "r")) == NULL){
abort();
}
}
In this example, if ‘test.txt’ file does not found then abort() is invoked and terminates program abnormally.

exit()  this function is used to terminate the process and control returns to the parent process. This function is generally
used in multithreading programming. This function perform cleanup task before terminating the process and the function
which are registered by atexit(). Cleanup task means – remove the data from buffered, close the open file, etc.

the main purpose to perform cleanup task is that parent process execute normally without encounter any memory related
problem or any interruption. If cleanup task is not performed then there may be chance that parent process encounter any
interruption or behave abnormal.
Following is the syntax of exit() function –
Void exit(int status);
Here status show the termination status. If its value is zero(0) it means process terminates normally. If its value is non-zero it
means process terminates with some error.
#include <iostream>
#include <cstdlib>
using namespace std;

void func(){
cout << "registered function" << endl;
}

int main(){
int i=1;
atexit(func);
while(i < 10){
cout << i << endl;
if(i == 5){
exit(0);
}
i++;
}
return 0;
}
In this program func() is registered for exit() function. When exit(0) function is called then it internally call that function which
is registered with atexit(). We will get following output –
1
2
3
4
5
registered function
the function pointed by atexit() is automatically called when the program terminates normally. In case more than one function
are registered with atexit() function then all functions are executed in the order of stack.
Example –
#include <iostream>
#include <cstdlib>
using namespace std;

void func(){
cout << "registered function" << endl;
}

void func2(){
cout << "registered function 2" << endl;
}

int main(){
int i=1;
atexit(func);
atexit(func2);
while(i < 10){
cout << i << endl;
if(i == 5){
exit(0);
}
i++;
}
return 0;
}
Output for above program
1
2
3
4
5
registered function 2
registered function

_Exit()  It is similar to exit() function. That means this function also used to terminate the process normally and control
returns to the parent process but, this function does not perform any clean up task as well as it does not call the registered
function. Example –

#include <iostream>
#include <cstdlib>
using namespace std;

void func(){
cout << "Exit" << endl;
}

int main(){
int i = 1;
int exit_code = 10;
while( i < 10){
if(i == 5)
_Exit(exit_code);
cout << i << endl;
i++;
}
return 0;
}
When we execute this program we will get the following output
1
2
3
4
Here, func() is not called by _Exit().
If we registered same function more than one times then at function calls as many times it is registered.
Example –
#include <iostream>
#include <cstdlib>
using namespace std;

void func(){
cout << "registered function" << endl;
}

void func2(){
cout << "registered function 2" << endl;
}

int main(){
int i=1;
atexit(func2);
atexit(func2);
while(i < 10){
cout << i << endl;
if(i == 5){
exit(0);
}
i++;
}
return 0;
}
In this example func2() registered with two times, therefore it called two times. We will get the following output of this
program –
1
2
3
4
5
registered function 2
registered function 2

quick_exit()  this function is defined in <cstdlib> header file. This function terminates the process normally and returns
control to the host environment. It calls all the registered function which are registered with at_quick_exit(). Before
terminating the process it perform clean up task. Following is the syntax of quick_exit().
void quick_exit(int status);
It is similar to exit() function. Here, if status is set to zero(0) of EXIT_SUCCESS it means process terminates successfully.
If status is set to non-zero or EXIT_FAILURE it means process does not terminate successfully.
Example –
#include <iostream>
#include <cstdlib>
using namespace std;
void func1(){
cout << "quick_exit handler 1" << endl;
}

void func2(){
cout << "quick_exit handler 2" << endl;
}

int main(){
at_quick_exit(func1);
at_quick_exit(func2);
quick_exit(0);
return 0;
}
When we execute this program we will get following output –
quick_exit handler 2
quick_exit handler 1

You might also like