Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
There are lots of places where guidelines for designing exception classes can be found. Almost everywhere I look, there's this list of things exception objects should never do, which impacts the design of those classes.
For instance, the Boost people recommend that the class contain no std::string members, because their constructor could throw, which would cause the run-time to terminate the program immediately.
Now, it seems to me that this is rather theoretical. If std::string's constructor throws, it's either a bug (I passed a null-pointer in) or an out-of-memory condition (correct me if I'm wrong here). Since I'm on a desktop, I just pretend I have an infinite amount of memory, and running out of memory is fatal to my application no matter what.
With that in mind, why shouldn't I embed std::string objects in my exception classes? In fact, why couldn't my exception classes be full-featured, and also take care of logging, stack tracing, etc. I'm aware of the one-responsibility principle, and it seems to me to be a fair trade-off to have the exception class do all that. Surely, if my parser needs to report a syntax error, an full-featured exception would be more helpful than an exception built around a statically allocated character array.
So: lean C++ exception classes - how big a deal is it in the real-world? What are the trade-offs? Are there good discussions on the topic?
You could use the Boost.Exception library to help define your exception hierarchy.
The Boost.Exception library supports the:
transporting of arbitrary data to the
catch site, which is otherwise tricky
due to the no-throw requirements
(15.5.1) for exception types.
The limitations of the framework will provide you with reasonably defined design parameters.
Boost.Exception
See also: Boost.System
As a general case, exception classes should be simple, self-sufficient structures and never allocate memory (like std::string does). The first reason is that allocations or other complex operations may fail or have side effects. The other reason is that exception objects are passed by value and thus are stack-allocated, so they must be as light-weighted as possible. Higher level features should be handled by the client code, not the exception class itself (unless for debugging purpose).
The number one job of an exception, well before any consideration to allowing code to handle the exception, is to be able to report to the user and/or dev exactly what went wrong. An exception class that cannot report OOM but just crashes the program without providing any clue to why it crashed is not worth much. OOM is getting pretty common these days, 32-bit virtual memory is running out of gas.
The trouble with adding a lot of helper methods to an exception class is that it will force you into a class hierarchy that you don't necessarily want or need. Deriving from std::exception is now required so you can do something with std::bad_alloc. You'll run into trouble when you use a library that has exception classes that don't derive from std::exception.
Have a look at the std exceptions they all use std::string internally.
(Or should I say my g++ implementation does, I am sure the standard is silent on the issue)
/** Runtime errors represent problems outside the scope of a program;
* they cannot be easily predicted and can generally only be caught as
* the program executes.
* #brief One of two subclasses of exception.
*/
class runtime_error : public exception
{
string _M_msg;
public:
/** Takes a character string describing the error. */
explicit runtime_error(const string& __arg);
virtual ~runtime_error() throw();
/** Returns a C-style character string describing the general cause of
* the current error (the same string passed to the ctor). */
virtual const char* what() const throw();
};
I usually derive my exceptions from runtime_error (or one of the other standard exceptions).
Since I'm on a desktop, I just pretend I have an infinite amount of memory, and running out of memory is fatal to my application no matter what.
So when your app fatally fails, wouldn't you prefer it to terminate cleanly? Let destructors run, file buffers or logs to be flushed, maybe even display an error message (or even better, a bug reporting screen) to the user?
With that in mind, why shouldn't I embed std::string objects in my exception classes? In fact, why couldn't my exception classes be full-featured, and also take care of logging, stack tracing, etc. I'm aware of the one-responsibility principle, and it seems to me to be a fair trade-off to have the exception class do all that.
Why is that a fair trade-off? Why is it a trade-off at all? A trade-off implies that you make some concessions to the Single-Responsibility Principle , but as far as I can see, you don't do that. You simply say "my exception should do everything". That's hardly a trade-off.
As always with the SRP, the answer should be obvious: What do you gain by making the exception class do everything? Why can't the logger be a separate class? Why does it have to be performed by the exception? Shouldn't it be handled by the exception handler? You may also want to localization, and provide syntax error messages in different languages. So your exception class should, while being constructed, go out and read external resource files, looking for the correct localized strings? Which of course means another potential source of errors (if the string can't be found), adds more complexity to the exception, and requires the exception to know otherwise irrelevant information (which language and locale settings the user uses). And the formatted error message may depend on how it is being shown. Perhaps it should be formatted differently when logged, when shown in a message box, or when printed to stdout. More problems for the exception class to deal with. And more things that can go wrong, more code where errors can occur.
The more your exception tries to do, the more things can go wrong. If it tries to log, then what happens if you run out of disk space? Perhaps you also assume infinite disk space, and just ignore that if and when it happens, you'll be throwing away all the error information?
What if you don't have write permission to the log file?
From experience, I have to say that there are few things more annoying than not getting any information about the error that just occurred, because an error occurred. If your error handling can't handle that errors occur, it isn't really error handling. If you exception class can't handle being created and thrown without causing more exceptions, what is the point?
Normally, the reason for the SRP is that the more complexity you add to a class, the harder it is to ensure correctness, and to understand the code. That still applies to exception classes, but you also get a second concern: The more complexity you add to the exception class, the more opportunities there are for errors to occur. And generally, you don't want errors to occur while throwing an exception. You're already in the middle of handling another error, after all.
However, the rule that "an exception class should not contain a std::string isn't quite the same as "an exception class is not allowed to allocate memory". std::exception does the latter. It stores a C-style string after all. Boost just says not to store objects which may throw exceptions. So if you allocate memory, you just have to be able to handle the case where allocation fails.
Surely, if my parser needs to report a syntax error, an full-featured exception would be more helpful than an exception built around a statically allocated character array.
Says the person who just said he didn't mind the app just terminating with no feedback to the user if an error occurred. ;)
Yes, your exception should contain all the data you need to produce a friendly, readable error message. In the case of a parser, I'd say that would have to be something like:
Input file name (or a handle or pointer which allows us to fetch the filename when needed)
The line number at which the error occurred
Perhaps the position on the line
The type of syntax error that occurred.
Based on this information, you can when handling the error produce a nice friendly, robust error message for the user. You can even localize it if you like. You can localize it when you handle the exception.
Generally, exception classes are for use by the programmer. They should not contain or construct text aimed at the user. That can be complex to create correctly, and should be done when handling the error.
The C++ standard requires that exceptions have no-throw copy costructor. If you have a std::string member, you don't have a no-throw copy constructor. If the system fails to copy your exception, it'll terminate your program.
It is also a good idea to use virtual inheritance in designing your exception type hierarchy, as explained in http://www.boost.org/doc/libs/release/libs/exception/doc/using_virtual_inheritance_in_exception_types.html.
However, there is no requirement that exception objects be simple or not allocate memory. In fact, exception objects themselves are typically allocated on the heap, so the system may run out of memory in an attempt to throw an exception.
I think refusing to use std::string in exception classes is unnecessary purism. Yes, it can throw. So what? If your implementation of std::string throws for reasons other than running out of memory just because you're constructing a message "Unable to parse file Foo", then there is something wrong with the implementation, not with your code.
As for running out of memory, you have this problem even when you construct an exception which takes no string arguments. Adding 20 bytes of helpful error message is unlikely to make or break things. In a desktop app, most OOM errors happen when you try to allocate 20 GB of memory by mistake, not because you've been happily running at 99.9999% capacity and something tipped you over the top.
... running out of memory is fatal to my application no matter what.
Unfortunately this is exactly what most people say because they don't want to deal with the complexity that arises otherwise. On the other hand, if you do follow the requirements set by the standard, you get much more robust software that can recover even in low-memory conditions.
Leaving mars rovers aside, think of such a simple case like writing a text processor. You want to implement a copy/paste feature. The user selects some text and hits Ctrl+C. What outcome you prefer: a crash or a message "Not enough memory"? (If there was no enough memory to show the message box just nothing happens.) The second scenario is no doubt much more user friendly, she can just close some other app and continue working on her document.
In fact it's not so hard to guarantee the no-throw copy constructor. You should just store a shared pointer inside your exception that points to the dynamically allocated memory:
class my_exception : virtual public std::exception {
public:
// public interface...
private:
shared_ptr<internal> representation;
};
Assuming that exceptions are for exceptional situations, the atomic count overhead is negligible. In fact this is what Boost.Exception does.
With that said, I recommend jalf's answer as well.
Related
If std::optional's value() member function is called when the optional has no actual value initialized, a std::bad_optional_access is thrown. As it is derived directly from std::exception, you need either catch (std::bad_optional_access const&) or catch (std::exception const&) for dealing with the exception. However, both options seem sad to me:
std::exception catches every single exception
std::bad_optional_access exposes implementation details. Consider the following example:
Placement Item::get_placement() const {
// throws if the item cannot be equipped
return this->placement_optional.value();
}
void Unit::equip_item(Item acquisition) {
// lets the exception go further if it occurs
this->body[acquisition.get_placement()] = acquisition;
}
// somewhere far away:
try {
unit.equip_item(item);
} catch (std::bad_optional_access const& exception) { // what is this??
inform_client(exception.what());
}
So, to catch the exception you need to be well-informed about the usage of std::optional in the Item's implementation, being led to a list of already known issues. Neither I want to catch and rewrap std::bad_optional_access because (for me) the key part of exceptions is the possibility of ignoring them until needed. This is how I see the right approach:
std::exception
<- std::logic_error
<- std::wrong_state (doesn't really exist)
<- std::bad_optional_access (isn't really here)
So, the "far away" example could be written like this:
try {
unit.equip_item(item);
} catch (std::wrong_state const& exception) { // no implementation details
inform_client(exception.what());
}
Finally,
Why is std::bad_optional_access designed like it is?
Do I feel exceptions correctly? I mean, were they introduced for such usage?
Note: boost::bad_optional_access derives from std::logic_error. Nice!
Note 2: I know about catch (...) and throwing objects of types other than std::exception family. They were omitted for brevity (and sanity).
Update: unfortunately, I can't accept two answers, so: if you're interested in the topic, you can read Zuodian Hu's answer and their comments.
You say that the key appeal of exceptions is that you can ignore them for as deep of a call stack as you can. Presumably, given your ambition of avoiding to leak implementation details, you no longer can let an exception propagate beyond the point where that exception cannot be understood and fixed by its handler. That seems to be a contradiction with your ideal design: it punts fixing the exception to the user, but bad_optional_access::what has exactly no context on what just happened–leaking implementation details to the user. How do you expect a user to take meaningful action against a failure to equip an item when all they see is, at best, "could not equip item: bad_optional_access"?
You have obviously made an over-simplification, but the challenge remains. Even with a "better" exception hierarchy, std::bad_optional_access simply does not have enough context that anything beyond extremely close callers might know what to do with it.
There are several fairly distinct cases in which you might want to throw:
You want control flow to be interrupted without much syntactical overhead. For instance, you have 25 different optionals that you want to unwrap, and you want to return a special value if any of them fails. You put a try-catch block around the 25 accesses, saving yourself 25 if blocks.
You have written a library for general use that does a lot of things that can go wrong, and you want to report fine-grained errors to the calling program to give it the best chance of programmatically doing something smart to recover.
You have written a large framework that performs very high-level tasks, such that you expect that usually, the only reasonable outcome of an operation failing is to inform the user that the operation has failed.
When you run into issues with exceptions not feeling right, it's usually because you're trying to handle an error meant for a different level than the one you wish it was operating at. Wishing for changes to the exception hierarchy is just trying to bring that exception in line for your specific use, which causes tensions with how other people use it.
Clearly, the C++ committee believes that bad_optional_access belongs to the first category, and you're asking why it's not part of the third category. Instead of trying to ignore exceptions until you "need" to do something with them, I believe that you should flip the question around and ask yourself what is intended to catch the exception.
If the answer truly is "the user", then you should throw something that's not a bad_optional_access and that instead has high-level features like localized error messages and enough data on it that inform_user is able to bring up a dialog with a meaningful title, main text, subtext, buttons, an icon, etc.
If the answer is that this is a general game engine error and that it might happen in the regular course of the game, then you should throw something that says that equipping the item failed, not that there was a state error. It's more likely that you'll be able to recover from failing to equip an item than from having a non-descript state error, including if, down the road, you need to produce a pretty error for the user.
If the answer is that you might try to equip 25 items in a row and you want to stop as soon as something goes wrong, then you need no changes to bad_optional_access.
Also note that different implementations make different uses more or less convenient. In most modern C++ implementations, there is no performance overhead on code paths that do not throw, and a huge overhead on paths that do throw. This often pushes against the use of exceptions for the first category of errors.
So, to catch the exception you need to be well-informed about the usage of std::optional in the Item's implementation
No, to catch the exception, you must read the documentation for get_placement, which will tell you that it throws std::bad_optional_access. By choosing to emit that exception, the function is making the emission of that exception a part of the interface of that function.
And therefore, it is no more dependent on Item's implementation than it would be if it directly returned a std::optional. You choose to put it in your interface, so you ought to live with the consequences.
To put it another way, if you felt that putting std::optional as a parameter type or return value was wrong, then you should feel the same way about emitting bad_optional_exception directly.
Ultimately, this all goes back to one of the most fundamental questions of error handling: how far away from the site of the error can you get before the specific nature of the error becomes effectively meaningless or even completely different?
Let's say you're doing text processing. You've got a file with each line containing 3 floating-point numbers. You're processing it line by line, and inserting each set of three values into a list. And you have a function that converts strings to floats, which throws an exception if that conversion fails.
So the code broadly looks like this:
for each line
split the line into a 3-element list of number strings.
for each number string
convert the string into a number.
add the number to the current element.
push the element into the list.
Alright, so... what happens if your string-to-float converter throws? That depends; what do you want to happen? That's determined by who catches it. If you want a default value on an error, then the code in the innermost loop catches it and writes a default value into the element.
But maybe you want to log that a particular line has an error, then skip that line (don't add it to the list), but continue processing the rest of the text as normal. In that case, you catch the exception in the first loop.
At that point, the meaning of the error has changed. The error which was thrown was "this string doesn't contain a valid float", but that's not how your code handles it. In fact, the catching code has completely lost the context of the error. It doesn't know whether it was the first, second, or third string in the text which caused the failure. At best, it knows that it was somewhere along that line, and maybe the exception happens to contain a couple of pointers to the bad string range (though that's increasingly dangerous the farther that exception gets from its source, due to the possibility of dangling pointers).
And what if a failed conversion ought to mean that the entire process can no longer be trusted, that the list you're building is invalid and should be discarded? This has even less context than the previous case, and the meaning is even more muddled and distant. At this point, the error just means to terminate the list building process. Maybe you put together a log entry, but that's about all you're going to do at this point.
The farther you get from where the exception is thrown, the more context about the error is lost, and the more the meaning ultimately drifts from the initial meaning of the error. That's not just about being an implementation detail; it's about the locality of information and the response to that information.
So basically, code close to the source of the error is catching specific exceptions with contextual meaning. The farther the catch gets from the source of the error, the more likely it is that the catching code is going to be very generic, dealing with vague "this didn't work because reasons" kinds of things. This is where vague types like std::logic_error come in.
Indeed, one could imagine that at each step in the process, the exception is reinterpreted (and by "reinterpreted", I mean converting it into a different type via catch/throw). The string-to-float converter throws a meaningful exception: could not convert string to float. The layer trying to build an element from 3 strings converts the exception to something that has value to its caller: string index X is malformed. And at the last phase, the exception is generalized to: couldn't parse the list due to line Y.
The idea that a single exception type can jump through whole libraries of code and designed intent and still retain its initial meaning is a fantasy. Exceptions work great when they have to pass through neutral code, such as throwing an exception from a callback or some other case of indirect function execution. In this case, the code which provoked the execution still has the local context of the process that provoked the exception. But the farther from the local context who knows what's going on you get, the less meaningful a specific exception becomes.
Inheriting from logic_error is wrong for these reasons. Catching a bad_optional_access is ultimately a very local thing. Past a certain point, the meaning of that error changes.
A "logic error" represents a failure of your program to make sense. But an optional which doesn't contain a value does not necessarily represent such a problem. In one piece of code, it could be a perfectly valid thing to have an empty optional, and the exception being thrown is simply how that gets reported to the caller. Another piece of code might treat an optional being empty at a certain point as a user having made some prior mistake in their API usage. One of these is a logic error, and the other isn't.
Ultimately, the right thing to do is make sure that your classes APIs all emit exceptions which are meaningful to the caller. And it's not clear what bad_optional_access means to the caller of get_placement.
Exposing Implementation Details
If you wish for your user to be entirely unaware of std::optional in your implementation, your interface would either check operator bool or has_value and do one of the following:
return a status code
throw a custom exception type
handle the emptiness in such a way that the client has no knowledge that an internal error ever happened
...or your interface would catch std::bad_optional_access and do one of the above. In either case, your client has no idea you used std::optional.
Note that whether you found out about the emptiness of the optional through an explicit check or an exception is a design choice (but personally I wouldn't catch and re-throw either in most cases).
Logic Error?
Based on the conceptual model for optional in the pre-standardization paper, std::optional is a value wrapper with a valid empty state. Hence, the intent was for emptiness to be intentional in normal usage. There are two general ways handling emptiness, as I stated in the comments:
use operator bool or has_value, then handle emptiness inline or use the wrapped value through operator* or operator->.
use value and bail out of the scope if the optional is empty
In either case, you should be expecting the optional to potentially be empty, and designed for that to be a valid state in your program.
In other words, when you use operator bool or has_value to check for emptiness, it is not to prevent an exception being thrown. Instead, you are choosing to not use the exception interface of optional at all (usually). And when you use value, you are choosing to accept optional potentially throwing std::bad_optional_access. Hence, the exception should never be a logic error in the intended usage of optional.
UPDATE
Logic Errors in the Design of C++
You seem to misunderstand the Standard's intended definition of what a logic error is.
In the design of C++ in recent years (not the same in history), a logic error is a programmer error the application shouldn't try to recover from because it can't reasonably recover. This includes things like de-referencing dangling pointers and references, using operator* or operator-> on an empty optional, passing invalid arguments to a function, or otherwise breaking API contracts. Note that dangling pointers' existence is not a logic error, but de-referencing a dangling pointer is a logic error.
In these cases of true logic errors, the Standard purposely chooses not to throw because they are true logic errors on the part of the programmer, and the caller can't be reasonably expected to handle all bugs in the code they call.
When a well-designed (under this philosophy) Standard Library function throws, it's never supposed to be because the code or the caller wrote buggy code. For buggy code, the Standard let's you fall flat on your face for writing the bug. For example, many functions in <algorithn> run infinite loops if you pass them bad begin and end iterators, and never even try to diagnose the fact that you did that. They certainly don't throw std::invalid_argument. "Good" implementations do try to diagnose this in Debug builds though, because those logic errors are bugs. When a well-designed (under this philosophy) Standard Library function throws, it's supposed to be because a truly exceptional and unavoidable event occurred. has many throwing functions, because you can't really ever know for sure what's on some random file system. That's the situation exceptions are supposed to be used for.
In the paper linked below, Herb Sutter speaks against std::logic_error's existence as an exception type for this very reason. Clearly stating the philosophy, catching std::logic_error or any of its children amounts to introducing runtime overhead to fix programmer logic bugs. Any true logic error condition you want to detect should be asserted on, really, so the bug can be reported back to the people who wrote the bug.
In the optional interface, designed with the above in mind, value throws so that you can programmatically deal with it in a sensible way with the expectation that whoever catches it either don't care what bad_optional_access means (catch( ... ) // catch everything) or can specifically deal with bad_optional_access. That exception really isn't meant to propagate far at all. When you purposely call value, you do so because you acknowledge the optional may be empty, and you choose to exit the current scope if it does turn out to be empty.
See the first section of this paper (download) for the philosophical rationale.
First, if you don't want to expose the imlementation, than the exceptions shouldn't even cross the border between the implementation and the client code. This is a common idiom that no exception should cross the boundaries of libraries, APIs, etc.
Next, the fact that you store something in an optional is the implementation that you should control by yourself. That means that you should check that the optional is not empty (at least if you don't want the client knowing the details of the implementation).
And finally, answer the question: is it an error that the client code performs an operation on an empty object? If that is something that it is allowed to do, than no exception should be thrown (e.g. error code may be returned). If that is a real problem that shouldn't happen, throwing an exception is appropriate. You may catch the std::bad_optional_access in your code and throw something else from the catch block.
Another solution to your problem could be nested exceptions. That means that you catch a lower level exception (in your case std::bad_optional_access) and then throw another exception (of any type, in your case you may implement a wrong_state : public std::logic_error) using the std::throw_with_nested function. Using this approach you:
Preserve the information about the lower level exception (it is
still stored as a nested exception)
Hide this information about the nested exception from the user
Allow user to catch the exception as a wrong_state or a std::logic_error.
see the example: https://coliru.stacked-crooked.com/view?id=b9bc940f2cc6d8a3
Consider this REAL WORLD example of when NOT to use std:bad_optional_access, involving the INELEGANT 900 lines of code, wrapped up into one HUGE class, just to render a vulkan triangle, in THIS example at https://vulkan-tutorial.com/code/06_swap_chain_creation.cpp
I am in the process of reimplementing the one HUGE HelloTriangleApplication class into multiple smaller classes. And,the QueueFamilyIndices struct begins as a couple of empty std::optional<uinit32_t> lists , ergo, EXACTLY the sort of not-yet-a-things which std::optional was invented to handle.
So, obviously, I wanted to test each class, before subclassing it into another class. But, this involved leaving some not-yet-a-things uninitialized until a parent's subclass was latter implemented.
It seemed right, at least to me, NOT to use std:bad-optional-access as a placeholder for future values, but rather to just code a 0 in the parent class, as a placeholder for not yet implemented std:optional not-yet-a-things. Which was enough to avoid my IDE reporting those annoying "bad optional access" warnings.
This is a good question with good answers. I want to highlight some of the main points more directly, and also add some points that I disagree with from the other answers. I come at this more from the POV of abstract flow of information, with the idea that all infinite variants of specific situations become easier to handle when appropriate information is passed around effectively.
The TL;DR here is:
Using bad_optional_access in a semantically incorrect way is common, but is the root cause of a lot of the other stuff (like logic_error) appearing to not making sense, and
value() should be used only when you know it has a value; it's not intended as some "exception-y" variation of value_or(). It doesn't make sense when there is no value: A thing with no value does not have a value, and so retrieving its value isn't something you can do. If you call 'value()' when there's no value then you made a mistake somewhere.
Regarding the use of value() itself:
If you cannot guarantee that an optional has a value, you use has_value() or value_or(). The use of value() assumes the optional has a value and by using it, you are stating that assumption as an invariant (i.e. assert(x.has_value()) is expected to pass), and if it doesn't have a value, then the invariant has been violated and an exception is appropriate. value() does not have meaning when the optional doesn't have a value. It is the same reason that you do not compute a / b in situations where b might be 0 -- you either know it isn't 0, or you check first. Likewise dereferencing invalid iterators, accessing invalid pointers, calling front() on an empty container, uh... computing the unbiased variance of a single sample... things like that.
Following that point, if you see a bad_optional_access, then this means there is a bug in your code: one of your assumptions (it had a value) was false. In other words, this is a development error, and in an ideal world a user should never encounter this exception in the same way a user should never encounter an assertion failure or a divide-by-zero or a null pointer access: It does not represent a user-actionable error, it represents code that needs to be fixed. Ideally only you as a developer should encounter this particular exception.
This is specifically why it is a logic_error: You used value() but did not honor its preconditions, and the implied assumption that you made about it having a value was not correct. You made a programming error by using value() in a situation where you could not guarantee that it had a value.
That said, the world isn't ideal. Also, generally speaking, if some exception below some layer of code is meant to represent a more user-appropriate error above that layer of code, then you need to translate that exception. For example:
An exception may expose an implementation detail that you want to abstract away.
An exception may contain information that doesn't make sense to a user, but may represent a larger more generic issue that is important to a user.
And so you need to translate that. For example:
Placement Item::get_placement() const {
// throws if the item cannot be equipped
return this->placement_optional.value();
}
The comment literally says "throws if the item cannot be equipped", but bad_optional_access doesn't mean "an item cannot be equipped". So if you allow it to be thrown out of that function, then you've miscommunicated the conceptual issue by throwing a semantically incorrect exception. Instead:
// elsewhere
class item_equip_exception : ... {
...
};
// then:
Placement Item::get_placement() const {
// throws if the item cannot be equipped
try {
return this->placement_optional.value();
} catch (const std::bad_optional_access &x) {
throw item_equip_exception(...);
}
}
Because that's what you're really trying to communicate.
However, an even more correct version of that would be:
Placement Item::get_placement() const {
// throws if the item cannot be equipped
if (!this->placement_optional.has_value())
throw item_equip_exception(...);
return this->placement_optional.value();
}
The reason this is more correct is because now you are calling value() in a situation where your assumption that it has a value should be guaranteed. And in this case, if you end up with a bad_optional_access, then it's truly a serious logic error. This now means that, as long as you're consistent with this approach, at the very top level of your application you can now actually catch std::logic_error and it really will mean that some program logic went terribly wrong, and you can inform the user as such.
All of the issues in the original post can basically be boiled down to semantics:
If you use value() when there might be no value, that's a programming error, and...
If you then use that "programming error" to signify a general user-facing error, then...
... you've now tangled up all the semantics and nothing makes sense any more, including `std::logic_error'.
Where on the other hand:
If there reasonably may be no value and that signifies some higher level thing like "the item can't be equipped", and...
You check for that and throw a more appropriate exception and never call value() when there's no value, then...
... you can now communicate that to the user, the logic_error -> bad_optional_access inheritance meaning continues to make sense, and you can still separately catch programmer errors at a higher level.
So yeah; "say what you mean" applies as much to information flow in programming as it does to speaking in real life!
Just wondering if there is any agreement on what type of std::exception is appropriate to throw when parsing a file with incorrect format in C++. I would say a generic std::logic_error even though it's not the logic but data what is wrong, what do you guys think it should be?
Considering Standard C++ exceptions, i would say runtime_error fits this case the best.
std::runtime_error
It reports errors that are due to events beyond the scope of the program and can not be easily predicted.
Parsing a file requires runtime resources (the file itself and its content are only determined during runtime). [0]
However defining a new exception (for example: call it file_error and derive it from std::runtime_error) is another reasonable approach i think. This way you can extend the exception's capabilities so your software will be able to deal with the occurring problems more easily. (You can add any functionality to an exception class that a standard class is able to have.)
It is possible to throw any object (from any class) as an exception but deriving custom exceptions from std::exception is considered good practice, because it fits well into STL's and third party's exception handling methods (in most of the time).
I personally prefer using my own exceptions.
Also read similar question: SO:Confused about std::runtime_error vs. std::logic_error
[0]: In case you logically connect the file system (and the file in it) to the operating system then you might consider using system_error for this case - but parsing it doesn't seem like this category.
You are not limited to throwing std exceptions. Any class can be thrown as an exception. In large applications you will typically find entire classes whose only purpose is for an instance of them to get thrown as an exception, so that when it's caught, the exception handler has more information and context available for it to decide the next course of action (that was, of course, prepared and placed into the class instance before it was thrown).
One is to use C++ exceptions: try catch blocks. But freeing dynamic memory will be an issue when an exception is raised.
Second is to use C style: errno variable
Third is just to return -1 on error and 0 on success :)
Which way should be chosen for a mid-size project and why? Any other better approach..?
But freeing dynamic memory will be an issue when an exception is raised.
No it's not. std::vector<int> v(100); Done.
The concept here is called Scope-Bound Resource Management (SBRM), also known by the much more common (and awkward) name Resource Acquisition Is Initialization (RAII). Basically, all resources are contained in some object which will clean up the resource in the destructor (which is always guaranteed to be run for an automatically allocated object). So whether or not the function exists normally or via exception, the destructor is run and your resource is cleaned up.
Never do an allocation where you need to free it explicitly, use containers and smart pointers.
Second is to use C style: errno variable
Third is just to return -1 on error and 0 on success :)
And how do they help solving your problem of freeing dynamic memory? They also use an early-exit strategy, same as throw.
So in summary, they don’t have an advantage over C++ exceptions (according to you).
In the first place, you should strive for a program with minimum error cases. (Because errors are not cool.)
Exceptions are a nice tool but should be used conservatively: reserve them for "exceptional cases", do not use them to control the flow of your program.
For example, do not use exceptions to test whether a user input is correct or not. (For such a case, return an error code.)
One is to use C++ exceptions: try
catch blocks. But freeing dynamic
memory will be an issue when an
exception is raised.
#see RAII.
Exceptions should be your preferred method of dealing with exceptional runtime situations like running out of memory. Note that something like std::map::find doesn't throw (and it shouldn't) because it's not necessarily an error or particularly exceptional case to search for a key that doesn't exist: the function can inform the client whether or not the key exists. It's not like a violation of a pre-condition or post-condition like requiring a file to exist for a program to operate correctly and finding that the file isn't there.
The beauty of exception-handling, if you do it correctly (again, #see RAII), is that it avoids the need to litter error-handling code throughout your system.
Let's consider a case where function A calls function B which calls C then D and so on, all the way up to 'Z'. Z is the only function that can throw, and A is the only one interested in recovering from an error (A is the entry point for a high-level operation, e.g., like loading an image). If you stick to RAII which will be helpful for more than exception-handling, then you only need to put a line of code in Z to throw an exception and a little try/catch block in A to catch the exception and, say, display an error message to the user.
Unfortunately a lot of people don't adhere to RAII as strictly as they should in practice, so a lot of real world code has more try/catch blocks than should be necessary to deal with manual resource cleanup (which shouldn't have to be manual). Nevertheless, this is the ideal you should strive to achieve in your code, and it's more practical if it's a mid-sized project. Likewise, in real world scenarios, people often ignore error codes returned by functions. if you're going to put the extra mile in favor of robustness, you might as well start with RAII because that will help your application regardless of whether you use exception handling or error code handling.
There is a caveat: you should not throw exceptions across module boundaries. If you do, you should consider a hybrid between error codes (as in returning error codes, not using a global error status like errno) and exceptions.
It is worth noting that if you use operator new in your code without specifying nothrow everywhere, ex:
int* p = new int(123); // can throw std::bad_alloc
int* p = new(std::nothrow) int(123); // returns a null pointer on failure
... then you already need to catch and handle bad_alloc exceptions in your code for it to be robust against out of memory exceptions.
Have a look at this comment by Herb Sutter on try catch for C++ GOTW. And do go through his whole set of articles. He does have a lot to say on when and how to check and save yourself from error conditions and how to handle them in the best ways possible.
Throw an exception. Destructors of variables are always called when an exception is thrown, and if your stack-based variables don't clean up after themselves (if for example you used a raw pointer when you need to delete the result), then you get what you deserve. Use smart pointers, no memory leaks.
But freeing dynamic memory will be an issue when an exception is raised.
Freeing memory (or any other resource for that matter) doesn't suddenly become a non-issue because you don't use exceptions. The techniques that make dealing with these problems while exceptions can be thrown easy, also make it easier when there can be "error conditions".
Exceptions are good for passing control from one context to another.
You let the compiler do the work of unrolling the stack between the contexts then in the new context compensate for the exception (and then hopefully continue).
If your error happens and can be corrected in the same context then error codes are a good method to do error handling and clean up (Don't take this to mean you should not be using RAII you still need that). But for example within a class an error occurs in a function and the calling function can correct for that type of error (then it probably is not an exceptional circumstance so no exceptions) then error code are useful.
You should not use error codes when you have to pass information out of a library or sub system as you are then relying on the developer using the code to actually check and handle the code to make sure it works correctly and more often than not they will ignore error codes.
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 9 years ago.
Exception handling (EH) seems to be the current standard, and by searching the web, I can not find any novel ideas or methods that try to improve or replace it (well, some variations exist, but nothing novel).
Though most people seem to ignore it or just accept it, EH has some huge drawbacks: exceptions are invisible to the code and it creates many, many possible exit points. Joel on software wrote an article about it. The comparison to goto fits perfect, it made me think again about EH.
I try to avoid EH and just use return values, callbacks or whatever fits the purpose. But when you have to write reliable code, you just can't ignore EH these days: It starts with the new, which may throw an exception, instead of just returning 0 (like in the old days). This makes about any line of C++ code vulnerable to an exception. And then more places in the C++ foundational code throw exceptions... std lib does it, and so on.
This feels like walking on shaky grounds.. So, now we are forced to take care about exceptions!
But its hard, its really hard. You have to learn to write exception safe code, and even if you have some experience with it, it will still be required to double check any single line of code to be safe! Or you start to put try/catch blocks everywhere, which clutters the code until it reaches a state of unreadability.
EH replaced the old clean deterministical approach (return values..), which had just a few but understandable and easily solveable drawbacks with an approach that creates many possible exit points in your code, and if you start writing code that catches exceptions (what you are forced to do at some point), then it even creates a multitude of paths through your code (code in the catch blocks, think about a server program where you need logging facilities other than std::cerr ..). EH has advantages, but that's not the point.
My actual questions:
Do you really write exception safe code?
Are you sure your last "production ready" code is exception safe?
Can you even be sure, that it is?
Do you know and/or actually use alternatives that work?
Your question makes an assertion, that "Writing exception-safe code is very hard". I will answer your questions first, and then, answer the hidden question behind them.
Answering questions
Do you really write exception safe code?
Of course, I do.
This is the reason Java lost a lot of its appeal to me as a C++ programmer (lack of RAII semantics), but I am digressing: This is a C++ question.
It is, in fact, necessary when you need to work with STL or Boost code. For example, C++ threads (boost::thread or std::thread) will throw an exception to exit gracefully.
Are you sure your last "production ready" code is exception safe?
Can you even be sure, that it is?
Writing exception-safe code is like writing bug-free code.
You can't be 100% sure your code is exception safe. But then, you strive for it, using well-known patterns, and avoiding well-known anti-patterns.
Do you know and/or actually use alternatives that work?
There are no viable alternatives in C++ (i.e. you'll need to revert back to C and avoid C++ libraries, as well as external surprises like Windows SEH).
Writing exception safe code
To write exception safe code, you must know first what level of exception safety each instruction you write is.
For example, a new can throw an exception, but assigning a built-in (e.g. an int, or a pointer) won't fail. A swap will never fail (don't ever write a throwing swap), a std::list::push_back can throw...
Exception guarantee
The first thing to understand is that you must be able to evaluate the exception guarantee offered by all of your functions:
none: Your code should never offer that. This code will leak everything, and break down at the very first exception thrown.
basic: This is the guarantee you must at the very least offer, that is, if an exception is thrown, no resources are leaked, and all objects are still whole
strong: The processing will either succeed, or throw an exception, but if it throws, then the data will be in the same state as if the processing had not started at all (this gives a transactional power to C++)
nothrow/nofail: The processing will succeed.
Example of code
The following code seems like correct C++, but in truth, offers the "none" guarantee, and thus, it is not correct:
void doSomething(T & t)
{
if(std::numeric_limits<int>::max() > t.integer) // 1. nothrow/nofail
t.integer += 1 ; // 1'. nothrow/nofail
X * x = new X() ; // 2. basic : can throw with new and X constructor
t.list.push_back(x) ; // 3. strong : can throw
x->doSomethingThatCanThrow() ; // 4. basic : can throw
}
I write all my code with this kind of analysis in mind.
The lowest guarantee offered is basic, but then, the ordering of each instruction makes the whole function "none", because if 3. throws, x will leak.
The first thing to do would be to make the function "basic", that is putting x in a smart pointer until it is safely owned by the list:
void doSomething(T & t)
{
if(std::numeric_limits<int>::max() > t.integer) // 1. nothrow/nofail
t.integer += 1 ; // 1'. nothrow/nofail
std::auto_ptr<X> x(new X()) ; // 2. basic : can throw with new and X constructor
X * px = x.get() ; // 2'. nothrow/nofail
t.list.push_back(px) ; // 3. strong : can throw
x.release() ; // 3'. nothrow/nofail
px->doSomethingThatCanThrow() ; // 4. basic : can throw
}
Now, our code offers a "basic" guarantee. Nothing will leak, and all objects will be in a correct state. But we could offer more, that is, the strong guarantee. This is where it can become costly, and this is why not all C++ code is strong. Let's try it:
void doSomething(T & t)
{
// we create "x"
std::auto_ptr<X> x(new X()) ; // 1. basic : can throw with new and X constructor
X * px = x.get() ; // 2. nothrow/nofail
px->doSomethingThatCanThrow() ; // 3. basic : can throw
// we copy the original container to avoid changing it
T t2(t) ; // 4. strong : can throw with T copy-constructor
// we put "x" in the copied container
t2.list.push_back(px) ; // 5. strong : can throw
x.release() ; // 6. nothrow/nofail
if(std::numeric_limits<int>::max() > t2.integer) // 7. nothrow/nofail
t2.integer += 1 ; // 7'. nothrow/nofail
// we swap both containers
t.swap(t2) ; // 8. nothrow/nofail
}
We re-ordered the operations, first creating and setting X to its right value. If any operation fails, then t is not modified, so, operation 1 to 3 can be considered "strong": If something throws, t is not modified, and X will not leak because it's owned by the smart pointer.
Then, we create a copy t2 of t, and work on this copy from operation 4 to 7. If something throws, t2 is modified, but then, t is still the original. We still offer the strong guarantee.
Then, we swap t and t2. Swap operations should be nothrow in C++, so let's hope the swap you wrote for T is nothrow (if it isn't, rewrite it so it is nothrow).
So, if we reach the end of the function, everything succeeded (No need of a return type) and t has its excepted value. If it fails, then t has still its original value.
Now, offering the strong guarantee could be quite costly, so don't strive to offer the strong guarantee to all your code, but if you can do it without a cost (and C++ inlining and other optimization could make all the code above costless), then do it. The function user will thank you for it.
Conclusion
It takes some habit to write exception-safe code. You'll need to evaluate the guarantee offered by each instruction you'll use, and then, you'll need to evaluate the guarantee offered by a list of instructions.
Of course, the C++ compiler won't back up the guarantee (in my code, I offer the guarantee as a #warning doxygen tag), which is kinda sad, but it should not stop you from trying to write exception-safe code.
Normal failure vs. bug
How can a programmer guarantee that a no-fail function will always succeed? After all, the function could have a bug.
This is true. The exception guarantees are supposed to be offered by bug-free code. But then, in any language, calling a function supposes the function is bug-free. No sane code protects itself against the possibility of it having a bug. Write code the best you can, and then, offer the guarantee with the supposition it is bug-free. And if there is a bug, correct it.
Exceptions are for exceptional processing failure, not for code bugs.
Last words
Now, the question is "Is this worth it ?".
Of course, it is. Having a "nothrow/no-fail" function knowing that the function won't fail is a great boon. The same can be said for a "strong" function, which enables you to write code with transactional semantics, like databases, with commit/rollback features, the commit being the normal execution of the code, throwing exceptions being the rollback.
Then, the "basic" is the very least guarantee you should offer. C++ is a very strong language there, with its scopes, enabling you to avoid any resource leaks (something a garbage collector would find it difficult to offer for the database, connection or file handles).
So, as far as I see it, it is worth it.
Edit 2010-01-29: About non-throwing swap
nobar made a comment that I believe, is quite relevant, because it is part of "how do you write exception safe code":
[me] A swap will never fail (don't even write a throwing swap)
[nobar] This is a good recommendation for custom-written swap() functions. It should be noted, however, that std::swap() can fail based on the operations that it uses internally
the default std::swap will make copies and assignments, which, for some objects, can throw. Thus, the default swap could throw, either used for your classes or even for STL classes. As far as the C++ standard is concerned, the swap operation for vector, deque, and list won't throw, whereas it could for map if the comparison functor can throw on copy construction (See The C++ Programming Language, Special Edition, appendix E, E.4.3.Swap).
Looking at Visual C++ 2008 implementation of the vector's swap, the vector's swap won't throw if the two vectors have the same allocator (i.e., the normal case), but will make copies if they have different allocators. And thus, I assume it could throw in this last case.
So, the original text still holds: Don't ever write a throwing swap, but nobar's comment must be remembered: Be sure the objects you're swapping have a non-throwing swap.
Edit 2011-11-06: Interesting article
Dave Abrahams, who gave us the basic/strong/nothrow guarantees, described in an article his experience about making the STL exception safe:
http://www.boost.org/community/exception_safety.html
Look at the 7th point (Automated testing for exception-safety), where he relies on automated unit testing to make sure every case is tested. I guess this part is an excellent answer to the question author's "Can you even be sure, that it is?".
Edit 2013-05-31: Comment from dionadar
t.integer += 1; is without the guarantee that overflow will not happen NOT exception safe, and in fact may technically invoke UB! (Signed overflow is UB: C++11 5/4 "If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.") Note that unsigned integer do not overflow, but do their computations in an equivalence class modulo 2^#bits.
Dionadar is referring to the following line, which indeed has undefined behaviour.
t.integer += 1 ; // 1. nothrow/nofail
The solution here is to verify if the integer is already at its max value (using std::numeric_limits<T>::max()) before doing the addition.
My error would go in the "Normal failure vs. bug" section, that is, a bug.
It doesn't invalidate the reasoning, and it does not mean the exception-safe code is useless because impossible to attain.
You can't protect yourself against the computer switching off, or compiler bugs, or even your bugs, or other errors. You can't attain perfection, but you can try to get as near as possible.
I corrected the code with Dionadar's comment in mind.
Writing exception-safe code in C++ is not so much about using lots of try { } catch { } blocks. It's about documenting what kind of guarantees your code provides.
I recommend reading Herb Sutter's Guru Of The Week series, in particular installments 59, 60 and 61.
To summarize, there are three levels of exception safety you can provide:
Basic: When your code throws an exception, your code does not leak resources, and objects remain destructible.
Strong: When your code throws an exception, it leaves the state of the application unchanged.
No throw: Your code never throws exceptions.
Personally, I discovered these articles quite late, so much of my C++ code is definitely not exception-safe.
Some of us have been using exception for over 20 years. PL/I has them, for example. The premise that they are a new and dangerous technology seems questionable to me.
First of all (as Neil stated), SEH is Microsoft's Structured Exception Handling. It is similar to but not identical to exception processing in C++. In fact, you have to enable C++ Exception Handling if you want it in Visual Studio - the default behavior does not guarantee that local objects are destroyed in all cases! In either case, Exception Handling is not really harder it is just different.
Now for your actual questions.
Do you really write exception safe code?
Yes. I strive for exception safe code in all cases. I evangelize using RAII techniques for scoped access to resources (e.g., boost::shared_ptr for memory, boost::lock_guard for locking). In general, consistent usage of RAII and scope guarding techniques will make exception safe code much easier to write. The trick is to learn what exists and how to apply it.
Are you sure your last "production ready" code is exception safe?
No. It is as safe as it is. I can say that I haven't seen a process fault due to an exception in several years of 24/7 activity. I don't expect perfect code, just well-written code. In addition to providing exception safety, the techniques above guarantee correctness in a way that is near impossible to achieve with try/catch blocks. If you are catching everything in your top control scope (thread, process, etc.), then you can be sure that you will continue to run in the face of exceptions (most of the time). The same techniques will also help you continue to run correctly in the face of exceptions without try/catch blocks everywhere.
Can you even be sure that it is?
Yes. You can be sure by a thorough code audit but no one really does that do they? Regular code reviews and careful developers go a long way to getting there though.
Do you know and/or actually use alternatives that work?
I have tried a few variations over the years such as encoding states in the upper bits (ala HRESULTs) or that horrible setjmp() ... longjmp() hack. Both of these break down in practice though in completely different ways.
In the end, if you get into the habit of applying a few techniques and carefully thinking about where you can actually do something in response to an exception, you will end up with very readable code that is exception safe. You can sum this up by following these rules:
You only want to see try/catch when you can do something about a specific exception
You almost never want to see a raw new or delete in code
Eschew std::sprintf, snprintf, and arrays in general - use std::ostringstream for formatting and replace arrays with std::vector and std::string
When in doubt, look for functionality in Boost or STL before rolling your own
I can only recommend that you learn how to use exceptions properly and forget about result codes if you plan on writing in C++. If you want to avoid exceptions, you might want to consider writing in another language that either does not have them or makes them safe. If you want to really learn how to fully utilize C++, read a few books from Herb Sutter, Nicolai Josuttis, and Scott Meyers.
It is not possible to write exception-safe code under the assumption that "any line can throw". The design of exception-safe code relies critically on certain contracts/guarantees that you are supposed to expect, observe, follow and implement in your code. It is absolutely necessary to have code that is guaranteed to never throw. There are other kinds of exception guarantees out there.
In other words, creating exception-safe code is to a large degree a matter of program design not just a matter of plain coding.
Do you really write exception safe code?
Well, I certainly intend to.
Are you sure your last "production ready" code is exception safe?
I'm sure that my 24/7 servers built using exceptions run 24/7 and don't leak memory.
Can you even be sure, that it is?
It's very difficult to be sure that any code is correct. Typically, one can only go by results
Do you know and/or actually use alternatives that work?
No. Using exceptions is cleaner and easier than any of the alternatives I've used over the last 30 years in programming.
Leaving aside the confusion between SEH and C++ exceptions, you need to be aware that exceptions can be thrown at any time, and write your code with that in mind. The need for exception-safety is largely what drives the use of RAII, smart pointers, and other modern C++ techniques.
If you follow the well-established patterns, writing exception-safe code is not particularly hard, and in fact it's easier than writing code that handles error returns properly in all cases.
EH is good, generally. But C++'s implementation is not very friendly as it's really hard to tell how good your exception catching coverage is. Java for instance makes this easy, the compiler will tend to fail if you don't handle possible exceptions .
I really like working with Eclipse and Java though (new to Java), because it throws errors in the editor if you are missing an EH handler. That makes things a LOT harder to forget to handle an exception...
Plus, with the IDE tools, it adds the try / catch block or another catch block automatically.
Some of us prefer languages like Java which force us to declare all the exceptions thrown by methods, instead of making them invisible as in C++ and C#.
When done properly, exceptions are superior to error return codes, if for no other reason than you don't have to propagate failures up the call chain manually.
That being said, low-level API library programming should probably avoid exception handling, and stick to error return codes.
It's been my experience that it's difficult to write clean exception handling code in C++. I end up using new(nothrow) a lot.
I try my darned best to write exception-safe code, yes.
That means I take care to keep an eye on which lines can throw. Not everyone can, and it is critically important to keep that in mind. The key is really to think about, and design your code to satisfy, the exception guarantees defined in the standard.
Can this operation be written to provide the strong exception guarantee? Do I have to settle for the basic one? Which lines may throw exceptions, and how can I ensure that if they do, they don't corrupt the object?
Do you really write exception safe code?
[There's no such thing. Exceptions are a paper shield to errors unless you have a managed environment. This applies to first three questions.]
Do you know and/or actually use alternatives that work?
[Alternative to what? The problem here is people don't separate actual errors from normal program operation. If it's normal program operation (ie a file not found), it's not really error handling. If it's an actual error, there is no way to 'handle' it or it's not an actual error. Your goal here is to find out what went wrong and either stop the spreadsheet and log an error, restart the driver to your toaster, or just pray that the jetfighter can continue flying even when it's software is buggy and hope for the best.]
A lot (I would even say most) people do.
What's really important about exceptions, is that if you don't write any handling code - the result is perfectly safe and well-behaved. Too eager to panic, but safe.
You need to actively make mistakes in handlers to get something unsafe, and only catch(...){} will compare to ignoring error code.
What techniques can I use to avoid exceptions in C++, as mentioned in Google's style guide?
Don't throw exceptions.
Don't use STL (which relies heavily on exceptions).
Use only new(std::nothrow) or override ::operator new to return 0 on failure.
Note that by avoiding exceptions, you're effectively throwing out lots of useful libraries, including Boost. Basically, you'll have to program everything from scratch.
Not throwing exceptions in your own code is relatively easy: you just don't use the throw statement.
Not throwing exceptions from memory allocation failures is a little more painful: either you don't use normal new (use new(std::nothrow) or malloc or something instead), or you use some nonstandard compiler option to get it to do something nonstandard when it fails (e.g. immediately terminate your program, or return 0), or you override operator new to do something nonstandard when it fails.
If your chosen approach is to immediately terminate the program, you can implement this with set_new_handler(), which I had forgotten about until litb reminded me.
That leaves the problem of dealing with exceptions generated by C++ libraries you don't maintain. Generally you'll have to wrap library calls in a wrapper that looks something like this:
int DoSomething(int &output, const int input) throw() {
try {
output = library_do_something(input);
return 1;
} catch (...) {
return 0;
}
}
The catch (...) catches all possible C++ exceptions from library_do_something (as well as the assignment operator on output, which isn't relevant here), throws away all the information they may have contained, and then maps all those failures to 0.
Note that this style means that you can't use RAII at all, not even a little bit, because you have no way of signaling failure within a constructor. The whole point of RAII is that you acquire all your resources inside of constructors so that they will be properly released by a destructor during exception propagation. But resource acquisition is something that can essentially always fail. So you can't do that inside a constructor.
I'm interested to know why one would want to avoid exceptions in C++ and what mechanism one would replace them with to deal with the reality of unexpected failure while still maintaining decent structure.
Sure adding them to a existing codebase that doesn't use RAII type semantics is extremely costly - but if one is doing green field development then what alternative would you suggest and how are going to justify not using high quality libraries that do use exceptions vs. writing your own exception free / bug free alternatives?
The style guide says they "don't use exceptions" which is just that - they don't throw them and don't call anything that could throw them (for example, they would use the new(std::nothrow) instead of usual new because the latter will throw bad_alloc when it fails to allocate memory.
In some compilers, you may be able to turn off exception handling. This could cause unexpected results with external code though - it is not something I would want to try.
Other than that, the obvious first step would be to avoid throwing exceptions from your own code, probably with liberal use of the nothrow directive, and attempt to avoid throwing exceptions from external (3rd party) code through defensive programming.
That means that your code needs to be aware of possible external exceptional failure conditions all the time, such as running out of memory, out of disk space, loss of an internet connection, hardware failure, and any number of other circumstances that might cause code to throw...
In some cases you may be able to use exception-free versions of some code (such as throwing-new vs. non-throwing new).
What I do is to never throw exceptions with my own code, and to "translate" or wrap any external code that does that.