Does C++ Support 'Finally' Blocks? (And What's This 'Raii' I Keep Hearing About)

How to do try/finally in C++ when RAII is not possible?

Make Resource a moveable type. Give it move construction/assignment. Then, your Initialize method can look like this:

void ResourceConsumer::Initialize(std::string name)
{
//Create the resource *first*.
Resource res(name);

//Move the newly-created resource into the current one.
_resource = std::move(res);
}

Note that in this example, there is no need for exception handling logic. It all works itself out. By creating the new resource first, if that creation throws an exception, then we keep the previously created resource (if any). That provides the strong exception guarantee: in the event of an exception, the state of the object is preserved exactly as it was before the exception.

And note that there is no need for explicit try and catch blocks. RAII just works.

Your Resource move operations would be like this:

class Resource {
public:
Resource() = default;

Resource(std::string name) : _resource(::GetResource(name))
{
if(_resource == NULL) throw "Error";
}

Resource(Resource &&res) noexcept : _resource(res._resource)
{
res._resource = NULL;
}

Resource &operator=(Resource &&res) noexcept
{
if(&res != this)
{
reset();
_resource = res._resource;
res._resource = NULL;
}
}

~Resource()
{
reset();
}

operator HANDLE() const { return _resource; }

private:
HANDLE _resource = NULL;

void reset() noexcept
{
if (_resource != NULL)
{
CloseHandle(_resource);
_resource = NULL;
}
}
};

Implementation of finally in C++

The standard answer is to use some variant of resource-allocation-is-initialization abbreviated RAII. Basically you construct a variable that has the same scope as the block that would be inside the block before the finally, then do the work in the finally block inside the objects destructor.

try {
// Some work
}
finally {
// Cleanup code
}

becomes

class Cleanup
{
public:
~Cleanup()
{
// Cleanup code
}
}

Cleanup cleanupObj;

// Some work.

This looks terribly inconvenient, but usually there's a pre-existing object that will do the clean up for you. In your case, it looks like you want to destruct the object in the finally block, which means a smart or unique pointer will do what you want:

std::unique_ptr<Object> obj(new Object());

or modern C++

auto obj = std::make_unique<Object>();

No matter which exceptions are thrown, the object will be destructed. Getting back to RAII, in this case the resource allocation is allocating the memory for the Object and constructing it and the initialization is the initialization of the unique_ptr.

Are C++ `try`/`catch` blocks the same as other blocks, regarding RAII?

"… will the RAII instance work the same way if I do this: ..."

Sure they will. The RAII instance will go out of scope and the destructors are called before the catch, if an exception is thrown.

Also this will work for any upper levels, that are calling your function if you just throw and there aren't any try/catch blocks. That's called stack unwinding.

RAII Failure - Why Does this C++ Code Leak? - throw in ctor in try block prevents dtor

In this case, your Holder object h is not fully constructued, that is that h's constructor had not finished its construction before the stack unwinding process had begun.

C++11 15.2 Constructors and destructors
(2)

An object of any storage duration whose initialization or destruction
is terminated by an exception will have destructors executed for all
of its fully constructed subobjects (excluding the variant members of
a union-like class), that is, for subobjects for which the principal
constructor (12.6.2) has completed execution and the destructor has
not yet begun execution.

Fastest `finally` for C++

You can implement Finally without type erasure and overhead of std::function:

template <typename F>
class Finally {
F f;
public:
template <typename Func>
Finally(Func&& func) : f(std::forward<Func>(func)) {}
~Finally() { f(); }

Finally(const Finally&) = delete;
Finally(Finally&&) = delete;
Finally& operator =(const Finally&) = delete;
Finally& operator =(Finally&&) = delete;
};

template <typename F>
Finally<F> make_finally(F&& f)
{
return { std::forward<F>(f) };
}

And use it like:

auto&& doFinally = make_finally([&] { var++; });

Demo

Is there a favored idiom for mimicing Java's try/finally in C++?

By making effective use of destructors. When an exception is thrown in a try block, any object created within it will be destroyed immediately (and hence its destructor called).

This is different from Java where you have no idea when an object's finalizer will be called.

UPDATE: Straight from the horse's mouth: Why doesn't C++ provide a "finally" construct?



Related Topics



Leave a reply



Submit