C++ exception safety paranoia: how much is too much? - c++

The strong exception safety guarantee says that an operation won't change any program state if an exception occurs. An elegant way of implementing exception-safe copy-assignment is the copy-and-swap idiom.
My questions are:
Would it be overkill to use copy-and-swap for every mutating operation of a class that mutates non-primitive types?
Is performance really a fair trade for strong exception-safety?
For example:
class A
{
public:
void increment()
{
// Copy
A tmp(*this);
// Perform throwing operations on the copy
++(tmp.x);
tmp.x.crazyStuff();
// Now that the operation is done sans exceptions,
// change program state
swap(tmp);
}
int setSomeProperty(int q)
{
A tmp(*this);
tmp.y.setProperty("q", q);
int rc = tmp.x.otherCrazyStuff();
swap(tmp);
return rc;
}
//
// And many others similarly
//
void swap(const A &a)
{
// Non-throwing swap
}
private:
SomeClass x;
OtherClass y;
};

You should always aim for the basic exception guarantee: make sure that in the event of an exception, all resources are released correctly and the object is in a valid state (which can be undefined, but valid).
The strong exception guarantee (ie. "transactions") is something you should implement when you think it makes sense: you don't always need transactional behavior.
If it is easy to achieve transactional operations (eg. via copy&swap), then do it. But sometimes it is not, or it incurs a big perfomance impact, even for fundamental things like assignment operators. I remember implementing something like boost::variant where I could not always provide the strong guarantee in the copy assignment.
One tremendous difficulty you'll encounter is with move semantics. You do want transactions when moving, because otherwise you lose the moved object. However, you cannot always provide the strong guarantee: think about std::pair<movable_nothrow, copyable> (and see the comments). This is where you have to become a noexcept virtuoso, and use an uncomfortable amount of metaprogramming. C++ is difficult to master precisely because of exception safety.

As all matters of engineering, it is about balance.
Certainly, const-ness/immutability and strong guarantees increase confidence in one's code (especially accompanied with tests). They also help trim down the space of possible explanations for a bug.
However, they might have an impact on performance.
Like all performance issues, I would say: profile and get rid of the hot spots. Copy And Swap is certainly not the only way to achieve transactional semantics (it is just the easiest), so profiling will tell you where you should absolutely not use it, and you will have to find alternatives.

It depends on what environment your application is going to run in. If you just run it on your own machine (one end of the spectrum), it might not be worth to be too strict on exception safety. If you are writing a program e.g. for medical devices (the other end), you do not want unintentional side-effects left behind when an exception occurs. Anything in-between is dependent on the level of tolerance for errors and available resources for development (time, money, etc.)

Yes, the problem you are facing is that this idiom is very hard to scale. None of the other answers mentioned it, but another very interesting idiom invented by Alexandrescu called scopeGuards. It helps to enhance the economy of the code and makes huge improvements in readability of functions that need to conform to strong exception safety guarantees.
The idea of a scope guard is a stack instance that lets just attach rollback function objects to each resource adquisition. when the scope guard is destructed (by an exception) the rollback is invoked. You need to explicitly call commit() in normal flow to avoid the rollback invocation at scope exit.
Check this recent question from me that is related to designed a safe scopeguard using c++11 features.

Related

noexcept visitation for std::variant

