A RAII file handle looks pretty basic so I guess it has already been implemented? But I couldn't find any implementation. I found file_descriptor in boost::iostreams but I don't know if it's what I'm looking for.
std::fstream
s support RAII-style usage - they can be opened and even tested at construction, and they're automatically flushed and closed in the destructor, though you could miss errors if you just assume that works so you may want to do something more explicit in code if you need the robustness.
For example:
if (std::ifstream input(filename))
... use input...
else
std::cerr << "unable to open '" << filename << "'\n";
If you really want to use file descriptors, you can tune something like the following to taste. It's a bit longer than something that just invokes close
, but if you want to do robust programming you need to check for and handle errors somehow....
#include <iostream>
#include <iomanip>
#include <string>
#include <exception>
#include <sstream>
#include <cstring> // for strerror
#include <unistd.h> // for close
struct Descriptor
{
Descriptor(const Descriptor&) = delete;
Descriptor(Descriptor&& other) noexcept
: fd_{other.fd_}, filename_{std::move(other.filename_)}
{ other.fd_ = -1; }
Descriptor(int fd, std::string filename = "")
: fd_{fd}, filename_{std::move(filename)}
{
if (fd < 0)
{
std::ostringstream oss;
throw std::runtime_error(err(oss, "open").str());
}
}
~Descriptor()
{
if (fd_ != -1 && close(fd_) == -1)
// destructor throw risks termination; avoid
err(std::cerr, "close") << '\n';
}
void operator=(const Descriptor&) = delete;
Descriptor& operator=(Descriptor&& rhs) noexcept {
swap(rhs);
return *this;
}
void swap(Descriptor& other) noexcept {
std::swap(fd_, rhs.fd_);
std::swap(filename_, rhs.filename_);
}
operator int() const { return fd_; }
private:
int fd_;
std::string filename_;
auto err(auto& os, const char* operation) const -> decltype(os) {
os << "failed to " << operation << " file";
if (!filename_.empty())
os << ' ' << std::quoted(filename_);
os << ": " << strerror(errno);
return os;
}
};
#include <fcntl.h>
int main()
try
{
const char* filename = "/proc/cmdline";
Descriptor fd(open(filename, O_RDONLY), filename);
char buffer[1024];
std::cout.write(buffer, read(fd, buffer, sizeof buffer)); // TODO error handling
}
catch (const std::exception& e)
{
std::cerr << e.what() << '\n';
}