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

View File

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

View File

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