For some standard library classes, access to parts of their contents may legitimately fail. Usually you have the choice between some potentially throwing method an one that is marked noexcept. The latter spares the check on the precondition, so if you want to take the responsibility yourself, you can. This can be used under circumstances where using exceptions are not permitted or when fixing a performance bottleneck.
Example 1: std::vector element access:
std::vector<int> vec;
vec.at(n) // throws std::out_of_range
vec[n] // potentially UB, thus your own responsibility
Example 2: std::optional access:
std::optional<int> optn;
optn.value() // throws std::bad_optional_access
*optn // potentially UB, thus your own responsibility
Now on to std::variant. Directly accessing an alternative somewhat follows this pattern:
std::variant<std::string, int> var;
std::get<int>(var) // potentially throwing std::bad_variant_access
*std::get_if<int>(&var) // potentially UB, thus your own responsibility
But this time the signature changes, we have to inject * and &. The downside of this is that we don't get automatic move semantics. One more thing to keep in your mind...
But it gets worse if you have a look at std::visit(Visitor&& vis, Variants&&... vars). There is no noexcept alternative for it, although it only throws
if any variant in vars is valueless_by_exception.
This means for visiting variants you cannot choose to take the responsibility yourself, and if you have no choice and must avoid exceptions, you cannot visit std::variants at all with standard tooling! (apart from the terrible workaround of switching on variant::index())
To me, this looks like a pretty bad design oversight... or there a reason for this? And in case I'm right about the oversight, is there an initiative to fix this in the standard?
This means for visiting variants you cannot choose to take the responsibility yourself
Sure you can. The "valueless-by-exception" state can only happen if you assign or emplace a value into an existing variant. Furthermore, by definition, it can only happen if an exception is actually thrown during these processes. That is not a state that ever just happens to a random variant.
If you take responsibility to ensure that you either never emplace/assign to a variant, or that the types you use never throw in those circumstances, or that you respond to any exceptions from doing so in such a way that the variant that provoked it is not being talked to (ie: if bad_alloc is thrown, your application doesn't catch it; it just shuts down), then you don't have to care about this possibility.
Basically, if you're already coding to avoid exceptions, the non-noexcept status of visit is irrelevant. No variant will ever get into the "valueless-by-exception" unless an exception is thrown.

Validity of the copy-swap idiom

The inherent value of a copy-swap pattern for the operator = in a C++ data container is said to arise from
Code reuse and
Exception safety.
However the copy-swap idiom allocates much more memory than would otherwise be necessary, as data is not destroyed before the copy is created, increasing the chance of exceptions substantially. Given that this is the case, what is the point in claiming that it is a useful pattern?
Are there any other cases in which a copy operation could throw (that do not relate to the objects being copied) other than running out of memory?
The copy&swap idiom for the assignment operator yields the strong exception guarantee: the original value is untouched when building a new object would throw. When reusing memory allocated for the assigned to object the strong exception guarantee can typically only be achieved when none of the involved operation may throw. Especially when class templates are involved (as in std::vector<T>) there is generally no guarantee that none of the operations throws (of course, std::vector<T> only provides the basic exception guarantee upon assignment).
In general, copy assignments can throw for whatever reason. Most likely they shouldn't throw for other reasons than failures to allocate resources but there isn't anything in the CopyAssignable concept which prohibits the operation from throwing.
I found it quite common that copy assignments were not correctly implemented and would, e.g., violate the basic exception guarantee. The indicator that this is the case is when the assignment operator needs to check for self-assignment: if a self-assignment is actually needed (rather than just being there, sometimes defined as an "optimization" - how often do people do self-assignments...?), the code is almost certainly wrong, most likely not maintaining the basic exception guarantee. As a result I recommend using the copy&swap approach unless there is a very good reason not to although I'm aware that it uses more memory (although the problem is rarely that there isn't enough memory, at least not on the systems I'm working on, but rather that use of more memory may result in slower execution).

no-throw exception guarantee and stack overflow

There are several special functions which usually guarantee not to throw excpetions, e.g.:
Destructors
swap method
Consider the following swap implementation, as stated in this answer:
friend void swap(dumb_array& first, dumb_array& second)
{
using std::swap;
swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray); // What if stack overlow occurs here?
}
It uses two swap functions - for integer and for pointer. What if the second function will cause stack overflow? Objects will become corrupted. I guess it is not an std::exception, it is some kind of system exception, like Win32-exception. But now we cannot guarantee no-throwing, since we're calling a function.
But all authoritative sources just use swap like it's ok, no exceptions will ever be thrown here. Why?
In general you cannot handle running out of stack. The standard doesn't say what happens if you run out of stack, neither does it talk about what the stack is, how much is available, etc. OSes may let you control it at the time the executable is built or when it is run, all of which is fairly irrelevant if you're writing library code, since you have no control of how much stack the process has, or how much has already been used before the user calls into your library.
You can assume that stack overflow results in the OS doing something external to your program. A very simple OS might just let it go weird (undefined behavior), a serious OS might blow the process away, or if you're really unlucky it throws some implementation-defined exception. I actually don't know whether Windows offers an SEH exception for stack overflow, but if it does then it's probably best not to enable it.
If you're concerned, you can mark your swap function as noexcept. Then in a conforming implementation, any exception that tries to leave the function will cause the program to terminate(). That is to say, it fulfils the noexcept contract at the cost of taking out your program.
What if the second function will cause stack overflow?
Then your program is in an unrecoverable faulted state, and there is no practical way to handle the situation. Hopefully, the overflow has already caused a segmenation fault and terminated the program.
But now we cannot guarantee no-throwing
I've never encountered an implementation that would throw an exception in that state, and I'd be rather scared if it did.
But all authoritative sources just use swap like it's ok, no exceptions will ever be thrown here. Why?
The authoritative sources I've read (like this one, for example) don't "just use it like it's OK"; they say that if you have (for example) a non-throwing swap function, and a non-throwing destructor, then you can provide exception-safety guarantees from functions that use them.
It's useful to categorise functions according to their exception guarantees:
Basic: exceptions leave everything in a valid but unspecified state
Strong: exceptions leave the state unchanged
No-throw: no exceptions will be thrown.
Than a common approach to providing the "strong" guarantee is:
do the work that might throw on a temporary copy of the state
swap that copy with the live state (requiring a non-throwing swap operation)
destroy the old state (requiring a non-throwing destructor)
If you don't have a no-throw guarantee from those operations, then it's more difficult, and perhaps impossible, to provide a strong guarantee.

