In computer programming, exception handling is the process of dealing with errors in a program such that it functions under unusual conditions. Further, it clearly explains why the program failed to the user. In many instances, a software program gets bad data or other unusual conditions that impact the code’s ability to run successfully.
As a result, exception handling is key to software development because reliably handling errors can prevent software programs from exhibiting unintended behavior. Implementing error handling well ideally guarantees code will successfully run under any condition.
Examples of unintended behavior include a program running slowly or failing upon receiving bad input. In the case of bad input, consider a function that takes a real-valued number (float) as user input, performs a calculation, and returns the result value. If the function expects a floating-point value but instead receives a string, the function will throw an error and fail. Error exception handling can be used to deal with cases like this. If a user inputs a bad value like a string, the program will return a meaningful message instead of failing or providing an unclear system error message.
In C++, exception handling uses the expressions Try, Throw and Catch. The Try expression identifies the block of code that may have error exceptions. It may contain logic such as dividing two numbers or iterating over a list of numbers. The Throw expression handles the exception. For example, when your code tries to divide two numbers but the denominator is zero, you can throw an error message such as “Can’t divide by Zero.” Finally, the Catch expression handles the error. These expressions can handle a variety of exceptions in C++, including length errors for arrays and vectors.
Here, we will walk through some simple examples of how to implement exception handling in C++. First, we’ll consider a runtime error example. We will define a function that takes height and weight as inputs and calculates body mass index. The formula for BMI is weight divided by height squared. If a value of zero is passed as an argument to the function, the code will attempt to divide by zero. We will use try, catch and throw to handle the runtime error. Finally, we will walk through an additional example of exception handling with length errors. Both of these examples should provide a good starting point for the developer just getting started with exception handling in C++.
How to Write Clean Exception Handling Code in C++
Runtime Exception Handling
To demonstrate exception handling for runtime error, we will walk through building a simple BMI calculator application. The app will take the user’s name, weight and height as inputs and display the calculated BMI. First, let’s walk through how to build out the application without any exception handling.
To start, let's write a script that we will call bmi.cpp. We will include <iostream> and <string> headings. The <iostream> heading allows us to write to the standard input/output streams. The <string> heading allows us to define string identifiers.
#include <iostream>
#include <string>
using namespace std;
When compiled and executed, the program will ask for the user’s name. To do this, we need to define a string variable for name and two float variables for weight and height, respectively:
// Main() function: where the execution of program begins
int main()
{
string name;
float weight;
float height;
}
Next, let’s add the logic for asking for the user’s name:
// Main() function: where the execution of program begins
int main()
{
string name;
float weight;
float height;
cout << "Please Enter your Name \n";
cin >> name;
}
To compile our code, we run the following command in terminal:
g++ bmi.cpp
And we execute using the following command:
./a.out
We get the following output:
Now, let’s write logic that greets the user upon entering their name and asks for their weight:
// Main() function: where the execution of program begins
int main()
{
string name;
float weight;
float height;
cout << "Please Enter your Name \n";
cin >> name;
cout << "Hello " << name << ", please enter your weight in Kg \n";
}
If we compile and execute, we get the following:
We can then add the logic for weight as a user input:
int main()
{
string name;
float weight;
float height;
cout << "Please Enter your Name \n";
cin >> name;
cout << "Hello " << name << ", please enter your weight in Kg \n";
cin >> weight;
}
The user will be prompted to enter a value for weight. Now, let’s include logic so that, upon providing a weight in kg, we ask the user for their height in meters:
int main()
{
string name;
float weight;
float height;
cout << "Please Enter your Name \n";
cin >> name;
cout << "Hello " << name << ", please enter your weight in Kg \n";
cin >> weight;
cout << "Thank you " << name << ", now please enter your height in meters \n";
cin >> height;
}
Now we can use this data to calculate a BMI. The formula for BMI is weight/height2. I input the values Sadrach for my name, 90 kg for my weight and 1.92 m for my height:
int main()
{
string name;
float weight;
float height;
float bmi;
cout << "Please Enter your Name \n";
cin >> name;
cout << "Hello " << name << ", please enter your weight in Kg \n";
cin >> weight;
cout << "Thank you " << name << ", now please enter your height in meters \n";
cin >> height;
bmi = weight/(height*height);
cout << "Your BMI is: " << bmi <<"\n";
}
With a height of 1.92 m and weight of 90 kg, we can calculate a BMI of 24.4:
Now, let’s try passing a value of zero for height:
We see that, upon entering zero for height, we have a BMI calculation of “inf,” meaning infinity. This clearly isn’t a useful value for the user and may only cause confusion. Instead of displaying infinity, we should try to catch this error and display a message to the user saying that they provided an invalid value for height. We can do this by catching a runtime error.
First, at the top of our script, we need to import stdexcept to use runtime_error:
#include<stdexcept>
Next, we can rewrite the logic for our BMI calculation in a function:
float BMI_calculator(float weight, float height){
if (height == 0){
throw runtime_error("You attempted to calculate BMI with an invalid value of zero for height \n");
}
return weight/(height*height);
}
We can then modify our main function to try to calculate BMI:
try{
bmi = BMI_calculator(weight, height);
cout << "Your BMI is: " << bmi <<"\n";
}
And catch the runtime error when it occurs. If it occurs, we display the text “Warning: You attempted to calculate BMI with an invalid value of zero for height.”
catch (runtime_error&e){
cout<< "Warning: "<< e.what();
}
We then compile and execute our code, passing in zero as our input for height. We get the following display message:
We see that this message is much more descriptive and useful than the original display message “Your BMI is: inf.”
Length Error Exception Handling
Another common exception is the length error exception with vectors. To demonstrate the occurrence of this type of error, we will define a function that calculates the sum of monthly contributions to a retirement account. First, let’s define a C++ script called retirement_contributions.cpp.
Let’s add our main function as well as add the necessary <iostream> heading, a <vector> heading that will allow us to define vectors:
#include <iostream>
#include <stdexcept> // std::out_of_range
#include <vector>
using namespace std;
int main(){
}
Let’s add logic so that a user can specify the number of months over which they want to calculate the total retirement contribution:
#include <iostream>
#include <stdexcept> // std::out_of_range
#include <vector>
int main(){
int months;
cout << "Please enter the number of months \n";
cin >> months;
}
Next, let’s define a vector that is the size of the number of months. The vector will contain dollar amount contributions, so it will be a vector of floats:
int main(){
int months;
cout << "Please enter the number of months \n";
cin >> months;
std::vector<float> contributions(months);
}
Let’s initialize a variable called current month, which will be the index we will use to iterate over our array:
int current_month = 1;
Let’s then define the first element of our array as our initial contribution:
contributions[1] = initial_contrbution;
And within a while loop, while the current month is less than the total number of months, we increase subsequent contributions by 2 percent:
while (current_month <= months){
contributions[current_month + 1]
=1.02*contributions[current_month];
}
We can also display each month’s contribution while incrementally increasing our index by one:
while (current_month <= months){
contributions[current_month + 1]
=1.02*contributions[current_month];
cout << "Month " << current_month << " contribution is:
" << contributions[current_month]<< endl;
current_month++;
}
So, the full script is as follows:
int main(){
int months;
int current_month = 1;
cout << "Please enter the number of months \n";
cin >> months;
std::vector<float> contributions(months);
float initial_contrbution = 100;
contributions[1] = initial_contrbution;
while (current_month <= months){
contributions[current_month + 1] =1.02*contributions[current_month];
cout << "Month " << current_month << " contribution is: " << contributions[current_month]<< endl;
current_month++;
}
}
If we compile and execute this script using an input value of five months, we get the following output:
From here, we can calculate the sum of contributions. Let’s initialize a float variable we will use to store the sum and, within the while loop, calculate it:
int main(){
int months;
int current_month = 1;
cout << "Please enter the number of months \n";
cin >> months;
std::vector<float> contributions(months);
float initial_contrbution = 100;
float sum_contributions = 0;
contributions[1] = initial_contrbution;
while (current_month <= months){
contributions[current_month + 1] =1.02*contributions[current_month];
cout << "Month " << current_month << " contribution is: " << contributions[current_month]<< endl;
sum_contributions += contributions[current_month];
current_month++;
}
cout<<"Sum of contributions for " << months << " months is: "<<sum_contributions << endl;
}
Now let’s consider an exceptional case. Suppose a user accidentally inputs a negative value, let’s say negative five, for months. We get the following std::length_error:
We see that our code fails with a cryptic message related to the length of our vector, std::length_error. This is because we can define a vector with a length of negative five. We can use exception handling to solve cases like this:
try{
std::vector<float> contributions(months); //float contributions[months];
float initial_contrbution = 100;
float sum_contributions = 0;
contributions[1] = initial_contrbution;
while (current_month <= months){
contributions[current_month + 1] =1.02*contributions[current_month];
cout << "Month " << current_month << " contribution is: " << contributions[current_month]<< endl;
sum_contributions += contributions[current_month];
current_month++;
}
cout<<"Sum of contributions for " << months << " months is: "<<sum_contributions << endl;
catch (const std::length_error& le) {
std::cerr << "Length of " << le.what() << " can't be negative \n";
}
So, the full script is as follows:
int main(){
int months;
int current_month = 1;
cout << "Please enter the number of months \n";
cin >> months;
try{
std::vector<float> contributions(months); //float contributions[months];
float initial_contrbution = 100;
float sum_contributions = 0;
contributions[1] = initial_contrbution;
while (current_month <= months){
contributions[current_month + 1] =1.02*contributions[current_month];
cout << "Month " << current_month << " contribution is: " << contributions[current_month]<< endl;
sum_contributions += contributions[current_month];
current_month++;
}
cout<<"Sum of contributions for " << months << " months is: "<<sum_contributions << endl;
catch (const std::length_error& le) {
std::cerr << "Length of " << le.what() << " can't be negative \n";
}
}
}
Now, if we compile, execute, and pass in a negative value for months, we appropriately handle the error:
We see that by appropriately handling the length error, we’re able to display a descriptive message to the user.
The scripts used in this blog are available on GitHub.
Wrapping Up Exception Handling
Exception handling is a very important part of software programming. It allows developers to handle unexpected behavior of code, anomalous inputs, unexpected runtimes, and much more. It is often a useful safeguard against code failing to run successfully.
Further, exceptional cases often result in cryptic or hard to understand system generated errors. Opaque, system-generated error messages can be frustrating to users, developers and engineers. If a user accidentally provides a bad input value, it is best to have measures for handling these types of values and providing a meaningful message to the user as to why the error occurred. Understanding exception handling is important for software developers, software engineers and even for machine learning engineers and data scientists.