diff --git a/posix.cc b/posix.cc new file mode 100644 index 00000000..9d883603 --- /dev/null +++ b/posix.cc @@ -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 +#include +#include +#include + +#ifndef _WIN32 +# include +#else +# include + +# 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(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; +} diff --git a/posix.h b/posix.h new file mode 100644 index 00000000..ef495bb0 --- /dev/null +++ b/posix.h @@ -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 +#include + +#include +#include + +#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 diff --git a/test/gtest-extra.cc b/test/gtest-extra.cc index 3e103afd..41a80a28 100644 --- a/test/gtest-extra.cc +++ b/test/gtest-extra.cc @@ -29,171 +29,7 @@ #if FMT_USE_FILE_DESCRIPTORS -#include -#include -#include -#include - -#ifndef _WIN32 -# include -#else -# include - -# 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(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 diff --git a/test/gtest-extra.h b/test/gtest-extra.h index 1c5e373d..3211c062 100644 --- a/test/gtest-extra.h +++ b/test/gtest-extra.h @@ -28,16 +28,8 @@ #ifndef FMT_GTEST_EXTRA_H #define FMT_GTEST_EXTRA_H -#include -#include #include -#include - -#if FMT_USE_FILE_DESCRIPTORS -# include -#endif - #include #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 {