1. Introduction

Exceptions have been a part of C++ since early 1990s and are sanctioned by the standard to be the mechanism for writing fault-tolerant code in this language. However, many developers for various reasons choose not to use exceptions, and voices that are skeptical of this language feature are still numerous and loud: Raymond Chen's article Cleaner, more elegant, and wrong, Joel Spolsky's blog Exceptions, and Google C++ Style Guide are some of the frequently quoted texts that advise against the use of exceptions.

Rather than taking a side in this debate, I am trying to present a balanced view of pros and cons of using exceptions. The purpose of this article is not to convince readers to use exceptions or error codes, but help them make an informed decision that is best for their particular project. I have structured the article as a list of six arguments for using exceptions and six arguments against it that can often be heard in the C++ community.

2. Arguments for using Exceptions

2.1 Exceptions separate error-handling code from the normal program flow and thus make the code more readable, robust, and extensible.

To illustrate the point, we'll compare the usage of two simple socket libraries that differ only in the error handling mechanism. Here is how we could use them to fetch HTML from a web site:

In both cases, the code does the same thing, error handling is delegated to the caller, resource cleanup is performed by destructors. The only difference is that in the former case, the socket library throws exceptions in case of failure, and in the latter case, error codes are used.

It is easy to notice that the sample with exceptions has cleaner and simpler flow, without a single if branch to disrupt it. Error handling is completely hidden and only the "normal" code flow is visible. The infrastructure for propagating exceptions is generated by the compiler: in case of an exception, the stack will "unwind" correctly, meaning that local variables in all stack frames will be destroyed properly - including running the destructors.

In fact, sample 2 as shown here is probably even simpler and cleaner than it would be in most cases: we are using only one library within the function, and return that library's error code. In practice, we would probably have to take into account different error code types that we would hit within the function, and then we would need to map all these error codes to the type we are returning from the function.

What about robustness? When exceptions are used, it is the compiler that produces the code for the "error path" and the programmer does not do it manually, meaning there are less opportunities for introducing bugs. That is especially important when the code is changed - it is very easy to forget updating the error-handling part when a change is introduced to the normal code path, or introduce a bug when doing so.

2.2 Throwing an exception is the only clean way to report an error from a constructor.

The purpose of a constructor is to establish the class invariant. To do that, it often needs to acquire system resources or in general perform an operation that may fail. If a constructor fails to establish the invariant, the object is in invalid state and the caller must be notified. Clearly, error codes cannot be used for that purpose because a constructor is not allowed to return a value. However, an exception can be thrown from a constructor in case of failure, and by doing this, we prevent creation of an invalid object.

What are some alternatives to throwing an exception from a failing constructor? One of the most popular is the "two-stage construction" idiom that is used in our sample 2. The process of constructing an object is split into two steps: the constructor performs only the part of initialization that cannot fail (i.e., setting primitive type members), and the part that can fail goes into a separate function that usually has a name such as init() or create() and returns an error code. Establishing the class invariant in this case involves not only constructing the object, but also calling this other function and checking the returned error code. The downsides of this approach are pretty obvious: it takes more work to correctly initialize an object and it is easy to end up with an object in invalid state without knowing it. Furthermore, copy construction cannot be implemented this way - there is no way to tell the compiler to insert the second step and check for the error code. Having said that, this idiom is pretty effectively used in many libraries, and with some discipline, it may be successful.

Another alternative to throwing an exception from a constructor is maintaining a "bad state flag" as a member variable, setting that flag in the constructor, and exposing a function to check the flag. The standard IO streams use this approach:

// sample 3: Use of the bad state flag
ifstream fs("somefile.txt");
if (fs.bad())
return err_could_not_open;

This technique is similar to the two-stage construction idiom. It requires an additional data member - the state flag which can be a prohibitive expense in some scenarios. On the other hand, copy construction can be implemented this way, although it is far from being safe - for instance, standard containers do a lot of copying internally and there is no way of making them check for the state flag. However, just as with the two-stage construction, this approach can work as long as we know what we are doing.

Other alternatives include setting some global value such as errno in the constructor and hoping that the caller would remember to check it. This approach is clearly inferior to the previous ones and we won't discuss it here further.

2.3 Exceptions are hard to ignore, unlike error codes.