Which operations must not throw an exception in C++?

Today I learned that swap is not allowed to throw an exception in C++.
I also know that the following cannot throw exceptions either:
Destructors
Reading/writing primitive types
Are there any others?
Or perhaps, is there some sort of list that mentions everything that may not throw?
(Something more succinct than the standard itself, obviously.)
There is a great difference between cannot and should not. Operations on primitive types cannot throw, as many functions and member functions, including many operations in the standard library and/or many other libraries.
Now on the should not, you can include destructors and swap. Depending on how you implement them, they can actually throw, but you should avoid having destructors that throw, and in the case of swap, providing a swap operation with the no-throw guarantee is the simplest way of achieving the strong exception guarantee in your class, as you can copy aside, perform the operation on the copy, and then swap with the original.
But note that the language allows both destructors and swap to throw. swap can throw, in the simplest case if you do not overload it, then std::swap performs a copy construction, an assignment and a destruction, three operations that can each throw an exception (depending on your types).
The rules for destructors have changed in C++11, which means that a destructor without exception specification has an implicit noexcept specification which in turn means that if it threw an exception the runtime will call terminate, but you can change the exception specification to noexcept(false) and then the destructor can also throw.
At the end of the day, you cannot provide exception guarantees without understanding your code base, because pretty much every function in C++ is allowed to throw.
So this doesn't perfectly answer you question -- I searched for a bit out of my own curiosity -- but I believe that nothrow guaranteed functions/operators mostly originate from any C-style functions available in C++ as well as a few functions which are arbitrarily simple enough to give such a guarantee. In general it's not expected for C++ programs to provide this guarantee ( When should std::nothrow be used? ) and it's not even clear if such a guarantee buys you anything useful in code that makes regular use of exceptions. I could not find a comprehensive list of ALL C++ functions that are nothrow functions (please correct me if I missed a standard dictating this) other than listings of swap, destructors, and primitive manipulations. Also it seems fairly rare for a function that isn't fully defined in a library to require the user to implement a nothrows function.
So perhaps to get to the root of your question, you should mostly assume that anything can throw in C++ and take it as a simplification when you find something that absolutely cannot throw an exception. Writing exception safe code is much like writing bug free code -- it's harder than it sounds and honestly is oftentimes not worth the effort. Additionally there are many levels between exception unsafe code and strong nothrow functions. See this awesome answer about writing exception safe code as verification for these points: Do you (really) write exception safe code?. There's more information about exception safety at the boost site http://www.boost.org/community/exception_safety.html.
For code development, I've heard mixed opinions from Professors and coding experts on what should and shouldn't throw an exception and what guarantees such code should provide. But a fairly consistent assertion is that code which can easily throw an exception should be very clearly documented as such or indicate the thrown capability in the function definition (not always applicable to C++ alone). Functions that can possible throw an exception are much more common than functions that Never throw and knowing what exceptions can occur is very important. But guaranteeing that a function which divides one input by another will never throws a divide-by-0 exception can be quite unnecessary/unwanted. Thus nothrow can be reassuring, but not necessary or always useful for safe code execution.
In response to comments on the original question:
People will sometimes state that exception throwing constructors are evil when throw in containers or in general and that two-step initialization and is_valid checks should always be used. However, if a constructor fails it's oftentimes unfixable or in a uniquely bad state, otherwise the constructor would have resolved the problem in the first place. Checking if the object is valid is as difficult as putting a try catch block around initialization code for objects you know have a decent chance of throwing an exception. So which is correct? Usually whichever was used in the rest of the code base, or your personal preference. I prefer exception based code as it gives me a feeling of more flexibility without a ton of baggage code of checking every object for validity (others might disagree).
Where does this leave you original question and the extensions listed in the comments? Well, from the sources provided and my own experience worrying about nothrow functions in an "Exception Safety" perspective of C++ is oftentimes the wrong approach to handling code development. Instead keep in mind the functions you know might reasonably throw an exception and handle those cases appropriately. This is usually involving IO operations where you don't have full control over what would trigger the exception. If you get an exception that you never expected or didn't think possible, then you have a bug in your logic (or your assumptions about the function uses) and you'll need to fix the source code to adapt. Trying to make guarantees about code that is non-trivial (and sometimes even then) is like saying a sever will never crash -- it might be very stable, but you'll probably not be 100% sure.
If you want the in-exhaustive-detail answer to this question go to http://exceptionsafecode.com/ and either watch the 85 min video that covers just C++03 or the three hour (in two parts) video that covers both C++03 and C++11.
When writing Exception-Safe code, we assume all functions throw, unless we know different.
In short,
*) Fundamental types (including arrays of and pointers to) can be assigned to and from and used with operations that don't involve user defined operators (math using only fundamental integers and floating point values for example). Note that division by zero (or any expression whose result is not mathematically defined) is undefined behavior and may or may not throw depending on the implementation.
*) Destructors: There is nothing conceptually wrong with destructors that emit exceptions, nor does the standard prohibited them. However, good coding guidelines usually prohibit them because the language doesn't support this scenario very well. (For example, if destructors of objects in STL containers throw, the behavior is undefined.)
*) Using swap() is an important technique for providing the strong exception guarantee, but only if swap() is non-throwing. In general, we can't assume that swap() is non-throwing, but the video covers how to create a non-throwing swap for your User-Defined Types in both C++03 and C++11.
*) C++11 introduces move semantics and move operations. In C++11, swap() is implemented using move semantics and the situation with move operations is similar to the situation with swap(). We cannot assume that move operations do not throw, but we can generally create non-throwing move operations for the User-Defined Types that we create (and they are provided for standard library types). If we provide non-throwing move operations in C++11, we get non-throwing swap() for free, but we may choose to implement our own swap() any way for performance purposes. Again, this is cover in detail in the video.
*) C++11 introduces the noexcept operator and function decorator. (The "throw ()" specification from Classic C++ is now deprecated.) It also provides for function introspection so that code can be written to handle situations differently depending on whether or not non-throwing operations exist.
In addition to the videos, the exceptionsafecode.com website has a bibliography of books and articles about exceptions which needs to be updated for C++11.

