Move POSIX wrappers to separate files.

This commit is contained in:
Victor Zverovich 2014-06-09 08:33:48 -07:00
parent b4dc0301f6
commit b692ab58b2
4 changed files with 487 additions and 424 deletions

194
posix.cc Normal file
View File

@ -0,0 +1,194 @@
/*
A C++ interface to POSIX functions.
Copyright (c) 2014, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "posix.h"
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32
# include <unistd.h>
#else
# include <io.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# define S_IRUSR _S_IREAD
# define S_IWUSR _S_IWRITE
#endif // _WIN32
// Retries the expression while it evaluates to -1 and error equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY(result, expression) \
do { \
result = (expression); \
} while (result == -1 && errno == EINTR)
#else
# define FMT_RETRY(result, expression) result = (expression)
#endif
namespace {
#ifdef _WIN32
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned ConvertRWCount(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#else
inline std::size_t ConvertRWCount(std::size_t count) { return count; }
#endif
}
BufferedFile::~BufferedFile() FMT_NOEXCEPT(true) {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
fmt::ReportSystemError(errno, "cannot close file");
}
void BufferedFile::close() {
if (!file_)
return;
int result = FMT_SYSTEM(fclose(file_));
file_ = 0;
if (result != 0)
fmt::ThrowSystemError(errno, "cannot close file");
}
int BufferedFile::fileno() const {
int fd = FMT_POSIX_CALL(fileno(file_));
if (fd == -1)
fmt::ThrowSystemError(errno, "cannot get file descriptor");
return fd;
}
File::File(const char *path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#ifdef _WIN32
fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path, oflag, _SH_DENYNO, mode));
#else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path, oflag, mode)));
#endif
if (fd_ == -1)
fmt::ThrowSystemError(errno, "cannot open file {}") << path;
}
File::~File() FMT_NOEXCEPT(true) {
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
fmt::ReportSystemError(errno, "cannot close file");
}
void File::close() {
if (fd_ == -1)
return;
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1;
if (result != 0)
fmt::ThrowSystemError(errno, "cannot close file");
}
std::streamsize File::read(void *buffer, std::size_t count) {
std::streamsize result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, ConvertRWCount(count))));
if (result == -1)
fmt::ThrowSystemError(errno, "cannot read from file");
return result;
}
std::streamsize File::write(const void *buffer, std::size_t count) {
std::streamsize result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, ConvertRWCount(count))));
if (result == -1)
fmt::ThrowSystemError(errno, "cannot write to file");
return result;
}
File File::dup(int fd) {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1)
fmt::ThrowSystemError(errno, "cannot duplicate file descriptor {}") << fd;
return File(new_fd);
}
void File::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
fmt::ThrowSystemError(errno,
"cannot duplicate file descriptor {} to {}") << fd_ << fd;
}
}
void File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT(true) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1)
ec = ErrorCode(errno);
}
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_end.close();
write_end.close();
int fds[2] = {};
#ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
#else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
#endif
if (result != 0)
fmt::ThrowSystemError(errno, "cannot create pipe");
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = File(fds[0]);
write_end = File(fds[1]);
}
BufferedFile File::fdopen(const char *mode) {
// Don't retry as fdopen doesn't return EINTR.
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f) {
fmt::ThrowSystemError(errno,
"cannot associate stream with file descriptor");
}
BufferedFile file(f);
fd_ = -1;
return file;
}

284
posix.h Normal file
View File

@ -0,0 +1,284 @@
/*
A C++ interface to POSIX functions.
Copyright (c) 2014, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FMT_POSIX_H
#define FMT_POSIX_H
#include <stdio.h>
#include <fcntl.h>
#include <cstddef>
#include <ios>
#ifndef FMT_POSIX
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// An error code.
class ErrorCode {
private:
int value_;
public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT(true) : value_(value) {}
int get() const FMT_NOEXCEPT(true) { return value_; }
};
// A buffered file.
class BufferedFile {
private:
FILE *file_;
friend class File;
explicit BufferedFile(FILE *f) : file_(f) {}
public:
// Constructs a BufferedFile object which doesn't represent any file.
BufferedFile() FMT_NOEXCEPT(true) : file_(0) {}
// Destroys the object closing the file it represents if any.
~BufferedFile() FMT_NOEXCEPT(true);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy {
FILE *file;
};
public:
// A "move constructor" for moving from a temporary.
BufferedFile(Proxy p) FMT_NOEXCEPT(true) : file_(p.file) {}
// A "move constructor" for for moving from an lvalue.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT(true) : file_(f.file_) {
f.file_ = 0;
}
// A "move assignment operator" for moving from a temporary.
BufferedFile &operator=(Proxy p) {
close();
file_ = p.file;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
BufferedFile &operator=(BufferedFile &other) {
close();
file_ = other.file_;
other.file_ = 0;
return *this;
}
// Returns a proxy object for moving from a temporary:
// BufferedFile file = BufferedFile(...);
operator Proxy() FMT_NOEXCEPT(true) {
Proxy p = {file_};
file_ = 0;
return p;
}
#else
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(BufferedFile);
public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT(true) : file_(other.file_) {
other.file_ = 0;
}
BufferedFile& operator=(BufferedFile &&other) {
close();
file_ = other.file_;
other.file_ = 0;
return *this;
}
#endif
// Closes the file.
void close();
// Returns the pointer to a FILE object representing this file.
FILE *get() const { return file_; }
int fileno() const;
};
// A file. Closed file is represented by a File object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT(true) may throw
// fmt::SystemError in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class File {
private:
int fd_; // File descriptor.
// Constructs a File object with a given descriptor.
explicit File(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a File object which doesn't represent any file.
File() FMT_NOEXCEPT(true) : fd_(-1) {}
// Opens a file and constructs a File object representing this file.
File(const char *path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy {
int fd;
};
public:
// A "move constructor" for moving from a temporary.
File(Proxy p) FMT_NOEXCEPT(true) : fd_(p.fd) {}
// A "move constructor" for for moving from an lvalue.
File(File &other) FMT_NOEXCEPT(true) : fd_(other.fd_) {
other.fd_ = -1;
}
// A "move assignment operator" for moving from a temporary.
File &operator=(Proxy p) {
close();
fd_ = p.fd;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
File &operator=(File &other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Returns a proxy object for moving from a temporary:
// File file = File(...);
operator Proxy() FMT_NOEXCEPT(true) {
Proxy p = {fd_};
fd_ = -1;
return p;
}
#else
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(File);
public:
File(File &&other) FMT_NOEXCEPT(true) : fd_(other.fd_) {
other.fd_ = -1;
}
File& operator=(File &&other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
#endif
// Destroys the object closing the file it represents if any.
~File() FMT_NOEXCEPT(true);
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT(true) { return fd_; }
// Closes the file.
void close();
// 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.
std::streamsize write(const void *buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
static File dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT(true);
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
static void pipe(File &read_end, File &write_end);
// Creates a BufferedFile object associated with this file and detaches
// this File object from the file.
BufferedFile fdopen(const char *mode);
};
#if !FMT_USE_RVALUE_REFERENCES
namespace std {
// For compatibility with C++98.
inline BufferedFile &move(BufferedFile &f) { return f; }
inline File &move(File &f) { return f; }
}
#endif
#endif // FMT_POSIX_H

View File

@ -29,171 +29,7 @@
#if FMT_USE_FILE_DESCRIPTORS
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32
# include <unistd.h>
#else
# include <io.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# define S_IRUSR _S_IREAD
# define S_IWUSR _S_IWRITE
#endif // _WIN32
// Retries the expression while it evaluates to -1 and error equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY(result, expression) \
do { \
result = (expression); \
} while (result == -1 && errno == EINTR)
#else
# define FMT_RETRY(result, expression) result = (expression)
#endif
namespace {
#ifdef _WIN32
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned ConvertRWCount(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#else
inline std::size_t ConvertRWCount(std::size_t count) { return count; }
#endif
}
BufferedFile::~BufferedFile() FMT_NOEXCEPT(true) {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
fmt::ReportSystemError(errno, "cannot close file");
}
void BufferedFile::close() {
if (!file_)
return;
int result = FMT_SYSTEM(fclose(file_));
file_ = 0;
if (result != 0)
fmt::ThrowSystemError(errno, "cannot close file");
}
int BufferedFile::fileno() const {
int fd = FMT_POSIX_CALL(fileno(file_));
if (fd == -1)
fmt::ThrowSystemError(errno, "cannot get file descriptor");
return fd;
}
File::File(const char *path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#ifdef _WIN32
fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path, oflag, _SH_DENYNO, mode));
#else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path, oflag, mode)));
#endif
if (fd_ == -1)
fmt::ThrowSystemError(errno, "cannot open file {}") << path;
}
File::~File() FMT_NOEXCEPT(true) {
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
fmt::ReportSystemError(errno, "cannot close file");
}
void File::close() {
if (fd_ == -1)
return;
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1;
if (result != 0)
fmt::ThrowSystemError(errno, "cannot close file");
}
std::streamsize File::read(void *buffer, std::size_t count) {
std::streamsize result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, ConvertRWCount(count))));
if (result == -1)
fmt::ThrowSystemError(errno, "cannot read from file");
return result;
}
std::streamsize File::write(const void *buffer, std::size_t count) {
std::streamsize result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, ConvertRWCount(count))));
if (result == -1)
fmt::ThrowSystemError(errno, "cannot write to file");
return result;
}
File File::dup(int fd) {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1)
fmt::ThrowSystemError(errno, "cannot duplicate file descriptor {}") << fd;
return File(new_fd);
}
void File::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
fmt::ThrowSystemError(errno,
"cannot duplicate file descriptor {} to {}") << fd_ << fd;
}
}
void File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT(true) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1)
ec = ErrorCode(errno);
}
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_end.close();
write_end.close();
int fds[2] = {};
#ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
#else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
#endif
if (result != 0)
fmt::ThrowSystemError(errno, "cannot create pipe");
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = File(fds[0]);
write_end = File(fds[1]);
}
BufferedFile File::fdopen(const char *mode) {
// Don't retry as fdopen doesn't return EINTR.
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f) {
fmt::ThrowSystemError(errno,
"cannot associate stream with file descriptor");
}
BufferedFile file(f);
fd_ = -1;
return file;
}
#include "posix.cc"
void OutputRedirect::Flush() {
#if EOF != -1

View File

@ -28,16 +28,8 @@
#ifndef FMT_GTEST_EXTRA_H
#define FMT_GTEST_EXTRA_H
#include <cstddef>
#include <ios>
#include <string>
#include <stdio.h>
#if FMT_USE_FILE_DESCRIPTORS
# include <fcntl.h>
#endif
#include <gtest/gtest.h>
#include "format.h"
@ -46,6 +38,14 @@
# include "posix-test.h"
#endif
#ifndef FMT_USE_FILE_DESCRIPTORS
# define FMT_USE_FILE_DESCRIPTORS 0
#endif
#if FMT_USE_FILE_DESCRIPTORS
# include "posix.h"
#endif
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::AssertionResult gtest_ar = ::testing::AssertionSuccess()) { \
@ -92,259 +92,8 @@ std::string FormatSystemErrorMessage(int error_code, fmt::StringRef message);
EXPECT_THROW_MSG(statement, fmt::SystemError, \
FormatSystemErrorMessage(error_code, message))
#ifndef FMT_USE_FILE_DESCRIPTORS
# define FMT_USE_FILE_DESCRIPTORS 0
#endif
#if FMT_USE_FILE_DESCRIPTORS
#ifndef FMT_POSIX
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// An error code.
class ErrorCode {
private:
int value_;
public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT(true) : value_(value) {}
int get() const FMT_NOEXCEPT(true) { return value_; }
};
// A buffered file.
class BufferedFile {
private:
FILE *file_;
friend class File;
explicit BufferedFile(FILE *f) : file_(f) {}
public:
// Constructs a BufferedFile object which doesn't represent any file.
BufferedFile() FMT_NOEXCEPT(true) : file_(0) {}
// Destroys the object closing the file it represents if any.
~BufferedFile() FMT_NOEXCEPT(true);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy {
FILE *file;
};
public:
// A "move constructor" for moving from a temporary.
BufferedFile(Proxy p) FMT_NOEXCEPT(true) : file_(p.file) {}
// A "move constructor" for for moving from an lvalue.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT(true) : file_(f.file_) {
f.file_ = 0;
}
// A "move assignment operator" for moving from a temporary.
BufferedFile &operator=(Proxy p) {
close();
file_ = p.file;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
BufferedFile &operator=(BufferedFile &other) {
close();
file_ = other.file_;
other.file_ = 0;
return *this;
}
// Returns a proxy object for moving from a temporary:
// BufferedFile file = BufferedFile(...);
operator Proxy() FMT_NOEXCEPT(true) {
Proxy p = {file_};
file_ = 0;
return p;
}
#else
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(BufferedFile);
public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT(true) : file_(other.file_) {
other.file_ = 0;
}
BufferedFile& operator=(BufferedFile &&other) {
close();
file_ = other.file_;
other.file_ = 0;
return *this;
}
#endif
// Closes the file.
void close();
// Returns the pointer to a FILE object representing this file.
FILE *get() const { return file_; }
int fileno() const;
};
// A file. Closed file is represented by a File object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT(true) may throw
// fmt::SystemError in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class File {
private:
int fd_; // File descriptor.
// Constructs a File object with a given descriptor.
explicit File(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a File object which doesn't represent any file.
File() FMT_NOEXCEPT(true) : fd_(-1) {}
// Opens a file and constructs a File object representing this file.
File(const char *path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy {
int fd;
};
public:
// A "move constructor" for moving from a temporary.
File(Proxy p) FMT_NOEXCEPT(true) : fd_(p.fd) {}
// A "move constructor" for for moving from an lvalue.
File(File &other) FMT_NOEXCEPT(true) : fd_(other.fd_) {
other.fd_ = -1;
}
// A "move assignment operator" for moving from a temporary.
File &operator=(Proxy p) {
close();
fd_ = p.fd;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
File &operator=(File &other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Returns a proxy object for moving from a temporary:
// File file = File(...);
operator Proxy() FMT_NOEXCEPT(true) {
Proxy p = {fd_};
fd_ = -1;
return p;
}
#else
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(File);
public:
File(File &&other) FMT_NOEXCEPT(true) : fd_(other.fd_) {
other.fd_ = -1;
}
File& operator=(File &&other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
#endif
// Destroys the object closing the file it represents if any.
~File() FMT_NOEXCEPT(true);
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT(true) { return fd_; }
// Closes the file.
void close();
// 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.
std::streamsize write(const void *buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
static File dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT(true);
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
static void pipe(File &read_end, File &write_end);
// Creates a BufferedFile object associated with this file and detaches
// this File object from the file.
BufferedFile fdopen(const char *mode);
};
#if !FMT_USE_RVALUE_REFERENCES
namespace std {
// For compatibility with C++98.
inline BufferedFile &move(BufferedFile &f) { return f; }
inline File &move(File &f) { return f; }
}
#endif
// Captures file output by redirecting it to a pipe.
// The output it can handle is limited by the pipe capacity.
class OutputRedirect {