To illustrate this argument, all we have to do is remove error checking from sample 2 - it will compile just fine and as long as there are no run time errors, it will work fine as well. However, imagine there was an error in the call to init(): the object client will be in invalid state and when its member functions are invoked, pretty much anything can happen depending on the internal implementation of the Socket class, Operating System, etc.; the program may crash immediately or it can even execute all the functions but do nothing and return without any sign that something went wrong - except for the result. On the other hand, if an exception was thrown from the constructor, the invalid object would have never even been constructed and the execution would continue at the exception handler. The usual phrase is: "we would get an exception right in our face".

But is it really that hard to ignore an exception? Let's go up the stack and see the caller of get_html():

This horrible piece of code is known as "swallowing exceptions" and the effect is not much different than ignoring error codes. It does take more work to swallow exceptions than to ignore error codes and these constructs are easier to detect during code reviews, but the fact is that exceptions are still pretty easy to ignore, and people do it.

However, even if they are easy to ignore, exceptions are easier to detect than error codes. On many platforms, it is possible to break when an exception is thrown if the process is run from a debugger. For instance, GNU gdb supports "catchpoints" for that purpose, and Windows debuggers support the option "break if an exception is thrown". It is much harder, if not impossible, to get a similar functionality with error codes.

2.4 Exceptions are easily propagated from deeply nested functions.

We are often not able to deal with an error at the point it is originally detected. We need to propagate the information about the error to the level where we can handle it, and exceptions enable jumping directly to the handler without writing all the plumbing manually.

Back at our sample 1 with the Socket class. Assume get_html() is invoked by a function get_title() that gets called by a UI event handler. Something like:

In the sample above, get_title() does not contain any code for propagating the error information from get_html() to on_button(). If we used error codes instead of exceptions, get_title() would need to check for the return value after calling get_html(), map that value to its own error code, and pass the new error code back to on_button(). In other words, using exceptions would make get_title() look something like our sample 1, and error codes would turn it into something similar to sample 2. Something like:

Just as with sample 2, sample 7 can easily get more complicated if we try propagating more specific error codes. In that case, our if branches would need to map error codes from the libraries to appropriate Get_Title_Err values. Also, note that we show only a part of the function nesting in this sample - it is not hard to imagine the work needed to propagate an error code from deep within the parser code to our get_title function.

2.5 Exceptions can be, and often are, user defined types that carry much more information than an error code.

An error code is usually an integer type and can carry only so much information about the error. Microsoft's HRESULT type is actually a pretty impressive attempt to pack as much information in a 32-bit integer as possible, but it clearly shows the limits of that approach. Of course, it is possible to have an error code that is a full-blown object, but the cost of copying such an object multiple times until it reaches the error handler makes this technique not desirable.

On the other hand, exceptions usually are objects and can carry a lot of useful information about the error. It is a pretty common practice for an exception to carry information about the source file and line that it was thrown at (using macros such as _FILE_ and _LINE_) and they can even automatically send messages to a logging system.

Why is it expensive to use objects for return error codes and not for exceptions? There are two reasons: first, an exception object is created only if an error actually happens, and that should be an exceptional event - pun intended. The error code needs to be created even if the operation succeeds. Another reason is that an exception is usually propagated by reference to the handler and there is no need to copy the exception object.

2.6 Exception objects are matched to the handlers by using the type system.

Let's expand a little on our sample 6 and display a specific error message in case the error happened when establishing the socket connection:

Sample 8 demonstrates the use of the type system to classify exceptions. The handlers go from most specific ones to the most general one, and this is expressed with the language mechanism that serves the purpose: inheritance. In our sample, Socket::SocketConnectionException is derived from Socket::Exception.

If we were using error codes instead of exceptions, the error handler would probably be a switch statement and each individual case would handle a value for the error code. default would most probably correspond to catch(...). It is doable, but not quite the same. In sample 8, we use a handler for Socket::Exception to handle all exceptions from the socket library except SocketConnectionException that we handle just above it; with error codes, we would need to list all these other error codes explicitly. If we forget one, or maybe add a new error code later but forget to update it... you get the picture.

3. Arguments against using Exceptions

