期望指针地址的shared_ptr自定义删除器

3
许多C API提供释放函数,这些函数使用**p参数来释放资源并将指针设置为NULL
我想用自定义删除器的boost::shared_ptr包装此类C API调用。
以下是FFMPEG示例:
AVFrame* frame = av_frame_alloc(); // allocate resource
// Do stuff with frame
av_frame_free(&frame)              // free resource

为了利用RAII,我可以这样重写:
AVFrame* frame = av_frame_alloc();
boost::shared_ptr<AVFrame*> frame_releaser(&frame, av_frame_free);
// Do stuff with frame

请注意,shared_ptr<> 是类型为 <AVFrame*> 而不是指针类型的 <AVFrame>
这种方法要求我分别保存资源和释放器,这有几个缺点:
  1. frame 可能会被外部更改,导致泄漏。
  2. 它需要两个变量而不是一个,这使代码更容易出错。

我想使用单个 shared_ptr 变量来同时保存资源并在需要时释放它。

boost::ref 的精神相似,我正在寻找编写或使用通用的 address_of_arg_wrapper 用于删除器,以便我可以编写类似以下内容的代码:

boost::shared_ptr<AVFrame> frame_handle(av_frame_alloc(), address_of_arg_wrapper(av_frame_free));
// Do stuff with frame_handle.get()

或者
boost::shared_ptr<AVFrame> frame_handle(av_frame_alloc(), address_of_arg_wrapper<av_frame_free>());
// Do stuff with frame_handle.get()

重要的是,包装器必须是通用的,并接受任何指针(ref)类型,以便可以与任何此类API函数一起使用。
我也不想指定类型。

Boost是否有这样的实用程序?
如果没有,那么如何编写这样一个通用的函数对象?

编辑 - 为完整性提供解决方案:

此解决方案基于@R. Martinho Fernandes下面的答案

  1. 它包括一个模板函数来创建模板函数对象,因此无需指定模板类型。
  2. 代码依赖于boost::decay。对于我测试的简单情况,只持有一个Fun fun;成员的版本也起作用。
  3. 我将名称更改为arg_ref_adaptor()。欢迎提出更好的名称建议!

以下是代码:

#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); }

使用方法:

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.

我认为你应该使用typedef boost::shared_ptr,因为C++11有std::shared_ptr,当升级编译器时,你会想要尽量减少迁移工作。 - Bathsheba
@Bathsheba 我会保持命名空间的清晰。将boost::shared_ptr批量替换为std::shared_ptr只需要一秒钟即可完成,而混合使用命名空间是很危险的。 - billz
@billz:很酷啊;只要我们记住迁移就好了。 - Bathsheba
我在考虑是否要提到boost::shared_ptrstd::shared_ptr,但我决定这并不是重点。使用Boost使人们明显地看到,来自Boost的解决方案(例如boost::ref)是可以接受的。 - Adi Shavit
1个回答

3

将指针设置为null是毫无意义的,因为在销毁后shared_ptr已经保证指针不会再次可见。因此,代码只需要传递一个地址以满足av_frame_free函数的要求。我建议简单地编写一个函数对象,它可以传递其参数的地址。

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;
};

在C++11中,可以使用lambda表达式:
[](AVFrame* ptr) { av_frame_free(&ptr); }

是的!这就是我尝试做的事情,但是没有完全做到 :-)。关于 null,那确实是一个 API 的问题而不是要求。为什么使用 boost::decay 而不只是 Fun fun?此外,Boost 中已经有类似的东西了吗? - Adi Shavit
@AdiShavit: boost::decay 用于将 Fun 表示为 void(),而不必使用 void(&)()。这提供了与 boost::function 用户期望的相同语法。 - Tanner Sansbury
@Tanner Sansbury:抱歉,我不明白你的答案。 - Adi Shavit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接