I wanted to create a class Channel
that facilitates file writes in a special way. It was clear to me at first that I call open()
on the file in the constructor and close()
on the file descriptor in the destructor. But this would mean that I have to throw in the destructor if close()
fails for some reason. Is it smart to use RAII for file management anyway? Should I throw in the destructor?
#include <iostream>
#include <fcntl.h> /* open */
#include <unistd.h> /* close */
#include <stdexcept>
#include <filesystem>
class Channel
{
public:
Channel(std::filesystem::path path_to_file)
{
_fd = open(path_to_file.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (!_fd)
{
throw std::runtime_error{ "Failed to open file" };
}
}
~Channel()
{
int r = close(_fd);
if (r < 0)
{
// ... ?
}
}
private:
int _fd;
};
int main()
{
Channel("/home/user/myfile.txt");
}
It has been already mentioned in a comment. Compare to what the standard library does:
{
std::ifstream f{"path/to/file"};
f >> data;
// f.close(); // possible but usually unnecessary
}
The situation with std::ifstream
is exactly what @john explains in their comment: The stream closes the file in its destructor. Any exceptions or errors encountered during that go unnoticed (because one should not throw from a destructor). If you want to check if closing the file succeeded you can call close
explicitly (and that's about the only reason you want to call close
explicitly).
Yes RAII is a good way to handle files. I remember Bjarne using that as example for why RAII is such a cool thing: It applies not only to memory, hence is more generally applicable compared to eg garbage collection.