That sounds exactly the opposite of what we talked about in 2.1. How is it possible that exceptions simultaneously make the code hard to read and easy to read? To understand this point of view, remember that by using exceptions we managed to display only the "normal flow". The error-handling code still gets generated by the compiler for us and it has its own flow which is orthogonal to the flow of the code we wrote. In effect, the normal code flow is cleaner and easier to read and understand with exceptions, but that is only a part of the story. Now we need to be aware that at any function call, we introduce an invisible branch that, in case of an exception, immediately exists (or jumps to the handler if it is in the same function). That sounds worse than goto and comparable to longjmp, but in fairness, C++ exceptions are much safer to use than longjmp: as we saw in section 2.1, the compiler generates the code that unwinds the stack.

Still, the problem with readability is not necessarily bogus: if we are taking a quick look at a piece of code to learn what it does, the normal flow is really all we need, but what if we need to get really knowledgeable about it? For instance, imagine you need to do a code review or extend a function - would you rather have all possible code paths right in front of you, or having to guess which function can throw an exception and under what circumstances? Granted, it is much less of a concern if you make sure that exceptions are used only for error handling, and that the code was originally written with exception-safety in mind.

3.2 Exceptions easily lead to resource leaks, especially in a language that has no built-in garbage collector and finally blocks.

To understand what we are talking about here, let's for a moment forget object oriented programming with its classes, destructors, and exceptions, and fall back to good old C. Many functions take the following form:

Basically, we acquire some resources at the beginning of the function, use them to do some processing, and then release them. The resources in question can come from the system (memory, file handles, sockets...), or external libraries. In any case, they are limited and need to be released, or may be exhausted at some point and that's what we call a "leak".

So where is the problem? Our function from sample 9 is an example of well-structured code with a single entry and a single exit, and we will always clean up the resources at the end of the function. Imagine now that something can go wrong at use_resources_1(). In that case, we don't want to execute the rest of the use_resources_... functions, but report an error and exit immediately. Well, not immediately - we need to clean up the resources first. How do we do it best with a language like C? Let's just say the discussions on that topic involve even more passion than our little "exceptions vs. error codes" dilemma: some people copy and paste the clean-up code and call it whenever they exit; other ones make macros for that purpose when possible; there are developers who preserve the "SESE" structure of the function and introduce if-else branches for each function that can fail. Such functions look like a closing angle bracket ">" and some call it "arrow anti-pattern". Many C developers use goto to jump to the cleanup part of the function.

One way or another, it is a mess even without exceptions. What if we switch to C++ and use_resources_1() to throw an exception in case of error? The way the function is written, the cleanup part will not be executed and we will have a leak. How would a garbage collector and finally blocks help here? Pretend for a moment that Java does not have networking support in the standard library and that we use something similar to our sample 1 but from Java:

In the getHtml method above, we have acquired some resources: a socket, a string buffer, and a couple of temporary string objects. The garbage collector takes care of the objects that ultimately need to release only memory to the system, and the socket is closed in the finally block which is executed when we leave the try block - whether we do it "normally" or via an exception.

C++ does not have a built-in garbage collector, nor does it support finally blocks; how come our function from sample 1 (or sample 2 for that matter) does not leak? The answer is usually called RAII (resource acquisition is initialization), and it really means that objects declared as local variables get destroyed after they go out of scope, regardless of the way they exit that scope. When they get destroyed, the destructors are executed first, and then the memory is returned to the system. It is the burden of the destructors to release all resources acquired by the objects - in fact the destructor of the Socket class used in sample 1 probably looks very much like the body of the finally block in sample 10. The best part is that in C++, we need to write this clean-up code only once and it is executed automatically each time an object goes out of scope. Another nice aspect of RAII is that it treats all resources in a uniform way - there is no need to distinguish between "GC-collectible" resources and "unmanaged" ones and leave the former to GC and clean up the latter in finally blocks: the cleanup is invisible, yet it reliably happens, much like error reporting with exceptions.

Am I saying here that the lack of a garbage collector and finally blocks is not really a reason not to use exceptions in C++ because RAII is a superior method of managing resources? Yes and no. RAII is indeed superior to the combination of a garbage collector and finally blocks, and it is a very straightforward idiom and easy to use. Yet, in order to get the benefits of RAII, it needs to be used consistently. There is a surprising amount of C++ code out there that looks like:

