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';
}
std::string
so there's no risk of it ending up pointing somewhere invalid (though that will use more memory), and it should really have a move constructor and assignment and the copy versions deleted.... –
Mismatch struct Descriptor
is broken. You defaulted the move constructor, but it doesn't do the right thing. At a minimum, you should remove that and delete the copy assignment, if you don't want to implement correct move semantics. –
Transliterate fd_
, then close it twice when the two instances of Descriptor
are destroyed. –
Transliterate Depends on what exactly you want.
If you really want a scoped handle, use:
std::unique_ptr<HANDLETYPE, closehandletypefunction> smartpointer;
For FILE
pointers, this would look like
std::unique_ptr<FILE, int (*)(FILE *)> f(fopen("myfile.txt", "a"), fclose);
The FILE*
can then be gotten with f.get()
. The same thing would work with file descriptors (open
and close
from <fcntl.h>
and <unistd.h>
, respectively).
The preferred C++ way is wrapping the handle in an object with thousands of members to do everything though.
I am using boost::filesystem::ifstream
(or ofstream
for writing).
I was actually asking this because I wanted to be sure that my file was closed even if an exception was raised before calling file.close()
But after reading the documentation again:
In case that an object is destroyed while still associated with an open file, the destructor automatically calls the member function close.
So, this is safe :)
© 2022 - 2024 — McMap. All rights reserved.
std::fstream
? – BeardsleyFILE*
. What does it mean to you? – GoodnaturedHANDLE
, as in msdn.microsoft.com/en-us/library/windows/desktop/… – Fiddlestickstdio
file . A file descriptor is an FD, a small integer. – Wolfe