2015-01-15 2 views
3

Я могу понять, что boost::shared_ptr не проверяет для NULL перед вызовом пользовательской функции удаления, но как я могу это достичь? Это поможет мне избежать написания немых оберток для fclose или любой функции, которая не (правильно) определяет поведение.boost smart pointer with custom deleter

My boost: #define BOOST_VERSION 104500. Это не C++ 11 (так как я использую boost).

вопрос связан с: make shared_ptr not use delete

Sample Код:

static inline 
FILE* safe_fopen(const char* filename, const char* mode) 
{ 
     FILE* file = NULL; 
     (void)fopen_s(&file, filename, mode); 
     return file; 
} 

static inline 
void safe_fclose(FILE* file) 
{ 
     if (file) 
     BOOST_VERIFY(0 == fclose(file)); 
} 

... 

boost::shared_ptr<FILE> file(safe_fopen(FILE_DOWNLOAD, "rb"), safe_fclose); 
... 
// now it is created => open it once again 
file.reset(safe_fopen(FILE_DOWNLOAD, "rb"), safe_fclose); 

EDIT

Мой вопрос первоначально имел вторую часть, касающуюся использования shared_ptr: почему обеспечивающего Deleter как параметр функции вместо параметра шаблона? Видимо, ответ здесь: Why does unique_ptr take two template parameters when shared_ptr only takes one? Ответ на C++ 11 is unique_ptr, но почему boost не обеспечил один - мы никогда не узнаем.

+1

Второй вопрос очень глубок и необходим для дизайна 'shared_ptr'. Я рекомендую переместить отдельный вопрос (который предположительно мы можем закрыть как дубликат). –

+0

Я бы предложил посмотреть [std :: shared_ptr] (http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr), если это возможно. – user3159253

+0

@ Kerrek SB Если я сам переведу его, не стал бы первым вопросом (слева) слишком глупым? Я уже сосредоточил всю свою энергию на этом: p – Liviu

ответ

1

Похоже, что существует разница между empty (non-owning) shared_ptr и null.

Nulled будет (и должен) ссылаться на deleter (это может означать что-то для определенного типа дескриптора ресурса. Фактически, значение «0» может быть даже не особенным).

Пустые будут вызывать дебетер, но с аргументом nullptr_t. Таким образом, вы могли бы обобщенно сделать обертку, которая оборачивает любые ADHOC Deleter (будь то встроенный лямбда, или указатель на функцию, как &::free):

template <typename F> 
OptionalDeleter<F> make_optional_deleter(F&& f) { return OptionalDeleter<F>(std::forward<F>(f)); } 

int main() { 
    auto d = make_optional_deleter([](void*){std::cout << "Deleting\n";}); 

    using namespace std; 
    { 
     shared_ptr<int> empty(std::nullptr_t{}, d); 
    } // deleter not called, empty 

    { 
     shared_ptr<int> thing(static_cast<int*>(nullptr), d); 
    } // deleter called, thing not empty 
} 

Напечатает

Empty, not deleting 
Deleting 

Похоже, что вы получите очень «чистый», верный, сквозной исходный подход к базовому API. Это GoodThing ™, поскольку, если shared_ptr<> сделал «произвольные» исключения, он стал бы непригодным в случаях, когда удаление значения «NULL» (или по умолчанию) было бы значимым и не должно быть пропущено.

Прямо сейчас, выбор за вами, как и должно быть.

Конечно, вы можете использовать очень похожую упаковку, как я только что продемонстрировал, чтобы пропустить общий вызов API, если значение дескриптора ресурса равно «NULL» или иным образом недействительным.

Live On Coliru

#include <memory> 
#include <iostream> 

template<typename F> 
struct OptionalDeleter final { 
    explicit OptionalDeleter(F&& f) : _f(std::forward<F>(f)) {} 

    template <typename... Ts> 
     void operator()(Ts&&... args) const { _f(std::forward<Ts>(args)...); } 

    void operator()(std::nullptr_t) const { std::cout << "Empty, not deleting\n"; } 
    private: 
    F _f; 
}; 

template <typename F> 
OptionalDeleter<F> make_optional_deleter(F&& f) { return OptionalDeleter<F>(std::forward<F>(f)); } 

int main() { 
    auto d = make_optional_deleter([](void*){std::cout << "Deleting\n";}); 

    using namespace std; 
    { 
     shared_ptr<int> empty(std::nullptr_t{}, d); 
    } // deleter not called, empty 

    { 
     shared_ptr<int> thing(static_cast<int*>(nullptr), d); 
    } // deleter called, thing not empty 
} 
+0

Спасибо, я скоро вернусь! – Liviu

+0

Я вернулся ... и я поражен, увидев C++ 11 (или что-то еще). _ «Это не C++ 11 (так как я использую boost)». _ – Liviu

1

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

static inline 
FILE* safe_fopen(const char* filename, const char* mode) 
{ 
     FILE* file = NULL; 
     (void)fopen_s(&file, filename, mode); 
     if (!file) 
     throw std::exception(...); // check errno 
     return file; 
} 

Однако это не помогает устранить ООН-инициализирован boost::shared_ptr если где еще случаются по какому-тому причиню.

В отличие от make shared_ptr not use delete Я думаю, что из-за характера функций вы просто застряли в наличии этих более длинных оберток.

+0

Так что невозможно использовать технику из ответа @ sehe в 'C++ 03'? Обертка шаблона вокруг 'void Func (T *)'? – Liviu

+0

@ sehe 'OptionalDeleter' использует методы, недоступные предыдущей версии компилятора, насколько мне известно. Вы могли бы, конечно, объединить шаблонные конструкции, чтобы сделать некоторые из них косвенно, если вы обнаружили, что вам нужно делать это со многими типами, но в какой-то момент подумайте, стоит ли это усилий и сложности. Лучше переместиться на C++ 11. –

+0

«Лучше перейти на C++ 11» Это бесплатно? Если это так, я могу порекомендовать его моей компании (для тех оберток)! – Liviu