In sample 11, RAII is used for all resources except the socket - yet it is a disaster waiting to happen: if an exception is thrown anywhere in the function, we leak a socket. If someone unplugs a network cable, our program may crash very quickly. Sure, it is possible to wrap the function in a try-catch block and then close the socket both in the catch block and after it - thus simulating the non-existing finally construct, but it is exactly the kind of repetitive and tedious task that sooner or later is forgotten and we have a serious bug.

But how common this kind of code is anyway? Creating objects on stack, when possible, is not only safer but also easier than pairing new and delete - one would expect that most programmers use RAII all the time. Unfortunately, this is not really the case. There is a lot of code out there that looks like sample 11. For instance, find the official sample code for the SAX2 parser of the popular Xerces-C library - it creates objects on the heap and deletes them after they are used - in case of an exception, there is a leak. In the early 1990s, it was considered "more object oriented" to create an object on the heap even if there were no reasons to do so. Now, many young C++ programmers learn Java first at universities and write C++ code in a Java-like manner rather than idiomatic C++. One way or another, there is a lot of existing code that does not rely on deterministic destructors to automatically release resources. If you are dealing with a code base like that, the only sane thing to do is use error codes and turn the exceptions off.

Note that preventing resource leaks is only a part of the exception safety puzzle. Sometimes a function needs to be transactional: it needs either to succeed or leave the state unchanged. In this scenario, we need to roll back the operation in case of failure before leaving the function. Unsurprisingly, we again turn to destructors to trigger such an operation if an exception is thrown. This idiom is called "Scope Guard" and rather than discussing it here, I suggest reading the original article by Andrei Alexandrescu and Petru Marginean.

3.3 Learning to write Exception safe code is hard.

The usual counter argument to this statement is that error handling in general is hard, and exceptions make it easier, not harder. While there is a lot of truth in that, the issue of the learning curve remains: complex language features often do help experienced programmers simplify their code, but the beginners mostly see the added pain of learning. To use exceptions in production code, a C++ programmer not only has to learn the language mechanism, but also the best practices of programming with exceptions. How soon a beginner learns to never throw from a destructor? Or use exceptions for exceptional conditions only and never for the normal code flow? Or catch by reference, and not by value or a pointer? How about usefulness (or the lack or thereof) of exception specifications? Exception safety guarantees? Dark corners, such as polymorphic throwing?

From a "glass half full" point of view, with exceptions or without them C++ has always been an expert-friendly language that offers very little hand-holding to beginners. Rather than relying on the constraints of the language, a good C++ programmer turns to the community to teach him the best practices of using the language. The guidelines from the community change faster than the language itself: someone who switched from C++ to Java in 1997 often gets very confused when presented with modern C++ code - it looks different, yet the language itself has not changed much. Back to the exceptions, the C++ community learned a lot on how to effectively use them since they were first introduced to the language; someone who follows new publications and newsgroups should pick up the best practices with some effort. A good coding standard that follows the community's best practices helps a lot with the learning curve.

3.4 Exceptions are expensive and break the promise to pay only for what we use.

When talking about the performance impact of a language feature or a library, usually the best advice to a developer is to measure and decide for himself or herself whether there is any performance impact and if it is acceptable in their scenarios. Unfortunately, sometimes it gets pretty hard to follow this advice when it comes to exceptions, mostly because the performance of an exception handling mechanism is very implementation dependent; therefore, the conclusions made based on measurements with one compiler may be invalid if the code is ported to another compiler, or even another version of the same compiler. At least, the good news is that compilers are getting better in producing efficient code in the presence of exceptions and it is likely that if exceptions are not impacting the performance of your code now, they will do it even less in the future.

To understand the performance impact of exception handling in general, I strongly advise reading Chapter 5.4 of Technical Report on C++ Performance (draft). It discusses in some detail the sources of performance overhead: the code added to try-catch blocks by the compiler, impact on the regular functions, and the cost of actually throwing an exception. It also compares the two most common implementation approaches: the "code" approach where the code is added to each try-catch block, and the "table" approach where the compiler generates static tables.

