6

The C++ standard library defines std::runtime_error and similar to inherit from std::exception, but the inheritance is not virtual. This complicates the extension of the exception hierarchy. For example the following code has problems.

class sql_exception : public virtual std::exception {...};
class sql_disconnected : public virtual std::runtime_error, public virtual sql_exception {...};
void do_something() {
  throw sql_disconnected();
}
void call_something() {
  try {
     do_something();
  } catch (const std::exception& e) {
  }
}

std::exception is being not caught, as IMHO it should be. This can be worked around in various (needlessly complicated [IMHO]) ways.

I believe the standard should permit this behavior, and since it doesn't I have to assume there is a good reason, other than "this is the standard because it is the standard".

I am aware that there is some cost associated with virtual inheritance, but AFAIK it's negligible compared to the cost of stack unwinding and the rest of the exception handling.

Q1: For what technical reason did the standard library implement this behavior?

Q2: Was the problem of extending the hierarchy considered, and if so what does the standard say about the topic? Does the standard discourage it or have a recommendation to follow?

Martin
  • 911
  • 7
  • 21
  • 1
    From your example, it is unclear why you require virtual inheritance. – Jarod42 Nov 20 '16 at 17:04
  • @Jarod42 virtual inheritance is recommended/required for exception classes to solve the diamond inheritance pattern, to allow std::exception to be caught, because both std::runtime_error and sql_exception inherit from std::exception, and without virtual inheritance, it's not knowable which std::exception base is meant because there are two, where there should be one. – Martin Nov 20 '16 at 17:08
  • 2
    Why should `sql_disconnected` inherit from `std::runtime_error`? – Christian Hackl Nov 20 '16 at 17:09
  • How would you use this exception? you can't use std::exception's only function `what()` for `std::runtime_error`. – Shmuel H. Nov 20 '16 at 17:10
  • @ChristianHackl because it's a runtime error, and client code catching only runtime_error should catch it and not other exceptions, e.g. sql_invalid_statement, which would be a logic_error. And should be possible to catch those separately. That is after all the point of a hierarchy. – Martin Nov 20 '16 at 17:14
  • @Martin: The standard C++ exception hierarchy is just needlessly complicated, and often redundant, sometimes even contradictory. I have **never** seen a situation in which it made sense to catch `std::runtime_error` but not `std::exception`, really. Have you? – Christian Hackl Nov 20 '16 at 17:19
  • P.S.: http://stackoverflow.com/a/5874313/3313064 might be of interest to you. – Christian Hackl Nov 20 '16 at 17:21
  • @ChristianHackl I have seen such examples, but that's not relevant to my question. ... I'm aware of the recommendation from boost, and the document by Koenig, and that particular answer from SO, which is why I ask why the standard does something different from boost. My question is, why is that standard the way it is? – Martin Nov 20 '16 at 17:32
  • 1
    @Martin: I am sorry, but I cannot imagine any of your examples, and that makes it relevant for the question, because it is IMO meaningless to distinguish between `std::exception` and `std::runtime_error` (or ever use `std::logic_error`, which sould always be an assertion instead), so... just remove the multiple inheritance and your problem is solved. I guess the standard is just that way because the classes were standardised years before Koenig made that discovery. – Christian Hackl Nov 20 '16 at 17:36
  • 1
    @ChristianHackl Are you telling me to code this way because it's actually the right way, or because the library is broken and there's no better choice? .... Either way, I am still asking about the design of the standard C++ library. – Martin Nov 20 '16 at 18:07
  • Possible duplicate of [Why does std::logic\_error not virtually inherit from std::exception?](https://stackoverflow.com/questions/40601894/why-does-stdlogic-error-not-virtually-inherit-from-stdexception) – user673679 Jan 13 '19 at 11:22

1 Answers1

1

It does not make sense to me to derive sql_disconnected from std::runtime_error but not also derive sql_exception from the same. IMHO any SQL error should be treated as a runtime error. std::runtime_error derives from std::exception, so you don't need to create a diamond hierarchy in the first place, and thus you don't need to use virtual inheritance to solve that problem. None of the standard STL exceptions use virtual inheritance, neither should your custom exceptions.

class sql_exception : public std::runtime_error {...};
class sql_disconnected : public sql_exception {...};

void do_something() {
  throw sql_disconnected();
}

void call_something() {
  try {
     do_something();
  }
  catch (const std::exception& e) {
  }
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 5
    This isn't an answer to the question. At best your describing why the example accompanying the question isn't the best example possible. The question was about the C++ standard library, not about the example code used to illustrate it. – Giel Mar 14 '17 at 13:22