Управление жизненным циклом типа C с помощью boost's shared_ptr? - PullRequest
2 голосов
/ 16 февраля 2012

У меня есть вопрос, похожий на Как управлять временем жизни объекта с помощью интеллектуальных указателей библиотеки Boost? , но, в моем случае, «объект» вообще не объект C ++, а непрозрачный тип вернулся / потерял сознание от C API. Тип не имеет семантики указателей, т.е. нет разыменования; однако он передается в качестве аргумента другим функциям в C API. Тип также имеет определенный close API, который должен вызываться для очистки внутренних ресурсов.

Итак, у меня есть C API, что-то вроде

opaque_legacy_type_t x;
XXopen(..., &x); // allocates/opens resource and fills out 'x' to be used later
XXdoSomethingWithResource(x, ...); // do something with resources related to 'x'
...more actions...
XXclose(x); // closes and cleans up resources related to 'x'

По разным причинам в моем коде на C ++ я хотел бы управлять «экземплярами» opaque_legacy_type_t так же, как я управлял бы экземплярами объекта, выделенными в куче, т.е. Кажется, что shared_ptr предлагает достаточно, чтобы я мог управлять вызовом XXclose, выполнив это:

opaque_legacy_type_t x;
XXopen(..., &x);
boost::shared_ptr<opaque_legacy_type_t> managed(x, XXclose);

Но, поскольку opaque_legacy_type_t не имеет семантики указателей, использование managed немного неуклюже.

Я хотел бы сделать что-то вроде managed_type, похожее на shared_ptr, и ищу идеи, которые не требуют, чтобы я все это написал.

РЕДАКТИРОВАТЬ: Я исправил мой первоначальный провал в примере. Устаревший API принимает непрозрачный тип по значению, а не по указателю.

Ответы [ 4 ]

2 голосов
/ 16 февраля 2012

Поскольку все устаревшие API принимают указатель на непрозрачный тип, вы можете напрямую использовать общие указатели.Ключ в том, чтобы вы не объявляли исходную структуру в стеке, а выделяли ее через new:

int main () {
    std::shared_ptr<opaque_legacy_type_t> x(new opaque_legacy_type_t,
        [](opaqeue_legacy_type_t* p) { XXClose(p); delete p; });
    XXopen(..., x.get());
    XXdoSomethingWithResource(x.get(), ...);
}

EDIT : Если какой-либо API принимает непрозрачный тип по значению вместо указателя, то передайте разыменованный указатель.

1 голос
/ 16 февраля 2012

Вы можете использовать умные указатели наддува вместе с pimpl idom:

class shared_opaque_legacy_type_t {
    struct impl {
        opaque_legacy_type_t t;
        impl(...) { XXOpen(..., t); }
        ~impl(...) { XXClose(t); }
    }
    boost::shared_ptr<impl> _impl;
public:
    shared_opaque_lagacy_type_t(...) : _impl(new impl(...)) {}

    opaque_legacy_type_t* get() {
        return _impl->t;
    }
};


shared_opaque_legacy_type_t x(...);
XXdoSomethingWithResource(x.get(), ...);

Недостатком является то, что вы все равно можете вызвать XXclose(x.get()) и сделать ваш объект недействительным.

ОБНОВЛЕНИЕ : Исправлено. : -)

1 голос
/ 16 февраля 2012

Вы можете написать оболочку для использования с boost, которая будет вызывать open() в ctor и close() в dtor.

0 голосов
/ 16 февраля 2012

Я проголосовал за ответ Роба, который просто использует shared_ptr без оболочки, но если вы действительно хотите избежать динамического размещения, вот простой небольшой пример того, как это сделать.

Это шаблон, который напрямуюдержит ручку и не выделяет.Вы передаете конструктору функтор, который создает объект непрозрачного типа, и средство удаления для вызова, когда тип должен быть уничтожен.Это подвижно и не копируется, поэтому теперь необходим общий счетчик ссылок.Он реализует операторы неявного преобразования, поэтому вы можете использовать его там, где вы будете использовать значение удерживаемого типа.

template<typename T,typename D>
class opaque_type_handle {
    T handle;
    D deleter;
    bool needs_delete;
public:
    template<typename F>
    opaque_type_handle(F f,D d) : handle(f()), deleter(d), needs_delete(true) {}

    opaque_type_handle(opaque_type_handle const &) = delete;
    opaque_type_handle &operator=(opaque_type_handle const &) = delete;

    opaque_type_handle(opaque_type_handle &&rhs) : handle(rhs.handle),deleter(rhs.deleter),needs_delete(true) {
        rhs.needs_delete = false;
    }
    opaque_type_handle &operator=(opaque_type_handle &&rhs) {
        handle = rhs.handle;
        deleter = rhs.deleter;
        needs_delete = true;
        rhs.needs_delete = false;
        returh *this;
    }

    ~opaque_type_handle() {
        if(needs_delete) {
            deleter(handle);
        }
    }

    operator T&() { return handle; }
    operator T() const { return handle; }
};

Используйте его так:

// wrap up the code for creating an opaque_legacy_type_t handle
typedef opaque_type_handle<opaque_legacy_type_t,decltype(&XXclose)> legacy_handle;

legacy_handle make_legacy_handle(...) {
    return legacy_handle(
        [](){
            opaque_legacy_type_t tmp;
            XXopen(..., &tmp);
            return tmp;
        },
        &XXclose
    );
}

legacy_handle x = make_legacy_handle(...);
XXdoSomethingWithResource(x,...);
...