Exception handling in C++

Exceptions are used to indicate that a program has encountered run-time anomalies or abnormal conditions. Let us use the Sorter::sortInAscending task that we discussed in an earlier post as an example. To follow this tutorial, it is highly recommended that you download the code from this link and repeat the instructions on your system. The sorting task that we implemented in the earlier post has the following functionality: 

  1. Read a text file containing one integer per line.
  2. Sort the integers in increasing order.
  3. Output the sorted values to another text file.

Reading of the text file is implemented in the function Sorter::readIntegersFromFile. Let us run the code using the following command line argument: 

./homework sortint ../sample_data/integers.txt  /tmp/result.txt

You will see the sorted list of numbers in the file "/tmp/result.txt". Let us run the code using an input file that does not exist: 

./homework sortint ../sample_data/not_there.txt  /tmp/result.txt

The result file is empty. As per the functionality mentioned, the code is doing the right thing. But let us say we now place a few addtional requirements. The code must throw an exception if:

  1. Input file cannot be read. 
  2. Output file cannot be written. 
  3. Input file contains non-integer values. 

This can be implemented using C++ exceptions. The list of standard exceptions defined in <exception> are below (Image source):

Replace lines 7 to 10 of the file Sorter.cpp with the following:


   if(filePointer==NULL){
    LogManager::writePrintfToLog(LogManager::Error, "Sorter::readIntegersFromFile", "Error, could not open file: %s\n", inFileName.c_str());
    std::stringstream errMsg;
    errMsg << "Could not open input file '" << inFileName  << "'";
    throw std::runtime_error(errMsg.str()); 
  }
  
Save the file, compile the code, and execute the following command:


make; ./homework sortint ../sample_data/not_there.txt  /tmp/result.txt 

[100%] Built target homework

terminate called after throwing an instance of 'std::runtime_error'
  what():  Could not open input file '../sample_data/not_there.txt'
Aborted (core dumped)
  

An error has appeared on the command line to show that the file could not be opened. The earlier code was printing a message to the log file. What is the benefit of throwing an exception or error? The advantage is that an error/exception thrown on a certain line is propagated back through the stack of functions upto that line. A user-defined exception can also be created by extending a standard error. Create the file util/UserDefinedExceptions.h and add the following lines: 


#ifndef UserDefinedExceptions_H
#define UserDefinedExceptions_H

#include < bits/stdc++.h >

class FileIOException : public std::runtime_error{
 public:
 FileIOException(const char* inputErrMessage) : std::runtime_error(inputErrMessage) { }
};

#endif
Create the file util/UserDefinedExceptions.cpp and add the following line:

#include "UserDefinedExceptions.h"
In the file Sorter.cpp, change line no 11 to the following:

throw FileIOException(errMsg.str().c_str());
Save all files. Since a new file has been created, you will have to run cmake from the command line. After cmake is finished, compile the code using make and execute the code. The command line arguments and screen results are below:

acv@acv-vm:~/practice/logger/bin$ cmake ..
-- Configuring done
-- Generating done
-- Build files have been written to: /home/acv/practice/logger/bin

acv@acv-vm:~/practice/logger/bin$ make; ./homework sortint ../sample_data/not_there.txt  /tmp/result.txt 
[100%] Built target homework
terminate called after throwing an instance of 'FileIOException'
  what():  Could not open input file '../sample_data/not_there.txt'
Aborted (core dumped)
Note that the error has changed to 'FileIOException'. Replace lines 31 and 32 in Sorter::writeVectorToFile with the following:

std::stringstream errMsg;
errMsg << "Could not write to output file '" << outFileName  << "'";
throw FileIOException(errMsg.str().c_str());
Save the files, compile the code and run it using the command lines shown below. Notice that we have used the correct input file, but the file name passed for the output file is "/result.txt". The user does not have write permission to this location. Becasue we are throwing an exception, an error is printed to the screen.

acv@acv-vm:~/practice/logger/bin$ make; ./homework sortint ../sample_data/integers.txt  /result.txt 
Scanning dependencies of target homework
[ 20%] Building CXX object CMakeFiles/homework.dir/src/Sorter.cpp.o
[ 40%] Linking CXX executable homework
[100%] Built target homework
terminate called after throwing an instance of 'FileIOException'
  what():  Could not open output file '/result.txt'
Aborted (core dumped)
In file Sorter.cpp, modify the function Sorter::readIntegersFromFile by adding following code at line no 20:

   if (fscanfStatus == 0){
    // The status becomes zero when the fscanf function did not find any entries matching the
    // scan pattern. i.e. In this case, no integers were found. 
    std::stringstream errMsg;
    errMsg << "Error while parsing the input file '" << inFileName <<"'. Encountered a non-integer value.\n";
    throw std::runtime_error(errMsg.str());
  } 
  
Change the ../sample_data/integers.txt file by adding alphabets in one line, and store it as "../sample_data/invalid_integers.txt". Example is shown below:

34
2
abc
Save the files, compile the code and run the following commands:

make; ./homework sortint ../sample_data/invalid_integers.txt  /tmp/result.txt 
Scanning dependencies of target homework
[ 20%] Building CXX object CMakeFiles/homework.dir/src/Sorter.cpp.o
[ 40%] Linking CXX executable homework
[100%] Built target homework
terminate called after throwing an instance of 'std::runtime_error'
  what():  Error while parsing the input file '../sample_data/invalid_integers.txt'. Encountered a non-integer value.

Aborted (core dumped)
We see an error message saying that a non-integer value has been encountered. We have now seen how to throw standard exceptions, and create user-defined exceptions. The completed code from this tutorial is available here. This tutorial did not cover how to catch exceptions. This is left to the student to explore and learn from a future article. Several tutorials throw more light on exceptions. A few are below:

Comments

Popular posts from this blog

Discrete Wavelet Transform

A log Manager for C++

Camera calibration