Как создать пользовательское средство удаления для члена класса unique_ptr, которое обертывает функцию c, требующую 2 аргумента?

Я пытаюсь использовать mupdf для создания программы (в Qt), которая позволит мне перечислить объекты документа в виде списка и позволит мне выбрать, какие объекты отображать/не отображать. Поскольку Qt — это С++, и мне с ним удобнее, я пытаюсь обернуть структуры, определенные в mupdf, в классы С++.

Прямо сейчас моя проблема заключается в следующем: одна из первых вещей, которые вы делаете в 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);

ie:

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

Это, очевидно, неправильно, но это то, чего я пытаюсь достичь.

Как я могу создать средство удаления для unique_ptr, которое включает 2 аргумента (в данном случае необходимый указатель контекста)? Есть ли способ сделать unique_ptr осведомленным об указателе контекста для удаления страницы (в этом примере)? Или (у меня была одна мысль) мне нужно создать что-то, что обертывает unique_ptr, чтобы я мог каким-то образом передать ему контекст для удаления позже (еще не полностью обдумал это).

Я видел примеры здесь:

Как использовать пользовательское средство удаления с член std::unique_ptr?

а также

Обертка C-кода с уникальным_ptr и пользовательским удаление

но я не могу понять, как заставить их работать в моем случае.


person tenspd137    schedule 29.12.2015    source источник


Ответы (3)


Сохраните fz_context * в средстве удаления и передайте экземпляр этого средства удаления unique_ptr, содержащему fz_page *.

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 для удобства.

person Praetorian    schedule 29.12.2015
comment
К сожалению, в C++ нет концепции аргументов деструктора: если бы она была, вы могли бы одним прикосновением уменьшить состояние ваших уникальных указателей. Было бы странной и сложной языковой функцией добавить метинкс. - person Yakk - Adam Nevraumont; 30.12.2015
comment
OP ищет реализацию PageDeleter, но вместо этого в вашем примере реализована ContextDeleter, для которой у OP уже есть рабочая реализация. Я обновил ваш ответ, чтобы поменять местами значения. - person Remy Lebeau; 30.12.2015

Использование конструктора std::unique_ptr непосредственно с deleter немного раздражает с лямбда-выражениями, потому что вы не можете назвать тип. Но вы можете вывести тип, используя такие шаблоны:

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

Таким образом, вы создаете 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 ...
}
person Ralph Tandetzky    schedule 29.12.2015

Попробуйте использовать лямбду, которая захватывает значение fz_context* вместо того, чтобы сохранять его в пользовательском удалении:

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

Or:

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);
person Remy Lebeau    schedule 29.12.2015