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

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 {