15

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::exceptions 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?

  • I'm not sure I follow. Just because you inherit from std::exception does not mean you throw a std::exception. Also, std::runtime_error does inherit from std::exception in the first place, and the what() method comes from std::exception, not std::runtime_error. And you should definitely create your own exception classes instead of throwing generic exceptions such as std::runtime_error. – Vincent Savard Dec 22 '15 at 13:32
  • 3
    The difference is, that when my lib_foo_exception class derives from std::exception, the library user would catch lib_foo_exception by just catching std::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 . – Superlokkus Dec 22 '15 at 13:40
  • 3
    @LightnessRacesinOrbit I mean "...in addition to", like "How many ways are there to catch lib_foo_exception?" With inheriting from std::exception you can do it by catch(std::exception) OR by catch(lib_foo_exception). Without deriving from std::exception, you would catch it if and only if, by catch(lib_foo_exception). – Superlokkus Dec 22 '15 at 14:17
  • 2
    @Superlokkus: We sort of ignore 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. – Lightness Races in Orbit Dec 22 '15 at 14:45
  • 1
    A lot of the design of exception-handling in C++ tends to encourage coarser, more general 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 of std::exception&, e.g., they often have a lot more code with intermediary try/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. – user204677 Dec 22 '15 at 18:03
29

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.

  • All those ampersands! C++ is weird. – SuperJedi224 Dec 23 '15 at 0:48
  • 2
    @SuperJedi224 and all those different letters! English is weird. – johannes Dec 23 '15 at 7:13
  • This rationale doesn't make sense to me. Isn't this what catch (...) (with the literal ellipsis) is for? – Maxpm Jun 11 '18 at 22:17
  • 1
    @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 Jun 12 '18 at 12:42
9

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.