106

I've seen at least one reliable source (a C++ class I took) recommend that application-specific exception classes in C++ should inherit from std::exception. I'm not clear on the benefits of this approach.

In C# the reasons for inheriting from ApplicationException are clear: you get a handful of useful methods, properties and constructors and just have to add or override what you need. With std::exception it seems that all you get is a what() method to override, which you could just as well create yourself.

So what are the benefits, if any, of using std::exception as a base class for my application-specific exception class? Are there any good reasons not to inherit from std::exception?

John M Gant
  • 18,970
  • 18
  • 64
  • 82
  • You might want to have a look at this: http://stackoverflow.com/questions/1605778/1605852#1605852 – sbi Nov 03 '09 at 23:31
  • 2
    Though as a side note unrelated to the particular question, *C++* classes you take need not neccessarily be reliable sources on good practices just out of their own right. – Christian Rau May 28 '13 at 15:25

14 Answers14

72

The main benefit is that code using your classes doesn't have to know exact type of what you throw at it, but can just catch the std::exception.

Edit: as Martin and others noted, you actually want to derive from one of the sub-classes of std::exception declared in <stdexcept> header.

Nikolai Fetissov
  • 82,306
  • 11
  • 110
  • 171
  • 21
    There is no way to pass a message to std::exception. std::runtime_error accepts a string and is derived from std::exception. – Martin York Nov 03 '09 at 19:59
  • 15
    You shouldn't be passing a message to the exception type constructor (consider that messages need to be localized.) Instead, define an exception type that categorizes the error semantically, store in the exception object what you need to format a user-friendly message, then do so at the catch site. – Emil Jan 04 '11 at 01:33
  • `std::exception` is _defined_ in the `` header ([proof](http://en.cppreference.com/w/cpp/error/exception)). `-1` – Alexander Shukaev May 14 '16 at 11:13
  • 5
    So what's your point? Definition implies declaration, what do you see being wrong here? – Nikolai Fetissov May 14 '16 at 17:48
44

The problem with std::exception is that there is no constructor (in the standard compliant versions) that accepts a message.

As a result I prefer to derive from std::runtime_error. This is derived from std::exception but its constructors allow you to pass a C-String or a std::string to the constructor that will be returned (as a char const*) when what() is called.

Keith Pinson
  • 7,835
  • 7
  • 61
  • 104
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • 13
    It is not a good idea to format a user-friendly message at the point of the throw because this would couple lower-level code with with localization functionality and whatnot. Instead, store in the exception object all relevant information, and let the catch site format a user-friendly message based on the exception type and the data it carries. – Emil Jan 04 '11 at 01:26
  • 19
    @ Emil: Sure if you're exception carry user displayable messages, though generally they are for logging purposes only. At the throw site you don't have the context to build a user message (as this may be a library anyway). The catch site will have more context and can generate the appropriate msg. – Martin York Jan 04 '11 at 01:40
  • 3
    I don't know where you got the idea that exceptions are for logging purposes only. :) – Emil Sep 09 '13 at 03:17
  • @Emil: Not the exception. The text in the exception. – Martin York Sep 09 '13 at 19:12
  • 1
    There should be no text in the exception, only data that is needed to handle it. In the case when handling the exception involves notifying the user about the problem, the data in the exception needs to be sufficient to format a friendly message at the point of the catch. It is incorrect to format the message at the point of the throw because 1) there may not be enough information to do that, and 2) it might have to be translated to different languages. – Emil Sep 10 '13 at 20:36
  • 1
    @Emil: We have already gone through this argument. Where I pointed out that that is not what you are putting in the exception. You understanding of the subject seems good but your argument is flawed. Error messages generated for the user are completely different from log messages for the developer. – Martin York Sep 10 '13 at 21:29
  • They have nothing to do with each other. At the catch point you can make nice message but the information in the exception is usually not relevant because it is too technical (maybe a file name is useful or something very general like that). So error messages for the user are generated at the catch point and usually use very little from the exception as that information is too technical. – Martin York Sep 10 '13 at 21:32
  • But at the catch point any data is to technical to make an error message for the log with out generalization which would practically make it usless. So log messages need to be generated at the point of the exception so that we do not loose information. Of course there are exceptions and generalizations but its a good rule of thumb. – Martin York Sep 10 '13 at 21:34
  • The data that may be needed to format a *friendly* message are innumerable, but here are some examples: file name, user name, any other name, port number, ip address, url, required image dimensions, required image format, error code (to be converted to string at the catch site), time/date stamps, ... – Emil Sep 11 '13 at 22:39
  • @Emil: I already mentioned that. Does not change my point about logging. Log messages must be generated at the throw point to prevent loss of information. Thus **your** statement There should be no text in the exception is just wrong. – Martin York Sep 12 '13 at 05:32
  • I didn't mean text as in "string", but as in "message". The point I'm making is that many programmers are tempted to write `throw error("File not found")` (which is wrong) rather than `throw file_not_found_error()` (which is correct). And let's not confuse people with logging. Yes, you can log the fact that you throw or catch an exception, and yes some data the exception may transport is not for the user to see, but that's besides the point. – Emil Sep 13 '13 at 06:47
  • @Emil: I agree that you can't do formatting of error messages at the throw point. But **again** you are going off on another completely different side track (and again having nothing to do with the question). I can **NOT** agree with that statement in the last comment. All (the ones I have used) I18N packages use strings as the index to pull the actual message from a resource so either of those is fine. Bad really you need to start your own question and get more opinions. – Martin York Sep 14 '13 at 00:35
  • The implementation allows you to efficiently catch exceptions by their type, that's why you should use different types to indicate different failures, rather than using the same type (string) but different values. E.g. you can catch(file_open_error&) and let everything else pass through. – Emil Sep 02 '14 at 00:48
  • 1
    @Emil. This discussion is a year old. Create your own question at this point. Your arguments as far as I am concerned are just wrong (and based on votes people tend to agree with me). See [What is a 'good number' of exceptions to implement for my library?](http://programmers.stackexchange.com/a/118187/12917) and [Is catching general exceptions really a bad thing?](http://programmers.stackexchange.com/a/164269/12917) You have not provided any nw information since your previous diatribe. – Martin York Sep 02 '14 at 05:34
  • @Emil I would say throw `file_not_found_error("File not found")` is correct, because if code will catch it as generic exception you want to leave user with some human friendly popup. – mip Sep 01 '15 at 17:45
  • 1
    MSVC adds a constructor to `std::exception` that takes a `const char* const`, presumably to ease with implementing the classes in `` by offloading shared code to the shared base class, but relying on that breaks portability. Figured that might be relevant, if not very useful. – Justin Time - Reinstate Monica Jun 29 '16 at 20:43
  • std::exception has a virtual what(). Even if you only derive from std::exception, your exception type is expected to put something into (i.e., override) that what() to be generally useful. Why not simply derive from something like std::runtime_error? Then you have both a custom what() for somebody just catching std::exception **and** dedicated, structured error data to inspect if someone really wants to catch your particular error class. Best of both worlds. – plexando Jan 26 '21 at 08:25
18

Reason for inheriting from std::exception is it "standard" base class for exceptions, so it is natural for other people on a team, for example, to expect that and catch base std::exception.

If you are looking for convenience, you can inherit from std::runtime_error that provides std::string constructor.

Aleksei Potov
  • 1,548
  • 11
  • 14
  • 2
    Deriving from any other standard exception type except for std::exception is probably not a good idea. The problem is that they derive from std::exception non-virtually, which can lead to subtle bugs involving multiple inheritance where a catch(std::exception&) might silently fail to catch an exception due to the conversion to std::exception being ambiguous. – Emil Jan 04 '11 at 01:15
  • 2
    I read the article on boost on the subject. It seems reasonable in a pure logic sense. But I have never seen MH in exceptions in the wild. Nor would I consider using MH in exceptions so it seems like a sledgehammer to fix a non existent problem. If this was a real problem I am sure we would have seen action on it from the standards committee to fix such an obvious flaw. So my opinion is that std::runtime_error (and family) are still perfectly acceptable exception to throw or derive from. – Martin York Jan 04 '11 at 11:55
  • 6
    @Emil: Deriving from other standard exceptions besides `std::exception` is an _excellent_ idea. What you shouldn't do is inherit from _more_ than one. – Mooing Duck Sep 06 '13 at 23:14
  • @MooingDuck: If you derive from more than one standard exception type you'll end up with std::exception being derived (non-virtually) multiple times. See http://www.boost.org/doc/libs/release/libs/exception/doc/using_virtual_inheritance_in_exception_types.html. – Emil Sep 07 '13 at 23:54
  • 1
    @Emil: That follows from what I said. – Mooing Duck Sep 08 '13 at 00:35
14

I once participated in the clean up of a large codebase where the previous authors had thrown ints, HRESULTS, std::string, char*, random classes... different stuff everywhere; just name a type and it was probably thrown somewhere. And no common base class at all. Believe me, things were much tidier once we got to the point that all the thrown types had a common base we could catch and know nothing was going to get past. So please do yourself (and those who'll have to maintain your code in future) a favor and do it that way from the start.

timday
  • 24,582
  • 12
  • 83
  • 135
12

You should inherit from boost::exception. It provides a lot more features and well-understood ways to carry additional data... of course, if you're not using Boost, then ignore this suggestion.

James Schek
  • 17,844
  • 7
  • 51
  • 64
  • 6
    However note that boost::exception itself does not derive from std::exception. Even when deriving from boost::exception, you should also derive from std::exception (as a separate note, whenever deriving from exception types, you should use virtual inheritance.) – Emil Jan 04 '11 at 01:04
11

Yes you should derive from std::exception.

Others have answered that std::exception has the problem that you can't pass a text message to it, however it is generally not a good idea to attempt to format a user message at the point of the throw. Instead, use the exception object to transport all relevant information to the catch site which can then format a user-friendly message.

cbuchart
  • 10,847
  • 9
  • 53
  • 93
Emil
  • 363
  • 3
  • 13
  • 6
    +1, good point. Both from a separation of concerns and an i18n perspective, it's definitely better to let the presentation layer construct the user message. – John M Gant Feb 26 '10 at 13:55
  • @JohnMGant and Emil: Interesting, can you point to a concrete example on how this can be done. I understand that one can derive from `std::exception` and carry the information of the exception. But who will be responsible for building/format the error message? The `::what()` function or something else? – alfC Jun 25 '14 at 20:42
  • 1
    You'd format the message at the catch site, most likely at the application (rather than library) level. You catch it by type, then probe it for information, then localize/format the appropriate user message. – Emil Sep 02 '14 at 00:44
9

The reason why you might want to inherit from std::exception is because it allows you to throw an exception that is caught according to that class, ie:

class myException : public std::exception { ... };
try {
    ...
    throw myException();
}
catch (std::exception &theException) {
    ...
}
SingleNegationElimination
  • 151,563
  • 33
  • 264
  • 304
7

There is one problem with inheritance that you should know about is object slicing. When you write throw e; a throw-expression initializes a temporary object, called the exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw. That could be not what you're expecting. Example of problem you could find here.

It is not an argument against inheritance, it is just 'must know' info.

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
  • 4
    I think the take away is that "throw e;" is evil, and "throw;" is ok. – James Schek Nov 03 '09 at 19:34
  • 2
    Yes, `throw;` is ok, but it is not obvious that you should write something like this. – Kirill V. Lyadvinsky Nov 03 '09 at 19:39
  • 1
    It's especially painful for Java developers where rethrow is done using "throw e;" – James Schek Nov 03 '09 at 22:22
  • Consider only deriving from abstract base types. Catching an abstract base type by value will give you an error (avoiding slicing); catching an abstract base type by reference then trying to throw a copy of it will give you an error (again avoiding slicing.) – Emil Jan 04 '11 at 01:09
  • You can also solve this problem by adding a member function, `raise()`, as such: `virtual void raise() { throw *this; }`. Just remember to override it in each derived exception. – Justin Time - Reinstate Monica Jun 29 '16 at 20:47
5

Difference: std::runtime_error vs std::exception()

Whether you should inherit from it or not is up to you. Standard std::exception and its standard descendants propose one possible exception hierarchy structure (division into logic_error subhierarchy and runtime_error subhierarchy) and one possible exception object interface. If you like it - use it. If for some reason you need something different - define your own exception framework.

Community
  • 1
  • 1
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
4

Whether to derive from any standard exception type or not is the first question. Doing so enables a single exception handler for all standard library exceptions and your own, but it also encourages such catch-them-all handlers. The problem is that one should only catch exceptions one knows how to handle. In main(), for example, catching all std::exceptions is likely a good thing if the what() string will be logged as a last resort before exiting. Elsewhere, however, it's unlikely to be a good idea.

Once you've decided whether to derive from a standard exception type or not, then the question is which should be the base. If your application doesn't need i18n, you might think that formatting a message at the call site is every bit as good as saving information and generating the message at the call site. The problem is that the formatted message may not be needed. Better to use a lazy message generation scheme -- perhaps with preallocated memory. Then, if the message is needed, it will be generated on access (and, possibly, cached in the exception object). Thus, if the message is generated when thrown, then a std::exception derivate, like std::runtime_error is needed as the base class. If the message is generated lazily, then std::exception is the appropriate base.

Rob
  • 41
  • 1
3

If all your possible exceptions derive from std::exception, your catch block can simply catch(std::exception & e) and be assured of capturing everything.

Once you've captured the exception, you can use that what method to get more information. C++ doesn't support duck-typing, so another class with a what method would require a different catch and different code to use it.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 7
    don't subclass std::exception and then catch (std::exception e). You need to catch a reference to std::exception& (or a pointer) or else you are slicing off the subclass data. – jmucchiello Nov 03 '09 at 19:32
  • 1
    Now that was a dumb mistake - certainly I know better. http://stackoverflow.com/questions/1095225/exception-slicing-is-this-due-to-generated-copy-constructor/1095233#1095233 – Mark Ransom Nov 03 '09 at 20:52
  • I think it's even better to catch by *const* reference. (I know the answer is old, but this also applied in 2009.) – Wolf Jun 08 '23 at 14:00
3

Since the language already throws std::exception, you need to catch it anyway to provide decent error reporting. You may as well use that same catch for all unexpected exceptions of your own. Also, almost any library that throws exceptions would derive them from std::exception.

In other words, its either

catch (...) {cout << "Unknown exception"; }

or

catch (const std::exception &e) { cout << "unexpected exception " << e.what();}

And the second option is definitely better.

3

Though this question is rather old and has already been answered plenty, I just want to add a note on how to do proper exception handling in C++11, since I am continually missing this in discussions about exceptions:

Use std::nested_exception and std::throw_with_nested

It is described on StackOverflow here and here, how you can get a backtrace on your exceptions inside your code without need for a debugger or cumbersome logging, by simply writing a proper exception handler which will rethrow nested exceptions.

Since you can do this with any derived exception class, you can add a lot of information to such a backtrace! You may also take a look at my MWE on GitHub, where a backtrace would look something like this:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

You don't even need to subclass std::runtime_error in order to get plenty of information when an exception is thrown.

The only benefit I see in subclassing (instead of just using std::runtime_error) is that your exception handler can catch your custom exception and do something special. For example:

try
{
  // something that may throw
}
catch( const MyException & ex )
{
  // do something specialized with the
  // additional info inside MyException
}
catch( const std::exception & ex )
{
  std::cerr << ex.what() << std::endl;
}
catch( ... )
{
  std::cerr << "unknown exception!" << std::endl;
}
GPMueller
  • 2,881
  • 2
  • 27
  • 36
0

Another reason to sub-class exceptions is a better design aspect when working on large encapsulated systems. You can reuse it for things such as validation messages, user queries, fatal controller errors and so on. Rather than rewriting or rehooking all of your validation like messages you can simply "catch" it on the main source file, but throw the error anywhere in your entire set of classes.

e.g. A fatal exception will terminate the program, a validation error will only clear the stack and a user query will ask the end-user a question.

Doing it this way also means you can reuse the same classes but on different interfaces. e.g. A windows application can use message box, a web service will show html and reporting system will log it and so on.