After getting familiar with the performance impact scenarios of exceptions in general, the best thing to do is research the topic for your particular platform and compiler. A very nice and relatively recent presentation given by Microsoft's Kevin Frei can be found at the Northwest C++ Users Group and it covers the Visual C++ compiler for 32 and 64 bit Windows platform. Another very interesting text related to the GNU C++ compiler on Itanium is Itanium C++ ABI: Exception Handling.

Before concluding the discussion on performance and exceptions, it would be worthwhile to add that current C++ implementations do not offer predictability guarantees for exceptions, which make them unsuitable for hard real-time systems.

3.5 Exceptions are hard to introduce to legacy code.

In other words, exception safety must never be an afterthought. If the code is written without exceptions in mind, it is much better to keep exceptions out of it or completely rewrite it - there are just too many ways things can go wrong. In this aspect, it reminds me of making a code thread safe if it was originally written without thread safety in mind - just don't do it! Not only is it hard to review the code for potential problems, but more importantly, it is hard to test. How are you going to trigger all the possible scenarios that lead to exceptions being thrown?

On the other hand, if the code base is modular, it is quite possible to write new code with exceptions that would peacefully coexist with the old code. The important thing is to draw the boundaries and not let exceptions cross them.

3.6 Exceptions are easily abused for performing tasks that belong to normal program flow.

Nice aspects of using exceptions, especially the fact that they can be easily propagated from deeply nested code structures, make them tempting to be used for tasks other than error handling - most likely for some kind of poor man's continuations.

Almost always, using exceptions to affect the "normal" flow is a bad idea. As we already discussed in section 3.1, exceptions generate invisible code paths. These code paths are arguably acceptable if they get executed only in the error handling scenarios. However, if we use exceptions for any other purpose, our "normal" code execution is divided into a visible and invisible part and it makes code very hard to read, understand, and extend.

Even if we accept that exceptions are meant to be used for error handling only, sometimes it is not obvious if a code branch represents an error handling scenario or normal flow. For instance, if std::map::find() results in a value not found, is it an error or normal flow? Sometimes, an answer is easier to find if we think in terms of "exceptional", rather than "error" state. In our example, is it an exceptional case that a value is not found in a map? In general, the answer is probably "no" - there are many "normal" scenarios in which we are checking whether a value is stored in a map and branch based on the result - none of these branches is an error state. That's why map::find does not throw in case there is no requested key in the map.

How about a functionality that depends on users' input? If a user enters invalid data, is it better to throw or not? Users' input is better considered invalid unless proven otherwise and, if possible, there should be some function that validates the input before it is processed. For these validation functions, invalid data is an expected outcome, and there is no reason for it to trigger an exception. The problem is, it is often impossible to validate the input before actually processing it (parsers, for instance); in such cases, using exceptions to break the processing and report an error is often a good approach. HtmlParser from our sample 5 throws if it cannot parse broken HTML.

My personal opinion is that an exception should never be thrown if a program is working in normal conditions and with valid data. In other words, when you test a program by running it from a debugger, be sure to set a "catchpoint" that triggers whenever an exception is thrown. If the program execution breaks because an exception is thrown, that signals either a bug that should be taken care of, or a misuse of an exception handling mechanism and that should be fixed as well. Of course, an exception may be triggered due to abnormal conditions of the environment (for instance, network connection suddenly gets lost, or a font handle allocation fails) or invalid input data and in that case the exception is thrown for a good reason.

4. Conclusion

There is no simple answer to the "exceptions or error codes" question. The decision needs to be made based on a specific situation that a development team faces. Some rough guidelines may be:

If you have a good development process and code standards that are actually being followed, if you are writing modern-style C++ code that relies on RAII to clean up resources for you, if your code base is modular, using exceptions may be a good idea.

If you are working with code that was not written with exception safety in mind, if you feel there is a lack of discipline in your development team, or if you are developing hard real-time systems, you should probably not use exceptions.

You're wrong about exceptions being expensive. I you examine the machine code generated you'll find that in fact there is negligible overhead incurred when no exception is thrown.
I think this myth exists because a very old version of MS compiler used to implemented exceptions badly, but any modern compiler will do a good job.

Having exceptions enabled alone (even if they are never thrown) introduces some performance ovrehead with most compilers; actually throwing an exception is a pretty expensive operation and also unpredictable from performance perspective which is why exceptions are prohibitted by the JSF C++ Standard[^]

