I've been reading about exceptions in C++, the pros and cons, and I've yet to encounter anyone mention the two things I really like about them: They allow me to extend the definition of an error (to something that is more than just an error message as a string), and, they allow me to define a contract between the exception handler and the thrower:
"If I'm going to handle this particular type of error, I'm going to need this, this and that from you."
I've not seen this mentioned in any of the articles, forums and blog posts, as a pro or a con. And that has made me suspect that my mindset on exceptions and how they should be used may be wrong.
To clarify, here's an example of my usage of exceptions:
A program communicates with a USB device (it sends commands to the device and expects a certain response from it). This USB device can sometimes behave in an unexpected way when responding to a command. In this case, the program will throw an UnexpectedResponseFromDevice
exception. The handler for this exception needs more than just an error message in order to do its job. For example, it may need to know of the command that we were sending to the device when the error occurred, and/or the state the device was in. I use the definition of my UnexpectedResponseFromDevice
class to explicitly set out what is required to handle the exception. See below for a rough idea.
class UnexpectedResponseFromDevice : public std::exception
{
private:
Command command;
DeviceState deviceState;
std::string msg;
public:
UnexpectedResponseFromDevice(std::string msg, Command command, DeviceState deviceState, ...)
Command getCommand();
DeviceState getDeviceState();
};
This is what I meant by "define a contract between the exception handler and the thrower". In order to throw the exception, these things (in this case a Command, a DeviceState and a message) need to be provided.
Is this an acceptable use case for exceptions? Is it OK for me to store this other information, that is required for the handling of the exception, in the exception object? Is it acceptable but a bad idea? If so, please explain why.
What happens if an exception is not caught? If an exception is not caught (with a catch block), the runtime system will abort the program (i.e. crash) and an exception message will print to the console.
I also know that the following cannot throw exceptions either: Destructors. Reading/writing primitive types.
When an error occurs within a method, the method creates an object and hands it off to the runtime system. The object, called an exception object, contains information about the error, including its type and the state of the program when the error occurred.
Both exceptions and errors are the subclasses of a throwable class. The error implies a problem that mostly arises due to the shortage of system resources. On the other hand, the exceptions occur during runtime and compile time.
Is this an acceptable use case for exceptions? Is it OK for me to store this other information, that is required for the handling of the exception, in the exception object?
Yes, that's how the exceptions in the standard library do too. One example is std::system_error
that derives from std::runtime_error
but adds a std::error_code
member to carry extra information. One that adds a lot more is std::filesystem::filesystem_error
that adds two std::filesystem::path
objects.
Is it acceptable but a bad idea? If so, please explain why.
It's only a bad idea if you risk throwing another exception (like a std::bad_alloc
) while throwing your exception - or if you use exceptions as something else than exceptions, like choosing between common branches in your program flow. Throwing should preferably be a rare event.
If your have all the info you need in a std::string
, or even in something like a std::stack<std::string>>
, member variable in the object that is a about to throw
you can probably std::move
that member object into the exception object to minimize the risk - if you don't need that data anymore in the object that is throwing that is.
I don't know how big your std::string
, Command
and DeviceState
objects are, but you may not want to construct the exception object by taking the parameters by value. Try to make construction noexcept
. The getters should also probably return by const&
. That's not as critical - but making them so minimizes the risk of an exception in your exception handler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With