While designing my first 'serious' C++ library, I'm asking myself:
Is it good style to derive ones exceptions from std::exception
and it's offsprings?!
Even after reading
I'm still not sure. Because, besides common (but maybe not good) practice, I would assume, as a library user, that a library function would throw std::exception
s only when standard library functions failed in the library implementation, and it can't do anything about it. But still, when writing application code, for me it's very convenient, and also IMHO good looking to just throw a std::runtime_error
. Also my users also can rely on the defined minimum interface, like what()
or codes.
And for example, my user supplies faulty arguments, what would be more convenient, than to throw a std::invalid_argument
, wouldn't it?
So combined with the yet common use of std::exception I see in others code:
Why not go even further and derive from your custom exception class (e.g. lib_foo_exception) and also from std::exception
.
Thoughts?
2 Answers 2
All exceptions should inherit from std::exception
.
Suppose, for example, I need to call ComplexOperationThatCouldFailABunchOfWays()
, and I want to handle any exceptions that it could throw. If everything inherits from std::exception
, this is easy. I only need a single catch
block, and I have a standard interface (what()
) for getting details.
try {
ComplexOperationThatCouldFailABunchOfWays();
} catch (std::exception& e) {
cerr << e.what() << endl;
}
If exceptions do NOT inherit from std::exception
, this gets much uglier:
try {
ComplexOperationThatCouldFailABunchOfWays();
} catch (std::exception& e) {
cerr << e.what() << endl;
} catch (Exception& e) {
cerr << e.Message << endl;
} catch (framework_exception& e) {
cerr << e.Details() << endl;
}
Regarding whether to throw runtime_error
or invalid_argument
versus creating your own std::exception
subclasses to throw: My rule of thumb is to introduce a new subclass whenever I need to handle a particular type of error differently than other errors (i.e., whenever I need a separate catch
block).
- If I introduce a new exception subclass for every conceivable type of error, even if I don't need to handle them separately, then that adds a lot of class proliferation.
- If I reuse existing subclasses to mean something specific (i.e., if a
runtime_error
thrown here means something different than a generic runtime error), then I run the risk of conflicting with other uses of the existing subclass. - If I don't need to handle an error specifically, and if the error that I'm throwing exactly matches one of the existing standard library's errors (such as
invalid_argument
), then I reuse the existing class. I just don't see much benefit to adding a new class in this case. (The C++ Core Guidelines disagree with me here - they recommend always using your own classes.)
The C++ Core Guidelines have further discussion and examples.
-
This rationale doesn't make sense to me. Isn't this what
catch (...)
(with the literal ellipsis) is for?Maxpm– Maxpm2018年06月11日 22:17:34 +00:00Commented Jun 11, 2018 at 22:17 -
2@Maxpm
catch (...)
is only useful if you don't need to do anything with what's thrown. If you want to do something - e.g., show or log the specific error message, as in my example - then you need to know what it is.Josh Kelley– Josh Kelley2018年06月12日 12:42:05 +00:00Commented Jun 12, 2018 at 12:42
I would assume, as a library user, that a library function would throw std::exceptions only when standard library functions failed in the library implementation, and it can't do anything about it
That is an incorrect assumption.
The standard exception types are provided for "commoner" use. They are not designed to only be used by the standard library.
Yes, make everything ultimately inherit from std::exception
. Oftentimes, that'll involve inheriting from std::runtime_error
or std::logic_error
. Whatever is appropriate for the class of exception you're implementing.
This is of course subjective — several popular libraries completely ignore the standard exception types, presumably to decouple the libraries from the standard library. Personally I think this is extremely selfish! It makes catching exceptions that much more difficult to get right.
Speaking personally, I often just throw a std::runtime_error
and be done with it. But that's getting into a discussion about how granular to make your exception classes, which is not what you're asking.
std::exception
does not mean you throw astd::exception
. Also,std::runtime_error
does inherit fromstd::exception
in the first place, and thewhat()
method comes fromstd::exception
, notstd::runtime_error
. And you should definitely create your own exception classes instead of throwing generic exceptions such asstd::runtime_error
.lib_foo_exception
class derives fromstd::exception
, the library user would catchlib_foo_exception
by just catchingstd::exception
, in addition to when he catches only the libary one. So I could also ask Should my library exception root class inherit from std::exception .lib_foo_exception
?" With inheriting fromstd::exception
you can do it bycatch(std::exception)
OR bycatch(lib_foo_exception)
. Without deriving fromstd::exception
, you would catch it if and only if, bycatch(lib_foo_exception)
.catch(...)
. It's there because the language allows for the case you're considering (and for "misbehaving" libraries), but that's not modern best practice.catch
sites, and likewise coarser transactions that model a user-end operation. If you compare it to languages which don't promote the idea of generalized catching ofstd::exception&
, e.g., they often have a lot more code with intermediarytry/catch
blocks concerned with very specific errors, which somewhat diminishes the generality of exception-handling as it's starting to place a much stronger emphasis on manual error handling, and also on all the disparate errors that could possibly occur.