Sorry, but I call bullshit.
In case of GCC the performance hit from exceptions is actually zero, because it has zero overhead exceptions enabled by default. I don't know about other compilors, but I doubt it's much in real world terms.

Also, the performance of when the exception is thrown is irrelevent, since it only happens when something goes wrong.

The document you link to gives no specific measurements.
To measure the overhead the only accuate way to do it is examine the assembler generated. Making time measurements is too inaccurate.

Sorry for the thread necromancy. But I just wanted to comment of a tendency of superstitious programming:

Instead of relying on faith (exceptions are free, exceptions are expensive). How about simply looking at the resulting assembly code. If exceptions are "free" then the resulting assembly code will be as fast if not faster than the one done by checking the code. If however the resulting code is not as fast, then the "exceptions are free" argument is a fallacy.

Is not important if a mythological compiler in the future may or not make things faster. What is important is the present, as its in the near present that you will ship. Is trivially easy to get proof by reading the resulting assembly code. No need to assume of one or another based on faith. Please keep theories for universities.

Contrary to compiler vendors and superstitious programmers, the resulting assembly code never lies. Where performance is a key factor. Rely on actual proof, not superstition.

I once read an analysis report of assembly code of a version of gcc, and indeed, it found that the overhead of execptions to be negligible. However, I am not an assembly code programmer, neither have I the time, so can't provide an analysis of other compilors. If you want to do it, by all means go ahead

Throwing an exception in a class constructor isn't a good idea as the destructor won't be called and this could lead to resource leaks.

Throwing an exception across module (dll) boundaries should be avoided as if an object is thrown it could have dependencies at the module level (memory layout/internal resources) leading to undefined behaviour (crashes).

Yoу are generally right about throwing accross module boundaries - this area is pretty grey, uncovered by the Standard and platform-specific which is why I chose not to mention it. In practice it is also not advisable to allocate/deallocate memory across dlls, etc.

As for the constructors, you are not right. The exceptions are meant to be thrown from constructors in case of failure. From Bjarne Stroustrup's technical FAQ[^]: You should throw an exception from a constructor whenever you cannot properly initialize (construct) an object. There is no really satisfactory alternative to exiting a constructor by a throw. The resource leaks in constructors need to be handled with RAII, just as with any other function.

The subtlety of my point is that some people might not realise that they have to tidy resources if the constructor throws an exception (assuming the destructor would tidy things up). Here's a simple example:

I understand your point, but again it is not really specific to constructors. If you call something like AllocateResource() without wrapping it into some kind of smart handler you are likely to have a leak - constructor or not. The sample you give should look like this to be exception-safe:

I have spent more than 4 hours today researching this topic on the net to determine what method(s) of error handling to use for the 100K line project that I have recently started. Your article is very informative and is the best one I have found.
Thanks again.

1. I like programming-by-contract paradigm - encapsulation of behavior based on pre/post conditions & invariants simply cannot be done effectively without exception system. Error codes work almost against the encapsulation, since single number is rarely enough to describe object state.

2. In my experience, writing test code (as in TDD) is by far easier when used in conjunction with exception system. As you mentioned, constructors are prime examples. The code is much simpler, and it's easy to recognize the intention and verification. Also, exceptions lower the degree of coupling between component and test code - there are no shared source files, just binary compatibility.

The points you mention are very valid. They are specific to concrete development practices (DbC, TDD) that are not being universaly used, so I am not sure they belong to a general article like this one.

"Opinions are neither right nor wrong. I cannot change your opinion. I can, however, change what influences your opinion." - David CrowNever mind - my own stupidity is the source of every "problem" - Mixture

Congratiolations for well written article. Last years I use mainly .NET with C# but still remember incredible learning curve when move from C to C++ in the 90s, and (from my point of view) biggest C++ advantage was bugfree and stable way to write code. This was imposible without exceptions, strict control to objects lifetime and const methods and pointer modifiers. Ability to stop in debugger on each exception thrown was my best friend, and this lead us to rule that exceptions shouldn't be used to return information - it's very sad that this rule is not implemented by microsoft in .net and exception break in debugger is turnet off by default in VS (beacuse you get lot of internal exceptions when start debugging). Also with using of nested objects and local stack instances - stack unwinding on throwing exception is powerfull nearly as modern garbage collectors but with better object lifetime control. This article brings me 10-15 years back in one better time. Take 5 from me.