Is it abusive to implement the "execute-around" idiom with scoped objects?

Should scoped objects (with complimentary logic implemented in constructor and destructor) only be used for resource cleanup (RAII)?
Or can I use it to implement certain aspects of the application's logic?
A while ago I asked about Function hooking in C++. It turns out that Bjarne addressed this problem and the solution he proposes is to create a proxy object that implements operator-> and allocates a scoped object there. The "before" and "after" are implemented in the scoped object's constructor and destructor respectively.
The problem is that destructors should not throw. So you have to wrap the destructor in a try { /* ... */ } catch(...) { /*empty*/ } block. This severely limits the ability to handle errors in the "after" code.
Should scoped objects only be used to cleanup resources or can I use it for more than that? Where do I draw the line?
If you pedantically consider the definition of RAII, anything you do using scoping rules and destructor invocation that doesn't involve resource deallocation simply isn't RAII.
But, who cares? Maybe what you're really trying to ask is,
I want X to happen every time I leave function Y. Is it
abusive to use the same scoping rules and destructor invocation that
RAII uses in C++ if X isn't resource deallocation?
I say, no. Scratch that, I say heck no. In fact, from a code clarity point of view, it might be better to use destructor calls to execute a block of code if you have multiple return points or possibly exceptions. I would document the fact that your object is doing something non-obvious on destruction, but this can be a simple comment at the point of instantiation.
Where do you draw the line? I think the KISS principle can guide you here. You could probably write your entire program in the body of a destructor, but that would be abusive. Your Spidey Sense will tell you that is a Bad Idea, anyway. Keep your code as simple as possible, but not simpler. If the most natural way to express certain functionality is in the body of a destructor, then express it in the body of a destructor.
You want a scenario where, guaranteed, the suffix is always done. That sounds exactly like the job of RAII to me. I however would not necessarily actually write it that way. I'd rather use a method chain of templated member functions.
I think with C++11 you can semi-safely allow the suffix() call to throw. The strict rule isn't "never throw from a destructor", although that's good advice, instead the rule is:
never throw an exception from a destructor while processing another
exception
In the destructor you can now use std::current_exception, which I think verifies the "while processing another exception" element of the destructor+exception rule. With this you could do:
~Call_proxy() {
if (std::current_exception()) {
try {
suffix();
}
catch(...) {
// Not good, but not fatal perhaps?
// Just don't rethrow and you're ok
}
}
else {
suffix();
}
}
I'm not sure if that's actually a good idea in practise though, you have a hard problem to deal with if it throws during the throwing of another exception still.
As for the "is it abuse" I don't think it's abusive anymore than metaprogramming or writing a?b:c instead of a full blown if statement if it's the right tool for the job! It's not subverting any language rules, simply exploiting them, within the letter of the law. The real issue is the predictability of the behaviour to readers unfamiliar with the code and the long term maintainability, but that's an issue for all designs.