Exception handling with try-catch in C++

While programming a piece of code that will be reused, think a library, you need to decide how to handle errors. What to do when something wrong happens.

When there’s an error, which cannot be handled locally, what you need is to get information about it to where something can be done to properly manage it. Exceptions exist for this. You throw an exception object at the point where the error occurred and catch it where it can be handled properly.

To throw an exception use the throw keyword. To catch it use a try {} catch () {} block.

In this article you will get the details on throwing exceptions, catching them, creating custom exception objects and re-throwing exceptions.

Throwing Exceptions

You can throw in an exception any object that can be copied and moved in C++. Nonetheless you’ll do well by using standard library exception objects 1.

Let’s look a code example where an exception is thrown.

void some_function()
{
	//...
	if (error)
		throw std::runtime_error{"some error"};
}

In this example if error is true the code throws an exception object of the standard library runtime_error2 type with the message “some error”. On the code that eventually catches this exception you can read the message “some error” using the std::runtime_error member function what().

Catching Exceptions

To catch the exception thrown above consider the following example.

void another_function()
{
	//...
	try {
		some_function();
	}
	catch (std::runtime_error& re) {
		// some code to handle the error
		//...
		std::cout << re.what();
	}
}

The catch part is the exception handler which is invoked for all exceptions, thrown within the try block, of the type (or derived from) std::runtime_error. In this example the exception object is passed by to the handler by reference3 to avoid copying, but that is not required.

Catching All Exceptions

The previous example only catches exceptions with objects of the type, or derived from, std::runtime_error. To catch all exceptions that occur within a try{} block you use the catch(...) which means “catch any exception”4. For example:

void yet_another_function()
{
	try {
		// ...
	}
	catch (...) {
		// do something ...
	}
}

Multiple Exception Handlers

A try block can have multiple catch clauses, to handle multiple exceptions in a differentiated manner. For example:

void multiple_handler_example_function()
{
	try {
		// ...
	}
	catch (std::runtime_error& re) {
		// handle a runtime error ...
	}
	catch (std::exception& e) {
		// handle any standart library exception ...
	}
	catch (...) {
		// handle any exception ...
	}
}

The order in which the handlers are written in this example is significant. The handlers (catch) are tried in order. In this example if a runtime_error exception occurs it will be handled by the first catch. But if we had written the catch (std::exception& e) first then a runtime_error exception would be handled by this handler instead, because runtime_error exception is derived from std::exception1.

Re-throwing Exceptions

If an exception handler decides it can’t completely deal with the error, it can throw the exception again so that it can be fully handled somewhere else. For example:

void rethrow_example_function()
{
	try {
		// ...
	}
	catch (std::runtime_error& re) {
		if (can_handle_completely) {
			// ...
			return;
		}
		else {
			// ...
			throw; // re-throw the exception
		}
	}
}

To throw an exception again, in a exception handler, just write throw;.

Creating Custom Exception Objects

So far in the examples only standard library exception types where used. But you can define your own exception object types. You may want to do it for pin-pointing specific errors. Also you may require specific functionalities in the exception object, like a specific class member function.

To be able to handle a specific error you can create a my_error type and throw an exception of that type. You can also decide that in addition to a what you also need a where member function on your exception object, which will provide information on where in the source code the error occurred (may be useful for debugging). For example, you can declare and define the following exception class:

class my_error {
	public:
		my_error(string what_msg, string where_msg);
		string what();
		string where();
	private:
		string what_msg_;
		string where_msg;
};

I’m omitting the definition of the class for brevity purposes.

With this example class, when your specific error occurs you can throw an exception with a my_error object and handle it with a try{...} catch(my_error e){...}.

Instead of creating an entirely new class you can derive one from the standard library exception.

Notes

  1. Stroustroup, 2013, p. 869.
  2. stdexcept runtime_error
  3. Stroustroup, 2013, p. 190.
  4. Stroustroup, 2013, p. 370.

References

  • Stroustrup, Bjarne. 2013. The C++ Programming Language. 4th ed. Pearson Education, Inc. pp. 363-374, pp. 868-873.
0%