92

I am intrigued by how the C++ exception handling mechanism works. Specifically, where is the exception object stored and how does it propagate through several scopes until it is caught? Is it stored in some global area?

Since this could be compiler specific could somebody explain this in the context of the g++ compiler suite?

Shog9
  • 156,901
  • 35
  • 231
  • 235
pjay
  • 2,422
  • 4
  • 21
  • 20
  • I don't know - but I'm guessing that the C++ spec has a clear definition. (I may be wrong though) – Paul Nathan Jan 29 '09 at 07:52
  • 2
    No, the spec doesn't give a definition. It dictates behavior, not implementation. Paul, you might want to specify which implementation you're interested in. – Rob Kennedy Jan 29 '09 at 07:55
  • 1
    Related question: http://stackoverflow.com/questions/307610/how-do-exceptions-work-behind-the-scenes-in-c – CesarB Jun 17 '09 at 19:10
  • 5
    Read this [Article](http://www.codeproject.com/KB/cpp/exceptionhandler.aspx) will help you – Ahmed Jan 29 '09 at 07:52
  • Refer to section 5.4 of [Technical Report on C++ Performance](http://www.open-std.org/jtc1/sc22/wg21/docs/TR18015.pdf) – Yogesh Arora Mar 25 '10 at 15:36

4 Answers4

58

Implementations may differ, but there are some basic ideas that follow from requirements.

The exception object itself is an object created in one function, destroyed in a caller thereof. Hence, it's typically not feasible to create the object on the stack. On the other hand, many exception objects are not very big. Ergo, one can create e.g a 32 byte buffer and overflow to heap if a bigger exception object is actually needed.

As for the actual transfer of control, two strategies exist. One is to record enough information in the stack itself to unwind the stack. This is basically a list of destructors to run and exception handlers that might catch the exception. When an exception happens, run back the stack executing those destructors until you find a matching catch.

The second strategy moves this information into tables outside the stack. Now, when an exception occurs, the call stack is used to find out which scopes are entered but not exited. Those are then looked up in the static tables to determine where the thrown exception will be handled, and which destructors run in between. This means there is less exception overhead on the stack; return addresses are needed anyway. The tables are extra data, but the compiler can put them in a demand-loaded segment of the program.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • 6
    AFAIR g++ uses the second, address-table approach, presumably for reasons of compatibility with C. Microsoft C++ compiler uses a combined approach, since its C++ exceptions are built on top of SEH (structured exception handling). In each C++ function, MSC++ creates and registers a SEH exception-handling record, which points to a table with address ranges for try-catch blocks and destructors in this particular function. throw packages a C++ exception as a SEH exception and calls RaiseException(), then SEH returns control to the C++-specific handler routine. – Anton Tykhyy Jun 14 '09 at 17:46
  • 1
    @Anton: yes, it uses the address-table approach. See my answer to another question at http://stackoverflow.com/questions/307610/how-do-exceptions-work-behind-the-scenes-in-c/307716#307716 for the details. – CesarB Jun 17 '09 at 19:11
  • Thanks for the answer. You can see how C purists could be terrified of C++ and its exceptions. The idea that a simple try/catch can unwittingly create a number of stack objects at runtime or bloat your program with extra tables is the reason why embedded systems often avoid them. – speedplane Jan 05 '16 at 05:07
  • 1
    @speedplane: No, that's more due to a lack of understanding. Error handling never is free. C just forces you to write it yourself. And we all know how many C programs are missing a `free()` or an `fclose()` in some rarely used code path. – MSalters Jan 05 '16 at 07:57
  • @MSalters I'm not disagreeing, it's almost entirely a lack of understanding. Engineers often don't understand how exceptions work and how exceptions will affect their code, which then, justifiably, leads to hesitation when using exceptions. If exception handling implementation was more clearly communicated (and didn't seem like magic), many would be less hesitant to use them. – speedplane Jan 05 '16 at 17:14
  • I guessed the stack approach but missed the table approach. Stack is natural but table can be more sophisticated. Either way, I think the essential part is, to record where/who to catch the exception. – smwikipedia May 14 '22 at 08:55
22

This is defined in 15.1 Throwing an exception of the standard.

The throw creates a temporary object.
How the memory for this temporary object is allocated is unspecified.

After creation of the temporary object control is passed to the closest handler in the call stack. unwinding the stack between throw and catch point. As the stack is unwind any stack variables are destroyed in reverse order of creation.

Unless the exception is re-thrown the temporary is destroyed at the end of the handler where it was caught.

Note: If you catch by reference the reference will refer to the temporary, If you catch by value the temporary object is copied into the value (and thus requires a copy constructor).

Advice from S.Meyers (Catch by const reference).

try
{
    // do stuff
}
catch(MyException const& x)
{
}
catch(std::exception const& x)
{
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • 3
    Something else that's unspecified is *how* the program unwinds the stack and *how* the program knows where the "closest handler" is. I'm pretty sure Borland holds a patent on one method of implementing that. – Rob Kennedy Jan 29 '09 at 16:00
  • As long as the objects are destroyed in reverse order of creation the implementation details are not important unless you are a compiler engineer. – Martin York Jan 30 '09 at 05:36
  • 2
    Voted down: a) "Scott Meyers", not "S. Myers"; b) untrue citation: "Effective C++": "Item 13: Catch exceptions by **reference.**". This will allow for tweaking/appending info to the exception object. – Sebastian Mach Jun 08 '11 at 11:25
  • 3
    @phresnel: Don't forget Item 21: "Use const whenever possible". There is no good case for tweaking an exception. You should be a) "fixing and discarding", b)re-throwing or c)generating a new exception. – Martin York Jun 08 '11 at 15:58
  • @Martin: That's not what my post was about. You quoted wrongly, both with original author type ("Myers") as well as the wrong statement that "Myers" advises on using `const&`, but he does not. Neither in Effective C++, nor in C++ Coding Standards (Item 73: "Throw by value, catch by reference"). Also, `const whenever possible` is too extreme and not what "Myers"/Meyers advised. If you intend to tweak a value, const puts unecessary burdens on the compiler. And the way to tweak exceptions (e.g. to add contextual information upon exception-propagation) is non-const/polymorhpic reference ... – Sebastian Mach Jun 08 '11 at 21:40
  • @phresnel : There is no reason to make an exception non const. As there is no good reason to tweak the exception. Adding contextual information to the exception may seem like a good idea but in reality adds little of use at the burden of complicating your exceptions. In reality you will create a new exceptions if more information is needed. There is no burden on the compiler (that's just a silly statement) did you mean burden on the developer. I stand by original statement. 1) Catch by reference. 2) Use const whenever possible. Means catch by const reference whenever possible. – Martin York Jun 09 '11 at 16:36
  • @Martin: Burden: indeed `const whenever possible` puts burden, on both the compiler AND the programmer. Don't forget that C++ is an imperative language with state and explicit flow-control. In some situations, mutating an existing variable is simply the right thing to do; let's just start with the common for-loop and the iterator. It is possible to make loops purely const, right, by doing them via recursion, but well; same with number crunching and huge arrays; expression templates to a lot, but if flow-control reaches a critical mass, it becomes an optimization burden to the compiler ... – Sebastian Mach Jun 10 '11 at 07:23
  • @Martin: ... I could construct endless real life examples; my advice: `const by default and where reasonable`, and I am aware that `reasonable` is relative. //// Exceptions) Well, I guess than this is personal taste. You have reasons, I (and Meyers, Alexandrescu and Sutter) have reasons, too. I just find it hard to justify to construct a completely new execption if it basically a slightly modified copy of what was caught. Imho, that's premature pessimization, as is `const whenever possible`. – Sebastian Mach Jun 10 '11 at 07:26
  • @Martin: Just as a side note, I've written a compile time ray tracer where indeed everything is const, so const that everything resides in enums and types. Perfectly possible, but for most programmers it's write only code (have a look at your copy of Effective STL about Read Only Code). So you see I know what I talk about. See http://gitorious.org/metatrace . – Sebastian Mach Jun 10 '11 at 07:34
  • 1
    @phresnel: Yes you have your reasons (don't agree with your logic), I have mine and though I will not claim to have talked to them about this *specific* subject or actually know their minds (Meyers, Alexandrescu and Sutter) I believe my interpretation stands valid. But if you are in the Seattle area then you can speak with all three as they are regular attendees at the [North West C++ User Group](http://www.nwcpp.org/) (Meyers less often than the others). – Martin York Jun 10 '11 at 20:18
  • Why do C++ programmers trust a mechanism for exceptions that the standard does not enforce implementation of? How can you be sure your exceptions behave in trustworthy ways if you cannot inspect the exceptions? Clearly the exceptions must be a runtime construct, and as such the exception objects must be inspectable at runtime, but is there any standard or API for inspecting the exceptions? If not; why don't we just use functions with error callbacks and default to(if no callback is given) not blowing up our program each time an exception is thrown? – Dmytro Nov 29 '16 at 23:22
  • `Why do C++ programmers trust a mechanism for exceptions that the standard does not enforce implementation of?` The C++ standard defines behaviors not implementation techniques. It is up-to the implementation to make sure it conforms to the standard. – Martin York Nov 29 '16 at 23:29
  • @Dmitry `How can you be sure your exceptions behave in trustworthy ways if you cannot inspect the exceptions?` You can. You catch the exception. – Martin York Nov 29 '16 at 23:29
  • @Dmitry `Clearly the exceptions must be a runtime construct, and as such the exception objects must be inspectable at runtime, but is there any standard or API for inspecting the exceptions?` You catch the exception. Then you can examine it. – Martin York Nov 29 '16 at 23:31
  • @Dmitry `why don't we just use functions with error callbacks and default to(if no callback is given) not blowing up our program each time an exception is thrown?` Your are basically asking why `C` code is so brittle. Exceptions were designed to stop people forgetting to handle exception and thus blowing up the code in undiagnosable ways. Now it blows up showing you what went wrong and forcing you to handle the error condition (or if you don't handle it terminating the application in a controlled manner so your data is not corrupted). – Martin York Nov 29 '16 at 23:34
  • @LokiAstari that is true in Java where there are checked exceptions that compiler forces you to catch(often in places where most of the time you silence them so it would make more sense to use callbacks instead in these cases), and if C++ exceptions showed the whole stack of the exception in the case that your program blew up because of an exception in an external library whose name you do not know nor function throwing it you do not know. Often telling you silly things like "Please contact the application's support team for more information.", how do I know which module to replace? – Dmytro Nov 29 '16 at 23:43
  • If you have not handled an error then the application **MUST** shutdown otherwise your application is left in an invalid state and is more than likely corrupting the data. Exception enforce this rule automatically; either the exception must be explicitly handled or the application shutdown automatically protecting your data. The problem with callbacks the way you describe is that the default action does nothing. This is basically leaving a time bomb in your code; an unhandeled error is broken code with no way to find the error. – Martin York Nov 30 '16 at 00:13
13

You could take a look here for a detailed explanation.

It may also help to take a look at a trick used in plain C to implement some basic sort of exception handling. This entails using setjmp() and longjmp() in the following manner: the former saves the stack in order to mark the exception handler (like "catch"), while the latter is used to "throw" a value. The "thrown" value is seen as if it has been returned from a called function. The "try block" ends when setjmp() is called again or when the function returns.

10

I know this is an old question, but there's a very good exposition, explaining both the methods used in each of gcc and VC here: http://www.hexblog.com/wp-content/uploads/2012/06/Recon-2012-Skochinsky-Compiler-Internals.pdf

Jules May
  • 753
  • 2
  • 10
  • 18