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
C++0X Has No Semaphores? How to Synchronize Threads
Which Is Faster: Stack Allocation or Heap Allocation
Optimizing Away a "While(1);" in C++0X
Significance of Ios_Base::Sync_With_Stdio(False); Cin.Tie(Null);
Do I Need to Cast to Unsigned Char Before Calling Toupper(), Tolower(), Et Al.
How to Convert Wstring into String
Constructor Initialization-List Evaluation Order
What Are All the Member-Functions Created by Compiler For a Class? Does That Happen All the Time
Good Input Validation Loop Using Cin - C++
Why Is Transposing a Matrix of 512X512 Much Slower Than Transposing a Matrix of 513X513
How to Create an Std::Function from a Move-Capturing Lambda Expression
Why Do Objects of the Same Class Have Access to Each Other'S Private Data
Rotating a Point About Another Point (2D)
How to Make a Simple C++ Makefile
Regular Expression to Detect Semi-Colon Terminated C++ For & While Loops