问题
I can understand that boost::shared_ptr
doesn't validate for NULL
before calling a custom deleter function, but how can I achieve this? This will help me avoid writing dumb wrappers for fclose
or any function that doesn't (rightly) specify the behaviour.
My boost: #define BOOST_VERSION 104500
. This is not C++ 11 (since I use boost).
The question is related to: make shared_ptr not use delete
Sample code:
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
My question initially had a second part concerning the use of shared_ptr
: why providing the deleter as a function parameter instead of a template parameter? Apparently, the answer is here: Why does unique_ptr take two template parameters when shared_ptr only takes one? C++ 11 answer is unique_ptr, but why boost did't provide one - we'll never know.
回答1:
It appears to be about the difference between an empty
(non-owning) shared_ptr and a null
-ed one.
Nulled ones will (and should) invoke the deleter (it might mean something for the particular type of resource handle. In fact, the value "0" might not even be special).
Empty ones will invoke the deleter, but with a nullptr_t
argument. Therefore, you could generically make a wrapper that wraps any adhoc deleter (be it an inline lambda, or a function pointer like &::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
}
Will print
Empty, not deleting
Deleting
It looks like you get very "pure", faithful, pass-through of the original intent to the underlying API. This is a GoodThing™ since if shared_ptr<>
made "arbitrary" exceptions, it would become unusable in cases where the deletion of a "NULL" (or default) value would be meaningful and should not be skipped.
Right now, the choice is yours, as it should be.
Of course, you can use a very similar wrapping stragegy like I just showed to generically skip the underlying API call if the resource handle value is "NULL" or otherwise invalid.
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
}
回答2:
One thought was perhaps instead of trying to protect the destructor, you should force the failure at construction, thus avoiding having to check an invalid during destruction.
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;
}
However this doesn't help fix an un-initialized boost::shared_ptr
if that where to still happen for some reason.
Unlike make shared_ptr not use delete I think due to the nature of the functions you are just stuck with having these longer wrappers.
来源:https://stackoverflow.com/questions/27962978/boost-smart-pointer-with-custom-deleter