如何为包装需要 2 个参数的 c 函数的 unique_ptr class 成员创建自定义删除器?

How do you create a custom deleter for a unique_ptr class member that wraps a c function which requires 2 arguments?

我正在尝试使用 mupdf 创建一个程序(在 Qt 中),它允许我将文档的对象列为列表,并允许我 select 渲染/不渲染哪些对象。由于 Qt 是 C++,而且我对它更熟悉,所以我尝试将 mupdf 中定义的结构包装在 C++ 类.

现在我的问题是 - 你在 mupdf 中做的第一件事就是创建一个全局上下文,它被传递给所有东西,包括清理和删除结构的函数。

我熟悉创建一个重载的对象 operator(),很像:

struct ContextDeleter
{
    inline void operator()(fz_context* ctx)
    {
        fz_drop_context(ctx);
    }
};

然后我可以交给 unique_ptr -

std::unique_ptr<fz_context, ContextDeleter> ctxPtr;

我想不通的是如何用像这样的函数做同样的事情:

fz_drop_page(ctx, page);

即:

struct PageDeleter
{
     inline void operator()(fz_context* ctx, fz_page* pg)
     {
          fz_drop_page(ctx, pg);
     }
}

这显然是不正确的,但正是我正在努力实现的目标。

如何为 unique_ptr 创建包含 2 个参数(在本例中为必要的上下文指针)的删除器?有没有办法让 unique_ptr 知道删除页面的上下文指针(在本例中)?或者(我曾想过)我是否需要创建一些东西来包装 unique_ptr 以便我可以稍后以某种方式将其删除的上下文(还没有完全考虑清楚)。

我看过这里的例子:

How do I use a custom deleter with a std::unique_ptr member?

Wrapping of C-code with a unique_ptr and custom deleter

但我不知道如何让它们在我的情况下工作。

fz_context * 存储在删除器中,并将该删除器的实例传递给包含 fz_page *

unique_ptr
struct PageDeleter
{
    explicit PageDeleter(fz_context *ctx)
    : ctx(ctx)
    {}
    void operator()(fz_page* page) const
    {
        fz_drop_page(ctx, page);
    }
    fz_context *ctx;
};

unique_ptr构造为

fz_context *ctx = // get the fz_context
fz_page *page = // get the fz_page

PageDeleter page_del(ctx);
std::unique_ptr<fz_page, PageDeleter> pagePtr(page, page_del);

为方便起见,您可以将所有这些包装在一个 make_unique_fz_page 函数中。

尝试使用捕获 fz_context* 值的 lambda,而不是将其存储在自定义删除器中:

fz_context *ctx = ...;
fz_page *page = ...;

std::unique_ptr<fz_page, std::function<void(fz_page*)>> pagePtr(page, [ctx](fz_page *page){ fz_drop_page(ctx, page); });

或者:

fz_context *ctx = ...;
fz_page *page = ...;

auto deleter = [ctx](fz_page *page){ fz_drop_page(ctx, page); };
std::unique_ptr<fz_page, decltype(deleter)> pagePtr(page, deleter);

直接将 std::unique_ptr 构造函数与删除器一起使用对于 lambda 表达式来说有点麻烦,因为您无法命名类型。但是您可以使用这样的模板推断类型:

template <typename T, typename D, typename ...Args>
std::unique_ptr<T,D> make_unique_with_deleter( D deleter, Args&&...args )
{
    return std::unique_ptr<T,D>(
        new T( std::forward<Args>(args)... ), 
        std::move( deleter ) );
}

这样,您可以直接在一个地方创建一个带有 lambda 删除器的 unique_ptr。这是一个例子:

int main()
{
    const auto ctx = make_unique_with_deleter<fz_context>(
        []( fz_context * ctx ){ fz_drop_context( ctx ); }, 
        nullptr, nullptr, FZ_STORE_UNLIMITED );
    const auto page = make_unique_with_deleter<fz_page>( 
        [ctx]( fz_page * pg ){ fz_drop_page( ctx.get(), pg ); }, 
        ctx.get(), 1000000 );
    // and so forth ...
}