I can't quite wrap my head around how a user will be able to distinguish between the exceptions my functions can throw. One of my functions can throw two instances of std::invalid_argument
.
For example, in a constructor:
#include <stdexcept> // std::invalid_argument
#include <string>
class Foo
{
public:
void Foo(int hour, int minute)
:h(hour), m(minute)
{
if(hour < 0 || hour > 23)
throw std::invalid_argument(std::string("..."));
if(minute < 0 || minute > 59)
throw std::invalid_argument(std::string("..."));
}
}
Note: It's an example, please do not answer with bounded integers.
Say the user calls with foo(23, 62);
, how would the user's exception handler distinguish between the two possible instances of std::invalid_argument
?
Or am I doing this wrong, and I should inherit from std::invalid_argument to distinguish between them? That is,
class InvalidHour: public std::invalid_argument
{
public:
InvalidHour(const std::string& what_arg)
:std::invalid_argument(msg) {};
}
class InvalidMinute: public std::invalid_argument
{
public:
InvalidMinute(const std::string& what_arg)
:std::invalid_argument(msg) {};
}
Then, throw InvalidHour
and InvalidMinute
?
Edit: Creating a class for every possible exception seems a little too much to me, especially in a large program. Does every program that effectively uses exceptions this way come with extensive documentation on what to catch?
As mentioned in an answer, I have considered assert
to. Looking around stackoverflow, I have found a majority of people saying you should throw an exception (as my particular case is for a constructor).
After looking around a lot of online information on when to use exceptions, the general consensus is to use an assert
for logic errors and exceptions for runtime errors. Although, calling foo(int, int)
with invalid arguments could be a runtime error. This is what I want to address.
By handling multiple exceptions, a program can respond to different exceptions without terminating it. In Python, try-except blocks can be used to catch and respond to one or multiple exceptions. In cases where a process raises more than one possible exception, they can all be handled using a single except clause.
Java allows you to catch multiple type exceptions in a single catch block. It was introduced in Java 7 and helps to optimize code. You can use vertical bar (|) to separate multiple exceptions in catch block.
You can't throw two exceptions. I.e. you can't do something like: try { throw new IllegalArgumentException(), new NullPointerException(); } catch (IllegalArgumentException iae) { // ... } catch (NullPointerException npe) { // ... }
Handling More Than One Type of Exception The catch clause specifies the types of exceptions that the block can handle, and each exception type is separated with a vertical bar ( | ). Note: If a catch block handles more than one exception type, then the catch parameter is implicitly final .
The standard exception hierarchy is unsuitable for logic errors. Use an assert
and be done with it. If you absolutely do want to transform hard bugs into harder to detect run time errors, then note that there are only two reasonable things a handler can do: achieve the contractual goal in some possibly different way (possibly just retrying the operation), or in turn throw an exception (usually just rethrowing), and that the exact cause of the original exception seldom plays any rôle in this. Finally, if you do want to support code that really tries various combinations of arguments until it finds one that doesn't throw, no matter how silly that appears now that it's written out in words, well, you have std::system_error
for passing an integer error code up, but you can define derived exception classes.
All that said, go for the assert
.
That's what it's for.
You could also create further error classes that derive from invalid_argument, and that would make them distinguishable, but this is not a solution that scales. If what you actually want is to show the suer a message that he can understand, then the string parameter to the invalid_argument would serve that purpose.
The standard exceptions do not allow storing the additional information you want, and parsing exception messages is a bad idea. One solution is to subclass, as you mention. There are others - with the advent of std::exception_ptr
. it is possible to use "inner" (or "nested") exceptions as in Java or .NET, though this feature is more applicable to exception translation. Some prefer Boost.Exception, as another solution for exceptions extensible at runtime.
Don't fall into the "just assert trap" like Cheers and hth. Simple example:
void safe_copy(const char *from, std::size_t fromLen, char *buf, std::size_t bufLen)
{
assert( fromLen <= bufLen );
std::copy(from, from + fromLen, buf);
}
There's nothing wrong with the assert
per se, but if the code is compiled for release (with NDEBUG
set), then safe_copy
will not be safe at all, and the result may be a buffer overrun, potentially allowing a malicious party to take over the process. Throwing an exception to indicate a logic error has its own problems, as mentioned, but at least it will prevent the immediate undefined behavior in the release build. I'd therefore suggest, in security-critical functions, to use assertions in the debug, and exceptions in the release build:
void safe_copy(const char *from, std::size_t fromLen, char *buf, std::size_t bufLen)
{
assert( fromLen <= bufLen );
if ( fromLen > bufLen )
throw std::invalid_argument("safe_copy: fromLen greater than bufLen");
std::copy(from, from + fromLen, buf);
}
Of course, if you use this pattern a lot, you may wish to define a macro of your own to simplify the task. This is beyond the scope of the current topic, however.
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