In theory, I should be able to use a custom pointer type and deleter in order to have unique_ptr
manage an object that is not a pointer. I tried the following c
This solution is based on Nicol Bolas answer:
struct FdDeleter
{
typedef int pointer;
void operator()(int fd)
{
::close(fd);
}
};
typedef std::unique_ptr<int, FdDeleter> UniqueFd;
It's short, but you have to avoid to compare UniqueFd instance with nullptr and use it as boolean expression:
UniqueFd fd(-1, FdDeleter()); //correct
//UniqueFd fd(nullptr, FdDeleter()); //compiler error
if (fd.get() != -1) //correct
{
std::cout << "Ok: it is not printed" << std::endl;
}
if (fd) //incorrect, avoid
{
std::cout << "Problem: it is printed" << std::endl;
}
if (fd != nullptr) //incorrect, avoid
{
std::cout << "Problem: it is printed" << std::endl;
}
return 1;
I would suggest using shared_ptr
rather than unique_ptr
to manage the life time of int
handles because the shared ownership semantics are usually a better fit, and because the type of the deleter is erased. You need the following helper:
namespace handle_detail
{
template <class H, class D>
struct deleter
{
deleter( H h, D d ): h_(h), d_(d) { }
void operator()( H * h ) { (void) d_(h_); }
H h_;
D d_;
};
}
template <class H,class D>
std::shared_ptr<H const>
make_handle( H h, D d )
{
std::shared_ptr<H> p((H *)0,handle_detail::deleter<H,D>(h,d));
return std::shared_ptr<H const>(
p,
&std::get_deleter<handle_detail::deleter<H,D> >(p)->h_ );
}
To use with a file descriptor:
int fh = open("readme.txt", O_RDONLY); // Check for errors though.
std::shared_ptr<int const> f = make_handle(fh, &close);
Can you do something simple like the following?
class unique_fd {
public:
unique_fd(int fd) : fd_(fd) {}
unique_fd(unique_fd&& uf) { fd_ = uf.fd_; uf.fd_ = -1; }
~unique_fd() { if (fd_ != -1) close(fd_); }
explicit operator bool() const { return fd_ != -1; }
private:
int fd_;
unique_fd(const unique_fd&) = delete;
unique_fd& operator=(const unique_fd&) = delete;
};
I do not see why you had to use unique_ptr
, which is designed to manage pointers.
A complete sample:
#include <memory>
#include <unistd.h>
#include <fcntl.h>
template<class T, T nullvalue, class D, D d> // auto d works in new compilers.
struct generic_delete
{
class pointer
{
T t;
public:
pointer(T t) : t(t) {}
pointer(std::nullptr_t = nullptr) : t(nullvalue) { }
explicit operator bool() { return t != nullvalue; }
friend bool operator ==(pointer lhs, pointer rhs) { return lhs.t == rhs.t; }
friend bool operator !=(pointer lhs, pointer rhs) { return lhs.t != rhs.t; }
operator T() { return t; }
};
void operator()(pointer p)
{
d(p);
}
};
using unique_fd = std::unique_ptr<void, generic_delete<int, -1, decltype(&close), &close>>;
static_assert(sizeof(unique_fd) == sizeof(int), "bloated unique_fd");
int main()
{
unique_fd fd1(open("fd.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR));
write(fd1.get(), "hello\n", 6);
}
In theory, I should be able to use a custom pointer type and deleter in order to have
unique_ptr
manage an object that is not a pointer.
No, you should not. That is, in terms of getting it to compile and run, maybe, but you simply shouldn't use a unique_ptr
to manage something which is not a pointer. You absolutely should write an appropriate RAII class for your resource - e.g. an OS file descriptor - or use an existing such class from some library. Only if you want a pointer to such a resource does a unique_ptr make sense; but then, you don't need a custom deleter.
Making Nicol Bolas class more general:
template<class T=void*,T null_val=nullptr>
class Handle
{
public:
Handle(T handle):m_handle(handle){}
Handle(std::nullptr_t):m_handle(null_val){}
operator T(){return m_handle;}
bool operator==(const Handle& other) const
{return other.m_handle==m_handle;}
private:
T m_handle;
};
typedef Handle<int,-1> FileDescriptor;
typedef Handle<GLuint,0> GlResource; // according to http://stackoverflow.com/questions/7322147/what-is-the-range-of-opengl-texture-id
// ...
I am not sure if I should have default template parameter values or not.