FileDescriptor -> File

This commit is contained in:
Victor Zverovich 2014-05-03 12:28:02 -07:00
parent 98b6e59272
commit c5c2cd63bc
3 changed files with 141 additions and 146 deletions

View File

@ -145,15 +145,15 @@ TEST(ErrorCodeTest, Ctor) {
EXPECT_EQ(42, ErrorCode(42).get());
}
TEST(FileDescriptorTest, DefaultCtor) {
FileDescriptor fd;
EXPECT_EQ(-1, fd.get());
TEST(FileTest, DefaultCtor) {
File f;
EXPECT_EQ(-1, f.get());
}
TEST(FileDescriptorTest, OpenFileInCtor) {
TEST(FileTest, OpenFileInCtor) {
FILE *f = 0;
{
FileDescriptor fd(".travis.yml", FileDescriptor::RDONLY);
File fd(".travis.yml", File::RDONLY);
f = fdopen(fd.get(), "r");
ASSERT_TRUE(f != 0);
}
@ -163,29 +163,28 @@ TEST(FileDescriptorTest, OpenFileInCtor) {
fclose(f);
}
TEST(FileDescriptorTest, OpenFileError) {
EXPECT_SYSTEM_ERROR(
FileDescriptor("nonexistent", FileDescriptor::RDONLY), ENOENT,
"cannot open file nonexistent");
TEST(FileTest, OpenFileError) {
EXPECT_SYSTEM_ERROR(File("nonexistent", File::RDONLY),
ENOENT, "cannot open file nonexistent");
}
TEST(FileDescriptorTest, MoveCtor) {
FileDescriptor fd(".travis.yml", FileDescriptor::RDONLY);
int fd_value = fd.get();
EXPECT_NE(-1, fd_value);
FileDescriptor fd2(std::move(fd));
EXPECT_EQ(fd_value, fd2.get());
EXPECT_EQ(-1, fd.get());
TEST(FileTest, MoveCtor) {
File f(".travis.yml", File::RDONLY);
int fd = f.get();
EXPECT_NE(-1, fd);
File f2(std::move(f));
EXPECT_EQ(fd, f2.get());
EXPECT_EQ(-1, f.get());
}
TEST(FileDescriptorTest, MoveAssignment) {
FileDescriptor fd(".travis.yml", FileDescriptor::RDONLY);
int fd_value = fd.get();
EXPECT_NE(-1, fd_value);
FileDescriptor fd2;
fd2 = std::move(fd);
EXPECT_EQ(fd_value, fd2.get());
EXPECT_EQ(-1, fd.get());
TEST(FileTest, MoveAssignment) {
File f(".travis.yml", File::RDONLY);
int fd = f.get();
EXPECT_NE(-1, fd);
File f2;
f2 = std::move(f);
EXPECT_EQ(fd, f2.get());
EXPECT_EQ(-1, f.get());
}
bool IsClosed(int fd) {
@ -194,48 +193,48 @@ bool IsClosed(int fd) {
return result == -1 && errno == EBADF;
}
TEST(FileDescriptorTest, MoveAssignmentClosesFile) {
FileDescriptor fd(".travis.yml", FileDescriptor::RDONLY);
FileDescriptor fd2("CMakeLists.txt", FileDescriptor::RDONLY);
int old_fd = fd2.get();
fd2 = std::move(fd);
TEST(FileTest, MoveAssignmentClosesFile) {
File f(".travis.yml", File::RDONLY);
File f2("CMakeLists.txt", File::RDONLY);
int old_fd = f2.get();
f2 = std::move(f);
EXPECT_TRUE(IsClosed(old_fd));
}
FileDescriptor OpenFile(int &fd_value) {
FileDescriptor fd(".travis.yml", FileDescriptor::RDONLY);
fd_value = fd.get();
return std::move(fd);
File OpenFile(int &fd) {
File f(".travis.yml", File::RDONLY);
fd = f.get();
return std::move(f);
}
TEST(FileDescriptorTest, MoveFromTemporaryInCtor) {
int fd_value = 0xdeadbeef;
FileDescriptor fd(OpenFile(fd_value));
EXPECT_EQ(fd_value, fd.get());
TEST(FileTest, MoveFromTemporaryInCtor) {
int fd = 0xdeadbeef;
File f(OpenFile(fd));
EXPECT_EQ(fd, f.get());
}
TEST(FileDescriptorTest, MoveFromTemporaryInAssignment) {
int fd_value = 0xdeadbeef;
FileDescriptor fd;
fd = OpenFile(fd_value);
EXPECT_EQ(fd_value, fd.get());
TEST(FileTest, MoveFromTemporaryInAssignment) {
int fd = 0xdeadbeef;
File f;
f = OpenFile(fd);
EXPECT_EQ(fd, f.get());
}
TEST(FileDescriptorTest, MoveFromTemporaryInAssignmentClosesFile) {
int fd_value = 0xdeadbeef;
FileDescriptor fd(".travis.yml", FileDescriptor::RDONLY);
int old_fd = fd.get();
fd = OpenFile(fd_value);
TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
int fd = 0xdeadbeef;
File f(".travis.yml", File::RDONLY);
int old_fd = f.get();
f = OpenFile(fd);
EXPECT_TRUE(IsClosed(old_fd));
}
TEST(FileDescriptorTest, CloseFileInDtor) {
int fd_value = 0;
TEST(FileTest, CloseFileInDtor) {
int fd = 0;
{
FileDescriptor fd(".travis.yml", FileDescriptor::RDONLY);
fd_value = fd.get();
File f(".travis.yml", File::RDONLY);
fd = f.get();
}
FILE *f = fdopen(fd_value, "r");
FILE *f = fdopen(fd, "r");
int error_code = errno;
if (f)
fclose(f);
@ -243,89 +242,88 @@ TEST(FileDescriptorTest, CloseFileInDtor) {
EXPECT_EQ(EBADF, error_code);
}
TEST(FileDescriptorTest, CloseError) {
FileDescriptor *fd =
new FileDescriptor(".travis.yml", FileDescriptor::RDONLY);
TEST(FileTest, CloseError) {
File *fd = new File(".travis.yml", File::RDONLY);
EXPECT_STDERR(close(fd->get()); delete fd,
FormatSystemErrorMessage(EBADF, "cannot close file") + "\n");
}
std::string ReadLine(FileDescriptor &fd) {
std::string ReadLine(File &f) {
enum { BUFFER_SIZE = 100 };
char buffer[BUFFER_SIZE];
std::streamsize result = fd.read(buffer, BUFFER_SIZE);
std::streamsize result = f.read(buffer, BUFFER_SIZE);
buffer[std::min<std::streamsize>(BUFFER_SIZE - 1, result)] = '\0';
if (char *end = strchr(buffer, '\n'))
*end = '\0';
return buffer;
}
TEST(FileDescriptorTest, Read) {
FileDescriptor fd(".travis.yml", FileDescriptor::RDONLY);
EXPECT_EQ("language: cpp", ReadLine(fd));
TEST(FileTest, Read) {
File f(".travis.yml", File::RDONLY);
EXPECT_EQ("language: cpp", ReadLine(f));
}
TEST(FileDescriptorTest, ReadError) {
FileDescriptor fd;
TEST(FileTest, ReadError) {
File f;
char buf;
EXPECT_SYSTEM_ERROR(fd.read(&buf, 1), EBADF, "cannot read from file");
EXPECT_SYSTEM_ERROR(f.read(&buf, 1), EBADF, "cannot read from file");
}
TEST(FileDescriptorTest, Dup) {
FileDescriptor fd(".travis.yml", FileDescriptor::RDONLY);
FileDescriptor dup = FileDescriptor::dup(fd.get());
EXPECT_NE(fd.get(), dup.get());
TEST(FileTest, Dup) {
File f(".travis.yml", File::RDONLY);
File dup = File::dup(f.get());
EXPECT_NE(f.get(), dup.get());
EXPECT_EQ("language: cpp", ReadLine(dup));
}
TEST(FileDescriptorTest, DupError) {
EXPECT_SYSTEM_ERROR(FileDescriptor::dup(-1),
TEST(FileTest, DupError) {
EXPECT_SYSTEM_ERROR(File::dup(-1),
EBADF, "cannot duplicate file descriptor -1");
}
TEST(FileDescriptorTest, Dup2) {
FileDescriptor fd(".travis.yml", FileDescriptor::RDONLY);
FileDescriptor dup("CMakeLists.txt", FileDescriptor::RDONLY);
fd.dup2(dup.get());
EXPECT_NE(fd.get(), dup.get());
TEST(FileTest, Dup2) {
File f(".travis.yml", File::RDONLY);
File dup("CMakeLists.txt", File::RDONLY);
f.dup2(dup.get());
EXPECT_NE(f.get(), dup.get());
EXPECT_EQ("language: cpp", ReadLine(dup));
}
TEST(FileDescriptorTest, Dup2Error) {
FileDescriptor fd(".travis.yml", FileDescriptor::RDONLY);
EXPECT_SYSTEM_ERROR(fd.dup2(-1), EBADF,
fmt::Format("cannot duplicate file descriptor {} to -1") << fd.get());
TEST(FileTest, Dup2Error) {
File f(".travis.yml", File::RDONLY);
EXPECT_SYSTEM_ERROR(f.dup2(-1), EBADF,
fmt::Format("cannot duplicate file descriptor {} to -1") << f.get());
}
TEST(FileDescriptorTest, Dup2NoExcept) {
FileDescriptor fd(".travis.yml", FileDescriptor::RDONLY);
FileDescriptor dup("CMakeLists.txt", FileDescriptor::RDONLY);
TEST(FileTest, Dup2NoExcept) {
File f(".travis.yml", File::RDONLY);
File dup("CMakeLists.txt", File::RDONLY);
ErrorCode ec;
fd.dup2(dup.get(), ec);
f.dup2(dup.get(), ec);
EXPECT_EQ(0, ec.get());
EXPECT_NE(fd.get(), dup.get());
EXPECT_NE(f.get(), dup.get());
EXPECT_EQ("language: cpp", ReadLine(dup));
}
TEST(FileDescriptorTest, Dup2NoExceptError) {
FileDescriptor fd(".travis.yml", FileDescriptor::RDONLY);
TEST(FileTest, Dup2NoExceptError) {
File f(".travis.yml", File::RDONLY);
ErrorCode ec;
fd.dup2(-1, ec);
f.dup2(-1, ec);
EXPECT_EQ(EBADF, ec.get());
}
TEST(FileDescriptorTest, Pipe) {
FileDescriptor read_fd, write_fd;
FileDescriptor::pipe(read_fd, write_fd);
EXPECT_NE(-1, read_fd.get());
EXPECT_NE(-1, write_fd.get());
TEST(FileTest, Pipe) {
File read_end, write_end;
File::pipe(read_end, write_end);
EXPECT_NE(-1, read_end.get());
EXPECT_NE(-1, write_end.get());
// TODO: try writing to write_fd and reading from read_fd
}
// TODO: test pipe
// TODO: test FileDescriptor::read
// TODO: test File::read
// TODO: compile both with C++11 & C++98 mode

View File

@ -51,7 +51,7 @@
result = (expression); \
} while (result == -1 && errno == EINTR)
FileDescriptor::FileDescriptor(const char *path, int oflag) {
File::File(const char *path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#ifdef _WIN32
fd_ = -1;
@ -63,7 +63,7 @@ FileDescriptor::FileDescriptor(const char *path, int oflag) {
fmt::ThrowSystemError(errno, "cannot open file {}") << path;
}
void FileDescriptor::close() {
void File::close() {
if (fd_ == -1)
return;
// Don't need to retry close in case of EINTR.
@ -72,7 +72,7 @@ void FileDescriptor::close() {
fmt::ReportSystemError(errno, "cannot close file");
}
std::streamsize FileDescriptor::read(void *buffer, std::size_t count) {
std::streamsize File::read(void *buffer, std::size_t count) {
std::streamsize result = 0;
FMT_RETRY(result, ::read(fd_, buffer, count));
if (result == -1)
@ -80,7 +80,7 @@ std::streamsize FileDescriptor::read(void *buffer, std::size_t count) {
return result;
}
std::streamsize FileDescriptor::write(const void *buffer, std::size_t count) {
std::streamsize File::write(const void *buffer, std::size_t count) {
std::streamsize result = 0;
FMT_RETRY(result, ::write(fd_, buffer, count));
if (result == -1)
@ -88,15 +88,15 @@ std::streamsize FileDescriptor::write(const void *buffer, std::size_t count) {
return result;
}
FileDescriptor FileDescriptor::dup(int fd) {
File File::dup(int fd) {
int new_fd = 0;
FMT_RETRY(new_fd, ::FMT_POSIX(dup(fd)));
if (new_fd == -1)
fmt::ThrowSystemError(errno, "cannot duplicate file descriptor {}") << fd;
return FileDescriptor(new_fd);
return File(new_fd);
}
void FileDescriptor::dup2(int fd) {
void File::dup2(int fd) {
int result = 0;
FMT_RETRY(result, ::FMT_POSIX(dup2(fd_, fd)));
if (result == -1) {
@ -105,18 +105,18 @@ void FileDescriptor::dup2(int fd) {
}
}
void FileDescriptor::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT(true) {
void File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT(true) {
int result = 0;
FMT_RETRY(result, ::FMT_POSIX(dup2(fd_, fd)));
if (result == -1)
ec = ErrorCode(errno);
}
void FileDescriptor::pipe(FileDescriptor &read_fd, FileDescriptor &write_fd) {
void File::pipe(File &read_end, File &write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_fd.close();
write_fd.close();
read_end.close();
write_end.close();
int fds[2] = {};
#ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
@ -130,25 +130,25 @@ void FileDescriptor::pipe(FileDescriptor &read_fd, FileDescriptor &write_fd) {
fmt::ThrowSystemError(errno, "cannot create pipe");
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_fd = FileDescriptor(fds[0]);
write_fd = FileDescriptor(fds[1]);
read_end = File(fds[0]);
write_end = File(fds[1]);
}
OutputRedirector::OutputRedirector(FILE *file) : file_(file) {
if (std::fflush(file) != 0)
fmt::ThrowSystemError(errno, "cannot flush stream");
int fd = fileno(file);
saved_fd_ = FileDescriptor::dup(fd);
FileDescriptor write_fd;
FileDescriptor::pipe(read_fd_, write_fd);
write_fd.dup2(fd);
saved_ = File::dup(fd);
File write_end;
File::pipe(read_end_, write_end);
write_end.dup2(fd);
}
OutputRedirector::~OutputRedirector() {
if (std::fflush(file_) != 0)
fmt::ReportSystemError(errno, "cannot flush stream");
ErrorCode ec;
saved_fd_.dup2(fileno(file_), ec);
saved_.dup2(fileno(file_), ec);
if (ec.get())
fmt::ReportSystemError(errno, "cannot restore output");
}
@ -157,7 +157,7 @@ std::string OutputRedirector::Read() {
// Restore output.
if (std::fflush(file_) != 0)
fmt::ThrowSystemError(errno, "cannot flush stream");
saved_fd_.dup2(fileno(file_));
saved_.dup2(fileno(file_));
// Read everything from the pipe.
std::string content;
@ -165,7 +165,7 @@ std::string OutputRedirector::Read() {
char buffer[BUFFER_SIZE];
std::streamsize count = 0;
do {
count = read_fd_.read(buffer, BUFFER_SIZE);
count = read_end_.read(buffer, BUFFER_SIZE);
content.append(buffer, count);
} while (count != 0);
return content;

View File

@ -103,16 +103,16 @@ class ErrorCode {
int get() const FMT_NOEXCEPT(true) { return value_; }
};
// A RAII class for file descriptors.
class FileDescriptor {
// A file.
class File {
private:
int fd_;
int fd_; // File descriptor.
// Closes the file if its descriptor is not -1.
void close();
// Constructs a FileDescriptor object with a given descriptor.
explicit FileDescriptor(int fd) : fd_(fd) {}
// Constructs a File object with a given descriptor.
explicit File(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
@ -122,13 +122,12 @@ class FileDescriptor {
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a FileDescriptor object with a descriptor of -1 which
// is ignored by the destructor.
FileDescriptor() FMT_NOEXCEPT(true) : fd_(-1) {}
// Constructs a File object which doesn't represent any file.
File() FMT_NOEXCEPT(true) : fd_(-1) {}
// Opens a file and constructs a FileDescriptor object with the descriptor
// of the opened file. Throws fmt::SystemError on error.
FileDescriptor(const char *path, int oflag);
// Opens a file and constructs a File object representing this file.
// Throws fmt::SystemError on error.
File(const char *path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
@ -143,22 +142,22 @@ class FileDescriptor {
public:
// A "move" constructor for moving from a temporary.
FileDescriptor(Proxy p) FMT_NOEXCEPT(true) : fd_(p.fd) {}
File(Proxy p) FMT_NOEXCEPT(true) : fd_(p.fd) {}
// A "move" constructor for for moving from an lvalue.
FileDescriptor(FileDescriptor &other) FMT_NOEXCEPT(true) : fd_(other.fd_) {
File(File &other) FMT_NOEXCEPT(true) : fd_(other.fd_) {
other.fd_ = -1;
}
// A "move" assignment operator for moving from a temporary.
FileDescriptor &operator=(Proxy p) {
File &operator=(Proxy p) {
close();
fd_ = p.fd;
return *this;
}
// A "move" assignment operator for moving from an lvalue.
FileDescriptor &operator=(FileDescriptor &other) {
File &operator=(File &other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
@ -166,7 +165,7 @@ class FileDescriptor {
}
// Returns a proxy object for moving from a temporary:
// FileDescriptor fd = FileDescriptor(...);
// File file = File(...);
operator Proxy() FMT_NOEXCEPT(true) {
Proxy p = {fd_};
fd_ = -1;
@ -174,37 +173,36 @@ class FileDescriptor {
}
#else
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(FileDescriptor);
GTEST_DISALLOW_COPY_AND_ASSIGN_(File);
public:
FileDescriptor(FileDescriptor &&other) FMT_NOEXCEPT(true) : fd_(other.fd_) {
File(File &&other) FMT_NOEXCEPT(true) : fd_(other.fd_) {
other.fd_ = -1;
}
FileDescriptor& operator=(FileDescriptor &&other) FMT_NOEXCEPT(true) {
File& operator=(File &&other) FMT_NOEXCEPT(true) {
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
#endif
// Closes the file if its descriptor is not -1 and destroys the object.
~FileDescriptor() { close(); }
// Destroys the object closing the file it represents if any.
~File() { close(); }
// Returns the file descriptor.
// TODO: rename
int get() const FMT_NOEXCEPT(true) { return fd_; }
// Attempts to read count bytes from the file associated with this file
// descriptor into the specified buffer.
// Attempts to read count bytes from the file into the specified buffer.
std::streamsize read(void *buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file
// associated with this file descriptor.
// Attempts to write count bytes from the specified buffer to the file.
std::streamsize write(const void *buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate. Throws fmt::SystemError on error.
static FileDescriptor dup(int fd);
// the duplicate as a file object. Throws fmt::SystemError on error.
static File dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary. Throws fmt::SystemError on error.
@ -214,15 +212,15 @@ class FileDescriptor {
// necessary.
void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT(true);
// Creates a pipe setting up read and write file descriptors for reading
// and writing respecively. Throws fmt::SystemError on error.
static void pipe(FileDescriptor &read_fd, FileDescriptor &write_fd);
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively. Throws fmt::SystemError on error.
static void pipe(File &read_end, File &write_end);
};
#if !FMT_USE_RVALUE_REFERENCES
namespace std {
// For compatibility with C++98.
inline FileDescriptor &move(FileDescriptor &fd) { return fd; }
inline File &move(File &f) { return f; }
}
#endif
@ -230,9 +228,8 @@ inline FileDescriptor &move(FileDescriptor &fd) { return fd; }
class OutputRedirector {
private:
FILE *file_;
FileDescriptor saved_fd_; // Saved file descriptor created with dup.
FileDescriptor read_fd_; // Read end of the pipe where the output is
// redirected.
File saved_; // Saved file created with dup.
File read_end_; // Read end of the pipe where the output is redirected.
GTEST_DISALLOW_COPY_AND_ASSIGN_(OutputRedirector);