【问题标题】:Custom deleter for shared_ptr that expects an address of pointer需要指针地址的 shared_ptr 的自定义删除器
【发布时间】:2013-07-01 10:48:40
【问题描述】:

许多 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&lt;&gt;&lt;AVFrame*&gt; 类型,而不是&lt;AVFrame&gt; 作为指针类型。
这种方法需要我分别持有资源和释放者,这有几个缺点:

  1. frame 可能会在外部更改导致泄漏。
  2. 它需要 2 个变量而不是 1 个变量,这使得代码更容易出错。

我想使用单个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's answer below

  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.

【问题讨论】:

  • IMO 你应该 typedef boost::shared_ptr 因为 C++11 有 std::shared_ptr,并且你希望在升级编译器时尽量减少迁移工作。
  • @Bathsheba 我会保持命名空间清晰。将 boost::shared_ptr 批量替换为 std::shared_ptr 可以在一秒钟内完成。而混合命名空间是危险的。
  • @billz:这很酷;只要我们牢记迁移。
  • 我正在考虑是否提及boost::shared_ptrstd::shared_ptr,但我认为这不是重点。使用 Boost 很明显,来自 Boost 的解决方案(a-la boost::ref 是可以接受的)。

标签: c++ boost shared-ptr


【解决方案1】:

将指针设置为 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 中不是已经有类似的东西了吗?
  • @AdiShavit: boost::decay 用于允许将Fun 表示为void(),而不必使用void(&amp;)()。这提供了boost::function 的用户所期望的相同语法。
  • @Tanner Sansbury:对不起,我不明白你的回答。
猜你喜欢
  • 2012-07-08
  • 2020-06-19
  • 1970-01-01
  • 1970-01-01
  • 2016-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多