Ability to stop in debugger on each exception thrown was my best friend, and this lead us to rule that exceptions shouldn't be used to return information

Funny enough I discovered the usefulness of this approach relatively recently (a couple of years ago). But I agree, it is a great feature, supported on Windows (through debugging API) and GNU gdb (catchpoints).

Clear and well written, and a very good attempt at balance though from my reading it does belong in the 'exceptions are good' camp. Almost all the negatives can be mitigated (or all of them if you're willing to switch languages )

though from my reading it does belong in the 'exceptions are good' camp.

My personal opinion is that exceptions are good when used properly and in right circumstances.

CurtainDog wrote:

Almost all the negatives can be mitigated (or all of them if you're willing to switch languages )

I am not sure what you mean here. Exceptions do create hidden code paths and can be easily abused regardles of the language. As for the leaks, I find them harder to avoid with languages that don't support RAII (Java) or require more effort to support it (C#, Python). GC helps with memory, but not with other resources.

Good for you! It is the same for the product I am currently working on. I also worked on a couple of projects where exceptions were not used, and one where they were abused; it is actually only the last one that was really bad

I'm mostly a VB rather than C++ programmer (though I do a tiny bit of C++, it's generally for integration with, or simulation of, straight C code that will be running on an embedded system), but it seems to me that in many cases the 'right' approach for functions where throwing an exception may or may not be the best course of action would be to support a callback function which could indicate what's supposed to happen. For something like a parser, the callback could look at the object being parsed along with its context and then decide whether to throw an exception, specify a return value, etc. Such an approach could be useful when trying to parse as much data as possible from files which may be slightly corrupted. Having a parser routine return gibberish without indicating that something's wrong could be disastrous, but throwing an exception for any problem will prevent the data from being parsed at all.

Have you seen such approaches used much? I would think that a really good implementation (ensuring the callback function would have all the information it might possibly need) might be tough, but it would seem like even in fairly simple cases using a callback could have advantages over simply throwing an exception. If nothing else, the callback could throw an exception which contained information about the context (e.g. it could include information about the record which was being parsed without having to use nested exceptions).

Have you seen such approaches used much? I would think that a really good implementation (ensuring the callback function would have all the information it might possibly need) might be tough, but it would seem like even in fairly simple cases using a callback could have advantages over simply throwing an exception. If nothing else, the callback could throw an exception which contained information about the context (e.g. it could include information about the record which was being parsed without having to use nested exceptions).

I have not seen this approach, so I can't comment on how good or bad it might be. From the top of my head, I cannot imagine a function that is general enough to cover all possible error states.

But maybe I am wrong. How would you write sample 1 from my article with a callback instead of exceptions?

RAII is an excellent pattern that consistently guarantees the resources are released in a deterministic way. Even Java does not have this idiom, and is prone to resource leaks due to forgotten finally clauses.

So as long as you do not use heap based allocation, and have a correctly implemented set of constructor/destructors, there is no worry of resource leaks in C++.

For example, why does Example 11 use pointers instead of a plain simple code:

For example, why does Example 11 use pointers instead of a plain simple code:

I am afraid you didn't read the article carefuly

I do claim that RAII is a good idiom for managing resources, and sample 11 is an example of what happens if RAII is not consistently used. Your "fixed" version looks exactly like my sample 1 which shows how exception safe C++ code looks like.

especially since it can easily be made into much more correct code that doesn't leak

It is very easy indeed, once you know how to do it. The problem is that too many people just don't know, and some of them write books on C++. See this Bruce Eckel's blog[^] (the bold font is mine):

"
The problem was the same millstone that dogged all C++ decisions: backward compatibility with C. Ideally, stack allocation of objects could simply have been discarded. But C compatibility required stack allocation, so there needed to be some way to distinguish heap objects from stack objects.
"

Granted, Bruce has not done much C++ for over a decade, but people are still learning C++ from his book.

The only way to use RAII with classes like this is to wrap them with custom classes - but if you are using many classes (like legacy libraries) that use this style, this will be a very stupefying task.