Safe system handle implementation
I’ve saw handy safe system handle implementation in The Modern C++ Challenge (problem 21) book and found it handy in case of working with plain C file streams (aka. FILE *
, fopen()
, fclose()
, …). It was originally written for windows HANDLE
, but it is easy to use it also with FILE *
, this way
#include <iostream>
#include <cstdio>
using std::cout;
int main(int argc, char * argv[]) {
file_handle fin{fopen("system_handle.cpp", "r")};
if (!fin)
return 1;
// read file
char buf[4096];
size_t bytes = fread(buf, sizeof(char), 4096, fin.get());
cout << "readed "<< bytes << " bytes\n";
cout << "done!\n";
return 0;
}
where file_handle
is defined as unique_handle<>
specialization this way
//! safe system handle wrapper
template <typename Traits>
class unique_handle {
using pointer = typename Traits::pointer;
public:
unique_handle(unique_handle const &) = delete;
unique_handle & operator=(unique_handle const &) = delete;
explicit unique_handle(pointer value = Traits::invalid()) noexcept
: _value{value}
{}
unique_handle(unique_handle && other) noexcept
: _value{other.release()}
{}
unique_handle & operator=(unique_handle && other) noexcept {
if (this != &other)
reset(other.release());
return *this;
}
~unique_handle() noexcept {
Traits::close(_value);
}
explicit operator bool() const noexcept {
return _value != Traits::invalid();
}
pointer get() const noexcept {
return _value;
}
pointer release() noexcept {
auto value = _value;
_value = Traits::invalid();
return value;
}
bool reset(pointer value = Traits::invalid()) noexcept {
if (_value != value) {
Traits::close(_value);
_value = value;
}
return static_cast<bool>(*this);
}
void swap(unique_handle<Traits> & other) noexcept {
std::swap(_value, other._value);
}
private:
pointer _value;
};
template <typename Traits>
void swap(unique_handle<Traits> & left, unique_handle<Traits> & right) {
left.swap(right);
}
template <typename Traits>
bool operator==(unique_handle<Traits> const & left, unique_handle<Traits> const & right) noexcept {
return left.get() == right.get();
}
template <typename Traits>
bool operator!=(unique_handle<Traits> const & left, unique_handle<Traits> const & right) noexcept {
return left.get() != right.get();
}
struct file_handle_traits {
using pointer = FILE *;
static pointer invalid() noexcept {return nullptr;}
static void close(pointer value) noexcept {fclose(value);}
};
using file_handle = unique_handle<file_handle_traits>;
see
system_handle.cpp
for full blown sample
At first, I was thinking that thanks to class template argument deduction from C++17 we can use unique_ptr
with custom deleter to handle FILE *
this way
unique_ptr fin{fopen("system_handle.cpp", "r"), &fclose};
but it ends up with error: class template argument deduction failed
comlpain while building. Later, I’ve found that unique_ptr
needs to be used this way
void close_file(FILE * f) {fclose(f);}
void foo() {
unique_ptr<FILE, decltype(&close_file)> fin{fopen("demo.txt", "r"), &close_file};
// ...
}
to get it working with FILE *
, which is far from the ideal. If you know the better way just leave me a comment.