Resource Acquisition is Initialization (RAII) is commonly used in C++ to manage the lifetimes of resources which require some manner of cleanup code at the end of their lifetime, from deleteing newed pointers to releasing file handles.
How do I quickly and easily use RAII to manage the lifetime of a resource I acquire from a C-style API?
In my case, I want to use RAII to automatically execute a cleanup function from a C-style API when the variable holding the C-style resource it releases goes out of scope. I don't really need additional resource wrapping beyond that, and I'd like to minimize the code overhead of using RAII here. Is there a simple way to use RAII to manage resources from a C-style API?
How to encapsulate C api into RAII C++ classes? is related, but I don't believe it's a duplicate--that question is regarding more complete encapsulation, while this question is about minimal code to get the benefits of RAII.
There is an easy way to use RAII to manage resources from a C-style interface: the standard library's smart pointers, which come in two flavors:
std::unique_ptrfor resources with a single owner and the team ofstd::shared_ptrandstd::weak_ptrfor shared resources. If you're having trouble deciding which your resource is, this Q&A should help you decide. Accessing the raw pointer a smart pointer is managing is as easy as calling itsgetmember function.If you want simple, scope-based resource management,
std::unique_ptris an excellent tool for the job. It's designed for minimal overhead, and is easy to set up to use custom destruction logic. So easy, in fact, that you can do it when you declare the resource variable:acquireResourceexecutes where you call it, at the start of the variable's lifetime.releaseResourcewill execute at the end of the variable's lifetime, usually when it goes out of scope.1 Don't believe me? you can see it in action on Coliru, where I've provided some dummy implementations to the acquire and release functions so you can see it happening.You can do much the same with
std::shared_ptr, if you require that brand of resource lifetime instead:Now, both of these are all well and good, but the standard library has
std::make_unique2 andstd::make_sharedand one of the reasons is further exception-safety.GotW #56 mentions that the evaluation of arguments to a function are unordered, which means if you have a function that takes your shiny new
std::unique_ptrtype and some resource that might throw on construction, supplying a that resource to a function call like this:means that the instructions might be ordered like this:
acquireResourceThrowsOnConstructionstd::unique_ptrfrom resource pointerand that our precious C interface resource won't be cleaned up properly if step 2 throws.
Again as mentioned in GotW #56, there's actually a relatively simple way to deal with the exception safety problem. Unlike expression evaluations in function arguments, function evaluations can't be interleaved. So if we acquire a resource and give it to a
unique_ptrinside a function, we'll be guaranteed no tricky business will happen to leak our resource whenThrowsOnConstructionthrows on construction. We can't usestd::make_unique, because it returns astd::unique_ptrwith a default deleter, and we want our own custom flavor of deleter. We also want to specify our resource acquisition function, since it can't be deduced from the type without additional code. Implementing such a thing is simple enough with the power of templates:3Live on Coliru
you can use it like this:
and call
funcworry-free, like this:The compiler can't take the construction of
ThrowsOnConstructionand stick it between the call toacquireResourceand the construction of theunique_ptr, so you're good.The
shared_ptrequivalent is similarly simple: just swap out thestd::unique_ptr<T, Deletion>return value withstd::shared_ptr<T>, and change the name to indicate a shared resource:4Use is once again similar to the
unique_ptrversion:and
Edit:
As mentioned in the comments, there's a further improvement you can make to the use of
std::unique_ptr: specifying the deletion mechanism at compile time so theunique_ptrdoesn't need to carry a function pointer to the deleter when it's moved around the program. Making a stateless deleter templated on the function pointer you're using requires four lines of code, placed beforemake_c_handler:Then you can modify
make_c_handlerlike so:The usage syntax then changes slightly, to
Live on Coliru
make_c_shared_handlerwould not benefit from changing to a templated deleter, asshared_ptrdoes not carry deleter information available at compile time.1. If the value of the smart pointer is
nullptrwhen it's destructed, it won't call the associated function, which is quite nice for libraries which handle resource release calls with null pointers as error conditions, like SDL.2.
std::make_uniquewas only included in the library in C++14, so if you're using C++11 you might want to implement your own--it's very helpful even if it's not quite what you want here.3. This (and the
std::make_uniqueimplementation linked in 2) depend on variadic templates. If you're using VS2012 or VS2010, which have limited C++11 support, you don't have access to variadic templates. The implementation ofstd::make_sharedin those versions was instead made with individual overloads for each argument number and specialization combination. Make of that what you will.4.
std::make_sharedactually has more complex machinery than this, but it requires actually knowing how big an object of the type will be. We don't have that guarantee since we're working with a C-style interface and may only have a forward declaration of our resource type, so we won't be worrying about it here.