Упаковка C создает и уничтожает функции, используя умный указатель

У меня есть C API, который обрабатывает создание и уничтожение объектов, он обеспечивает: createObject(...) а также destroy(...), Я хочу обернуть это в некоторые более современные механизмы строительства / разрушения и использовать их с умными указателями. Я боюсь, что в какой-то момент я забуду уничтожить объект, или произойдет какое-то исключение.

Я знаю о пользовательской функции удаления shared_ptr, но я не могу явно назвать new, так как createOjbect функция обрабатывает инициализацию.

Могу ли я использовать интеллектуальные указатели STL в этой ситуации? Должен ли я с нуля реализовать класс с инициализацией в конструкторе, уничтожением в деструкторе и подсчетом ссылок в этой ситуации?

3 ответа

Решение

std::shared_ptr полностью способен создавать и удалять объекты с помощью Cutstom Creator и Deleteer, но вместо new Вы должны использовать функцию создателя.

Давайте рассмотрим, у нас есть следующий создатель и удалитель:

typedef struct {
    int m_int;
    double m_double;
} Foo;

Foo* createObject(int i_val, double d_val) {
    Foo* output = (Foo*)malloc(sizeof(Foo));

    output->m_int = i_val;
    output->m_double = d_val;

    puts("Foo created.");
    return output;
}

void destroy(Foo* obj) {
    free(obj);
    puts("Foo destroyed.");        
}

Для управления экземпляром Foo созданные функциями выше, просто сделайте следующее:

std::shared_ptr<Foo> foo(createObject(32, 3.14), destroy);

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

struct FooDeleter {
    void operator()(Foo* p) const {
        destroy(p);
    }
};
using FooWrapper = std::unique_ptr<Foo, FooDeleter>;

/* ... */

FooWrapper foo(createObject(32, 3.14));

C++17.

template<auto X> using constant_t=std::integral_constant<std::decay_t<decltype(X)>, X>
template<auto X> constexpr constant_t<X> constant{};
template<class T, auto dtor> using smart_unique_ptr=std::unique_ptr< T, constant_t<dtor> >;

Теперь предположим, что у вас есть оболочка C API Bob с Bob* createBob(some_args...) а также destroyBob(Bob*):

using unique_bob=smart_unique_ptr< Bob, destroyBob >;
unique_bob make_unique_bob(some_args args){
  return unique_bob( createBob(args) );
}

unique_bob могут быть неявно перемещены в shared_ptr<Bob>,

Небольшое дополнительное предположение может заставить это работать в C++14:

template<class T, void(*dtor)(T*)> using smart_unique_ptr=std::unique_ptr< T, std::integral_constant<decltype(dtor),dtor> >;

который предполагает, что подпись dtor void(T*),

В C++ 11 вы должны написать новый диспетчер указателей на функции без сохранения состояния для нулевых служебных данных.

Публикация полного решения для моего случая:

Основываясь на предложениях @ Акиры, я обернул его с помощью общего указателя, потому что я хочу, чтобы этот объект использовался во многих дворцах и лямбда-выражениях:

// coming from some API:
struct SomeStruct;
bool initializedata(SomeStruct **data);
bool destorycdata(SomeStruct **data);

class SomeStructWrapper
{
public:
    SomeStructWrapper()
    {
        SomeStruct* data;
        if(initializedata(&data))
        {
            m_data = std::shared_ptr<SomeStruct>(data, [](SomeStruct* ptr){
                destorycdata(&ptr);
            });
        }
        else
        {
            throw std::runtime_error("Data was not initalized");
        }
    }

    const SomeStruct* operator->() const {return m_data.get();}
    SomeStruct* operator->() {return m_data.get();}

private:
    std::shared_ptr<SomeStruct> m_data;
};
Другие вопросы по тегам