Many C APIs provide release functions that take a **p
which besides releasing the resource also sets the pointer to NULL
.
I want to wrap such C API calls with a boost::shared_ptr
with a custom deleter.
Here's an example with FFMPEG:
AVFrame* frame = av_frame_alloc(); // allocate resource
// Do stuff with frame
av_frame_free(&frame) // free resource
To leverage RAII, I can rewrite this like so:
AVFrame* frame = av_frame_alloc();
boost::shared_ptr<AVFrame*> frame_releaser(&frame, av_frame_free);
// Do stuff with frame
Note that the shared_ptr<>
is of type <AVFrame*>
and not <AVFrame>
as the pointer type.
This approach requires me to hold the resource and the releaser separately, which has several drawbacks:
frame
may be changed externally causing a leak.I'd like to use a single shared_ptr
variable to both hold the resource and release it when needed.
In the spirit of boost::ref
, I'm looking to write or use a generic address_of_arg_wrapper
for the deleter that will allow me to write somthing like this:
boost::shared_ptr<AVFrame> frame_handle(av_frame_alloc(), address_of_arg_wrapper(av_frame_free));
// Do stuff with frame_handle.get()
or
boost::shared_ptr<AVFrame> frame_handle(av_frame_alloc(), address_of_arg_wrapper<av_frame_free>());
// Do stuff with frame_handle.get()
It is important that the wrapper be generic and accept any pointer (ref) type, so it can be used with any such API functions.
I also do not want to specify the types.
Does Boost have such a utility?
If not, then how can one write such a generic functor?
EDIT - Solution for completeness:
This solution is based on @R. Martinho Fernandes's answer below.
boost::decay
. A version that just holds a Fun fun;
member also worked for simple cases I tested.arg_ref_adaptor()
. Better name suggestions are welcome!Here's the code:
#include <boost\type_traits\decay.hpp>
//////////////////////////////////////////////////////////////////////////
// Given a function or callable type 'fun', returns an object with
// a void operator(P ptr) that calls fun(&ptr)
// Useful for passing C API function as deleters to shared_ptr<> which require ** instead of *.
template <typename Fun>
struct arg_ref_adaptor_functor
{
public:
arg_ref_adaptor_functor(Fun fun): fun(fun) {}
template <typename P>
void operator()(P ptr)
{ fun(&ptr); }
private:
typename boost::decay<Fun>::type fun;
};
template <typename Fun>
inline arg_ref_adaptor_functor<Fun> arg_ref_adaptor(Fun fun)
{ return arg_ref_adaptor_functor<Fun>(fun); }
Usage:
boost::shared_ptr<AVFrame> frame_handle(::av_frame_alloc()
,arg_ref_adaptor(::av_frame_free));
// Do stuff with frame_handle.get()
// The resource will be released using ::av_frame_free() when frame_handle
// goes out of scope.
Setting the pointer to null is pointless since the shared_ptr
already guarantees the pointer will never be visible again after destruction. So the code only needs to pass along an address to please the av_frame_free
function. I suggest simply writing a function object that passes along the address of its argument.
template <typename Fun>
struct address_of_arg_wrapper {
public:
address_of_arg_wrapper(Fun fun) : fun(fun) {}
template <typename P>
void operator()(P ptr) {
fun(&ptr);
}
private:
typename boost::decay<Fun>::type fun;
};
In C++11 a lambda can be used:
[](AVFrame* ptr) { av_frame_free(&ptr); }