What the OP describes here is a common C++ pattern known as "scope guard" (check "Solution 4" in http://drdobbs.com/184403758). It was possible to implement it in C++03 with some tricks but C++11 lambdas make it much easier.
This is a very nice small implementation but, as always with C++, devil is in the details:
* Creation of an std::function needs a dynamic allocation, so if the allocation fails an exception will be thrown and if the finally is guarding a resource, the resource will be leaked
* std::function has a non-negligible calling overhead, hence this should not be used in performance-sensitive code
* Checking a condition inside the finally clause is not very elegant, a better idiom in C++ is to support dismissing a scope guard.
* The finallyClause may throw an exception, and since it is called in a destructor this is generally considered a bad idea. I don't know what could happen in this case, but some scope guard implementations I've seen catch the exception and explicitly call std::terminate(). I guess this is for performance reasons, because the destructor can be declared nothrow.
Here there is a more complex implementation, which addresses most of the corner cases:
I get your points. In general, the use cases for this pattern are not production code. The post points out this is not a good way of handling resource and that the example is about debugging. It would be up to the implementer to ensure the finally clause does not throw and exception or to catch it (I think there is something about nesting).
As for memory allocation failures, my experience is that depends a lot on the platform. It is a common misconception that dynamic memory is likely to run out and therefore we need to worry about that but not automatic memory. However, I have seen stack overflow many many many more times than a genuine 'out of memory'. Further, if an application has actually run out of memory so badly that a allocation of the closure's internal storage barfs then I suspect it cannot be retrieved. Naturally this does not apply if your platform has a very low heap size (e.g. realtime hardware or some such).
I’ve done this before, and this version has a significant flaw: it is not exception-safe. The destructor should not read as it does:
~finally() {
finallyClause();
}
This allows exceptions thrown by finallyClause() to propagate out of the destructor, which, if an exception is already being thrown, will result in a call to std::terminate(), causing the application to die horrifically. Because C++ kinda-should’ve-but-really-doesn’t have checked exceptions, we must simply discard them:
That is really nice. Could you expand on how this works a little "You can also avoid problems with exceptions and std::function’s dynamic allocation by using a template instead:"
std::function can store function pointers (and member function pointers) by value, but using a function-like object that defines operator()—or by extension std::bind()—requires dynamic allocation, which can throw. The templated version, on the other hand, always stores its parameter by value. Come to think of it, you could also make the constructor take an rvalue reference (F&&) to use move construction instead of copy construction.
... because the compiler knows how big the function like object will be at compile time when the template is used but it only knows it at runtime if the class is used. ...
> Because C++ kinda-should’ve-but-really-doesn’t have checked exceptions, we must simply discard them:
This would make the error pass silently, possibly leaving the program in an inconsistent state. It is probably better to call std::terminate() instead, which is what C++ does when an exception is thrown when another exception is active.
Which kinda goes to my point about out of memory exceptions - they generally end up being fatal sooner or later. Trying to catch them just results in memory running out somewhere else. The only place you can deal with them is if the code is doing something you expect to cause memory issues because it is consuming an atypically large amount of memory (like a cache or some such) which you can get away with shrinking.
You’re quite right that ignoring the (programmer) error is too lax, but std::terminate() is far too extreme. This is perhaps a situation for std::nested_exception:
No, if the finally clause is called while an exception is active, the runtime will call std::terminate(). This is why it is bad practice to throw exceptions in destructors.
Hence, in most cases where the scope guard pattern is used (to guarantee exception safety), your code is basically equivalent to calling std::terminate().
Alright, yeah, no exception should escape the finally destructor. After so many years of using it, I should know better than to write C++.
But anyway, the problem is still programmer error. Just as you shouldn’t throw an exception from a destructor, you shouldn’t throw an exception from a “finally”, because it is, by definition, run in a destructor. So, sure, calling std::terminate() explicitly makes about as much sense as anything, but I’d prefer to ignore (and perhaps log) such erroneous exceptions, for reasons of stability.
12 comments
[ 156 ms ] story [ 302 ms ] threadThis is a very nice small implementation but, as always with C++, devil is in the details:
* Creation of an std::function needs a dynamic allocation, so if the allocation fails an exception will be thrown and if the finally is guarding a resource, the resource will be leaked
* std::function has a non-negligible calling overhead, hence this should not be used in performance-sensitive code
* Checking a condition inside the finally clause is not very elegant, a better idiom in C++ is to support dismissing a scope guard.
* The finallyClause may throw an exception, and since it is called in a destructor this is generally considered a bad idea. I don't know what could happen in this case, but some scope guard implementations I've seen catch the exception and explicitly call std::terminate(). I guess this is for performance reasons, because the destructor can be declared nothrow.
Here there is a more complex implementation, which addresses most of the corner cases:
http://channel9.msdn.com/Shows/Going+Deep/C9-Lectures-Stepha...
As for memory allocation failures, my experience is that depends a lot on the platform. It is a common misconception that dynamic memory is likely to run out and therefore we need to worry about that but not automatic memory. However, I have seen stack overflow many many many more times than a genuine 'out of memory'. Further, if an application has actually run out of memory so badly that a allocation of the closure's internal storage barfs then I suspect it cannot be retrieved. Naturally this does not apply if your platform has a very low heap size (e.g. realtime hardware or some such).
I am guessing this is the situation?
This would make the error pass silently, possibly leaving the program in an inconsistent state. It is probably better to call std::terminate() instead, which is what C++ does when an exception is thrown when another exception is active.
Hence, in most cases where the scope guard pattern is used (to guarantee exception safety), your code is basically equivalent to calling std::terminate().
But anyway, the problem is still programmer error. Just as you shouldn’t throw an exception from a destructor, you shouldn’t throw an exception from a “finally”, because it is, by definition, run in a destructor. So, sure, calling std::terminate() explicitly makes about as much sense as anything, but I’d prefer to ignore (and perhaps log) such erroneous exceptions, for reasons of stability.