This commit is contained in:
Remotion 2018-05-20 02:35:18 +02:00
commit 3e9c0d5879
19 changed files with 389 additions and 275 deletions

View File

@ -12,7 +12,28 @@
of the library for standardization in `P0645R2 Text Formatting of the library for standardization in `P0645R2 Text Formatting
<https://wg21.link/P0645>`_. <https://wg21.link/P0645>`_.
* Implemented ``constexpr`` parsing of format strings. * Implemented ``constexpr`` parsing of format strings and `compile-time format
string checks
<http://fmtlib.net/dev/api.html#compile-time-format-string-checks>`_. For
example
.. code:: c++
#include <fmt/format.h>
std::string s = format(fmt("{:d}"), "foo");
gives a compile-time error because ``d`` is an invalid specifier for strings
(`godbolt <https://godbolt.org/g/rnCy9Q>`_)::
...
<source>:4:19: note: in instantiation of function template specialization 'fmt::v5::format<S, char [4]>' requested here
std::string s = format(fmt("{:d}"), "foo");
^
format.h:1337:13: note: non-constexpr function 'on_error' cannot be used in a constant expression
handler.on_error("invalid type specifier");
Compile-time checks require relaxed ``constexpr`` (C++14 feature) support. If
the latter is not available, checks will be performed at runtime.
* Separated format string parsing and formatting in the extension API to enable * Separated format string parsing and formatting in the extension API to enable
compile-time format string processing. For example compile-time format string processing. For example
@ -45,7 +66,7 @@
std::string s = format(fmt("{:x}"), S()); std::string s = format(fmt("{:x}"), S());
will give a compile-time error due to invalid format specifier (`godbolt gives a compile-time error due to invalid format specifier (`godbolt
<https://godbolt.org/g/ywhrPp>`_):: <https://godbolt.org/g/ywhrPp>`_)::
... ...
@ -63,9 +84,20 @@
std::vector<char> out; std::vector<char> out;
fmt::format_to(std::back_inserter(out), "{}", 42); fmt::format_to(std::back_inserter(out), "{}", 42);
* Added the `format_to_n
<http://fmtlib.net/dev/api.html#_CPPv2N3fmt11format_to_nE8OutputItNSt6size_tE11string_viewDpRK4Args>`_
function that restricts the output to the specified number of characters
(`#298 <https://github.com/fmtlib/fmt/issues/298>`_):
.. code:: c++
char out[4];
fmt::format_to_n(out, sizeof(out), "{}", 12345);
// out == "1234" (without terminating '\0')
* Added the `formatted_size * Added the `formatted_size
<http://fmtlib.net/dev/api.html#output-iterator-support>`_ function for <http://fmtlib.net/dev/api.html#_CPPv2N3fmt14formatted_sizeE11string_viewDpRK4Args>`_
computing output size: function for computing the output size:
.. code:: c++ .. code:: c++
@ -101,6 +133,9 @@
vreport_error(format, fmt::make_format_args(args...)); vreport_error(format, fmt::make_format_args(args...));
} }
* Added the ``make_printf_args`` function for capturing ``printf`` arguments.
Thanks `@Kronuz (Germán Méndez Bravo) <https://github.com/Kronuz>`_.
* Added prefix ``v`` to non-variadic functions taking ``format_args`` to * Added prefix ``v`` to non-variadic functions taking ``format_args`` to
distinguish them from variadic ones: distinguish them from variadic ones:
@ -141,30 +176,46 @@
* Disallowed formatting of multibyte strings into a wide character target * Disallowed formatting of multibyte strings into a wide character target
(`#606 <https://github.com/fmtlib/fmt/pull/606>`_). (`#606 <https://github.com/fmtlib/fmt/pull/606>`_).
* Added a section describing `the use of header-only target with CMake * Improved documentation (
<http://fmtlib.net/dev/usage.html#header-only-usage-with-cmake>`_ to the docs `#515 <https://github.com/fmtlib/fmt/pull/515>`_,
(`#515 <https://github.com/fmtlib/fmt/pull/515>`_).
Thanks `@ibell (Ian Bell) <https://github.com/ibell>`_.
* Added a `note about errno <http://fmtlib.net/latest/index.html#safety>`_ to the
documentation (
`#614 <https://github.com/fmtlib/fmt/issues/614>`_, `#614 <https://github.com/fmtlib/fmt/issues/614>`_,
`#617 <https://github.com/fmtlib/fmt/pull/617>`_). `#617 <https://github.com/fmtlib/fmt/pull/617>`_,
Thanks `@mihaitodor (Mihai Todor) <https://github.com/mihaitodor>`_. `#661 <https://github.com/fmtlib/fmt/pull/661>`_,
`#680 <https://github.com/fmtlib/fmt/pull/680>`_).
Thanks `@ibell (Ian Bell) <https://github.com/ibell>`_,
`@mihaitodor (Mihai Todor) <https://github.com/mihaitodor>`_, and
`@johnthagen <https://github.com/johnthagen>`_.
* Implemented more efficient handling of large number of format arguments. * Implemented more efficient handling of large number of format arguments.
* Added debug postfix ``d`` to the `fmt`` library name
(`#636 <https://github.com/fmtlib/fmt/issues/636>`_).
* Removed unnecessary ``fmt/`` prefix in includes * Removed unnecessary ``fmt/`` prefix in includes
(`#397 <https://github.com/fmtlib/fmt/pull/397>`_). (`#397 <https://github.com/fmtlib/fmt/pull/397>`_).
Thanks `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_. Thanks `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_.
* Renamed ``CHAR_WIDTH`` to ``CHAR_SIZE`` to avoid collision with ISO/IEC TS * Moved ``fmt/*.h`` to ``include/fmt/*.h`` to prevent irrelevant files and
18661-1:2014 macro. directories appearing on the include search paths when fmt is used as a
subproject and moved source files to the ``src`` directory.
* Added qmake project file ``support/fmt.pro``
(`#641 <https://github.com/fmtlib/fmt/pull/641>`_).
Thanks `@cowo78 (Giuseppe Corbelli) <https://github.com/cowo78>`_.
* Added Gradle build file ``support/build.gradle``
(`#649 <https://github.com/fmtlib/fmt/pull/649>`_).
Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_.
* Removed ``FMT_CPPFORMAT`` CMake option.
* Fixed a name conflict with the macro ``CHAR_WIDTH`` in glibc * Fixed a name conflict with the macro ``CHAR_WIDTH`` in glibc
(`#616 <https://github.com/fmtlib/fmt/pull/616>`_). (`#616 <https://github.com/fmtlib/fmt/pull/616>`_).
Thanks `@aroig (Abdó Roig-Maranges) <https://github.com/aroig>`_. Thanks `@aroig (Abdó Roig-Maranges) <https://github.com/aroig>`_.
* Fixed handling of nested braces in ``fmt::join``
(`#638 <https://github.com/fmtlib/fmt/issues/638>`_).
* Added ``SOURCELINK_SUFFIX`` for compatibility with Sphinx 1.5 * Added ``SOURCELINK_SUFFIX`` for compatibility with Sphinx 1.5
(`#497 <https://github.com/fmtlib/fmt/pull/497>`_). (`#497 <https://github.com/fmtlib/fmt/pull/497>`_).
Thanks `@ginggs (Graham Inggs) <https://github.com/ginggs>`_. Thanks `@ginggs (Graham Inggs) <https://github.com/ginggs>`_.
@ -173,6 +224,68 @@
(`#626 <https://github.com/fmtlib/fmt/pull/626>`_). (`#626 <https://github.com/fmtlib/fmt/pull/626>`_).
Thanks `@aroig (Abdó Roig-Maranges) <https://github.com/aroig>`_. Thanks `@aroig (Abdó Roig-Maranges) <https://github.com/aroig>`_.
* Fixed various compiler warnings (
`#640 <https://github.com/fmtlib/fmt/pull/640>`_,
`#656 <https://github.com/fmtlib/fmt/pull/656>`_,
`#679 <https://github.com/fmtlib/fmt/pull/679>`_,
`#681 <https://github.com/fmtlib/fmt/pull/681>`_,
`#705 <https://github.com/fmtlib/fmt/pull/705>`_).
Thanks `@peterbell10 <https://github.com/peterbell10>`_,
`@LarsGullik <https://github.com/LarsGullik>`_,
`@foonathan (Jonathan Müller) <https://github.com/foonathan>`_,
`@eliaskosunen (Elias Kosunen) <https://github.com/eliaskosunen>`_, and
`@christianparpart (Christian Parpart) <https://github.com/christianparpart>`_.
* Worked around an MSVC bug and fixed several warnings
(`#653 <https://github.com/fmtlib/fmt/pull/653>`_).
Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
* Worked around GCC bug 67371
(`#682 <https://github.com/fmtlib/fmt/issues/682>`_).
* Fixed compilation with ``-fno-exceptions``
(`#655 <https://github.com/fmtlib/fmt/pull/655>`_).
Thanks `@chenxiaolong (Andrew Gunnerson) <https://github.com/chenxiaolong>`_.
* Made ``constexpr remove_prefix`` gcc version check tighter
(`#648 <https://github.com/fmtlib/fmt/issues/648>`_).
* Renamed internal type enum constants to prevent collision with poorly written
C libraries (`#644 <https://github.com/fmtlib/fmt/issues/644>`_).
* Added detection of ``wostream operator<<``
(`#650 <https://github.com/fmtlib/fmt/issues/650>`_).
* Fixed compilation on OpenBSD
(`#660 <https://github.com/fmtlib/fmt/pull/660>`_).
Thanks `@hubslave <https://github.com/hubslave>`_.
* Fixed compilation when there is a mismatch between ``-std`` options between
the library and user code
(`#664 <https://github.com/fmtlib/fmt/issues/664>`_).
* Improved generated binary code on GCC 7 and older
(`#668 <https://github.com/fmtlib/fmt/issues/668>`_).
* Fixed handling of numeric alignment with no width
(`#675 <https://github.com/fmtlib/fmt/issues/675>`_).
* Fixed handling of empty strings in UTF8/16 converters
(`#676 <https://github.com/fmtlib/fmt/pull/676>`_).
Thanks `@vgalka-sl (Vasili Galka) <https://github.com/vgalka-sl>`_.
* Fixed formatting of an empty ``string_view``
(`#689 <https://github.com/fmtlib/fmt/issues/689>`_).
* Fixed detection of ``string_view`` on libc++
(`#686 <https://github.com/fmtlib/fmt/issues/686>`_).
* Fixed DLL issues (`#696 <https://github.com/fmtlib/fmt/pull/696>`_).
Thanks `@sebkoenig <https://github.com/sebkoenig>`_.
* Fixed compile checks for mixing narrow and wide strings
(`#690 <https://github.com/fmtlib/fmt/issues/690>`_).
4.1.0 - 2017-12-20 4.1.0 - 2017-12-20
------------------ ------------------
@ -234,7 +347,7 @@
`@thelostt (Mário Feroldi) <https://github.com/thelostt>`_, and `@thelostt (Mário Feroldi) <https://github.com/thelostt>`_, and
`@Manu343726 (Manu Sánchez) <https://github.com/Manu343726>`_. `@Manu343726 (Manu Sánchez) <https://github.com/Manu343726>`_.
* Improved CMake: Used GNUInstallDirs to set installation location * Improved CMake: Used ``GNUInstallDirs`` to set installation location
(`#610 <https://github.com/fmtlib/fmt/pull/610>`_) and fixed warnings (`#610 <https://github.com/fmtlib/fmt/pull/610>`_) and fixed warnings
(`#536 <https://github.com/fmtlib/fmt/pull/536>`_ and (`#536 <https://github.com/fmtlib/fmt/pull/536>`_ and
`#556 <https://github.com/fmtlib/fmt/pull/556>`_). `#556 <https://github.com/fmtlib/fmt/pull/556>`_).

View File

@ -629,8 +629,9 @@ inline typename std::enable_if<
template <typename C, typename T, typename Char = typename C::char_type> template <typename C, typename T, typename Char = typename C::char_type>
inline typename std::enable_if< inline typename std::enable_if<
!convert_to_int<T, Char>::value && !convert_to_int<T, Char>::value &&
!std::is_convertible<T, basic_string_view<Char>>::value && !std::is_convertible<T, basic_string_view<Char>>::value,
!std::is_convertible<T, std::basic_string<Char>>::value, // Implicit conversion to std::string is not handled here because it's
// unsafe: https://github.com/fmtlib/fmt/issues/729
typed_value<C, custom_type>>::type typed_value<C, custom_type>>::type
make_value(const T &val) { return val; } make_value(const T &val) { return val; }

View File

@ -113,14 +113,14 @@ int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer"); FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
class StrError { class dispatcher {
private: private:
int error_code_; int error_code_;
char *&buffer_; char *&buffer_;
std::size_t buffer_size_; std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings. // A noop assignment operator to avoid bogus warnings.
void operator=(const StrError &) {} void operator=(const dispatcher &) {}
// Handle the result of XSI-compliant version of strerror_r. // Handle the result of XSI-compliant version of strerror_r.
int handle(int result) { int handle(int result) {
@ -157,14 +157,14 @@ int safe_strerror(
} }
public: public:
StrError(int err_code, char *&buf, std::size_t buf_size) dispatcher(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() { int run() {
return handle(strerror_r(error_code_, buffer_, buffer_size_)); return handle(strerror_r(error_code_, buffer_, buffer_size_));
} }
}; };
return StrError(error_code, buffer, buffer_size).run(); return dispatcher(error_code, buffer, buffer_size).run();
} }
void format_error_code(internal::buffer &out, int error_code, void format_error_code(internal::buffer &out, int error_code,

View File

@ -2974,7 +2974,7 @@ FMT_API void report_windows_error(int error_code,
#endif #endif
/** Fast integer formatter. */ /** Fast integer formatter. */
class FormatInt { class format_int {
private: private:
// Buffer should be large enough to hold all digits (digits10 + 1), // Buffer should be large enough to hold all digits (digits10 + 1),
// a sign and a null character. // a sign and a null character.
@ -3015,12 +3015,12 @@ class FormatInt {
} }
public: public:
explicit FormatInt(int value) { format_signed(value); } explicit format_int(int value) { format_signed(value); }
explicit FormatInt(long value) { format_signed(value); } explicit format_int(long value) { format_signed(value); }
explicit FormatInt(long long value) { format_signed(value); } explicit format_int(long long value) { format_signed(value); }
explicit FormatInt(unsigned value) : str_(format_decimal(value)) {} explicit format_int(unsigned value) : str_(format_decimal(value)) {}
explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {} explicit format_int(unsigned long value) : str_(format_decimal(value)) {}
explicit FormatInt(unsigned long long value) : str_(format_decimal(value)) {} explicit format_int(unsigned long long value) : str_(format_decimal(value)) {}
/** Returns the number of characters written to the output buffer. */ /** Returns the number of characters written to the output buffer. */
std::size_t size() const { std::size_t size() const {
@ -3647,7 +3647,7 @@ FMT_END_NAMESPACE
#include <fmt/format.h> #include <fmt/format.h>
// A compile-time error because 'd' is an invalid specifier for strings. // A compile-time error because 'd' is an invalid specifier for strings.
std::string s = fmt::format(fmt("{:d}"), "foo"); std::string s = format(fmt("{:d}"), "foo");
\endrst \endrst
*/ */
# define fmt(s) FMT_STRING(s) # define fmt(s) FMT_STRING(s)

View File

@ -15,7 +15,7 @@ FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
template <class Char> template <class Char>
class FormatBuf : public std::basic_streambuf<Char> { class formatbuf : public std::basic_streambuf<Char> {
private: private:
typedef typename std::basic_streambuf<Char>::int_type int_type; typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type; typedef typename std::basic_streambuf<Char>::traits_type traits_type;
@ -23,7 +23,7 @@ class FormatBuf : public std::basic_streambuf<Char> {
basic_buffer<Char> &buffer_; basic_buffer<Char> &buffer_;
public: public:
FormatBuf(basic_buffer<Char> &buffer) : buffer_(buffer) {} formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {}
protected: protected:
// The put-area is actually always empty. This makes the implementation // The put-area is actually always empty. This makes the implementation
@ -90,7 +90,7 @@ void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) {
template <typename Char, typename T> template <typename Char, typename T>
void format_value(basic_buffer<Char> &buffer, const T &value) { void format_value(basic_buffer<Char> &buffer, const T &value) {
internal::FormatBuf<Char> format_buf(buffer); internal::formatbuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf); std::basic_ostream<Char> output(&format_buf);
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value; output << value;

View File

@ -113,31 +113,31 @@ typedef basic_cstring_view<char> cstring_view;
typedef basic_cstring_view<wchar_t> wcstring_view; typedef basic_cstring_view<wchar_t> wcstring_view;
// An error code. // An error code.
class ErrorCode { class error_code {
private: private:
int value_; int value_;
public: public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {} explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; } int get() const FMT_NOEXCEPT { return value_; }
}; };
// A buffered file. // A buffered file.
class BufferedFile { class buffered_file {
private: private:
FILE *file_; FILE *file_;
friend class File; friend class file;
explicit BufferedFile(FILE *f) : file_(f) {} explicit buffered_file(FILE *f) : file_(f) {}
public: public:
// Constructs a BufferedFile object which doesn't represent any file. // Constructs a buffered_file object which doesn't represent any file.
BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {} buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~BufferedFile() FMT_DTOR_NOEXCEPT; FMT_API ~buffered_file() FMT_DTOR_NOEXCEPT;
#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
@ -152,22 +152,22 @@ class BufferedFile {
public: public:
// A "move constructor" for moving from a temporary. // A "move constructor" for moving from a temporary.
BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {} buffered_file(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
// A "move constructor" for moving from an lvalue. // A "move constructor" for moving from an lvalue.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) { buffered_file(buffered_file &f) FMT_NOEXCEPT : file_(f.file_) {
f.file_ = FMT_NULL; f.file_ = FMT_NULL;
} }
// A "move assignment operator" for moving from a temporary. // A "move assignment operator" for moving from a temporary.
BufferedFile &operator=(Proxy p) { buffered_file &operator=(Proxy p) {
close(); close();
file_ = p.file; file_ = p.file;
return *this; return *this;
} }
// A "move assignment operator" for moving from an lvalue. // A "move assignment operator" for moving from an lvalue.
BufferedFile &operator=(BufferedFile &other) { buffered_file &operator=(buffered_file &other) {
close(); close();
file_ = other.file_; file_ = other.file_;
other.file_ = FMT_NULL; other.file_ = FMT_NULL;
@ -175,7 +175,7 @@ public:
} }
// Returns a proxy object for moving from a temporary: // Returns a proxy object for moving from a temporary:
// BufferedFile file = BufferedFile(...); // buffered_file file = buffered_file(...);
operator Proxy() FMT_NOEXCEPT { operator Proxy() FMT_NOEXCEPT {
Proxy p = {file_}; Proxy p = {file_};
file_ = FMT_NULL; file_ = FMT_NULL;
@ -184,14 +184,14 @@ public:
#else #else
private: private:
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile); FMT_DISALLOW_COPY_AND_ASSIGN(buffered_file);
public: public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) { buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = FMT_NULL; other.file_ = FMT_NULL;
} }
BufferedFile& operator=(BufferedFile &&other) { buffered_file& operator=(buffered_file &&other) {
close(); close();
file_ = other.file_; file_ = other.file_;
other.file_ = FMT_NULL; other.file_ = FMT_NULL;
@ -200,7 +200,7 @@ public:
#endif #endif
// Opens a file. // Opens a file.
FMT_API BufferedFile(cstring_view filename, cstring_view mode); FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file. // Closes the file.
FMT_API void close(); FMT_API void close();
@ -222,18 +222,18 @@ public:
} }
}; };
// A file. Closed file is represented by a File object with descriptor -1. // A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw // Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as // fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather // closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the // than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler. // invalid parameter handler with _set_invalid_parameter_handler.
class File { class file {
private: private:
int fd_; // File descriptor. int fd_; // File descriptor.
// Constructs a File object with a given descriptor. // Constructs a file object with a given descriptor.
explicit File(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.
@ -243,11 +243,11 @@ class File {
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
}; };
// Constructs a File object which doesn't represent any file. // Constructs a file object which doesn't represent any file.
File() FMT_NOEXCEPT : fd_(-1) {} file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a File object representing this file. // Opens a file and constructs a file object representing this file.
FMT_API File(cstring_view path, int oflag); FMT_API file(cstring_view 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
@ -262,22 +262,22 @@ class File {
public: public:
// A "move constructor" for moving from a temporary. // A "move constructor" for moving from a temporary.
File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {} file(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
// A "move constructor" for moving from an lvalue. // A "move constructor" for moving from an lvalue.
File(File &other) FMT_NOEXCEPT : fd_(other.fd_) { file(file &other) FMT_NOEXCEPT : 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.
File &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.
File &operator=(File &other) { file &operator=(file &other) {
close(); close();
fd_ = other.fd_; fd_ = other.fd_;
other.fd_ = -1; other.fd_ = -1;
@ -285,7 +285,7 @@ class File {
} }
// Returns a proxy object for moving from a temporary: // Returns a proxy object for moving from a temporary:
// File file = File(...); // file f = file(...);
operator Proxy() FMT_NOEXCEPT { operator Proxy() FMT_NOEXCEPT {
Proxy p = {fd_}; Proxy p = {fd_};
fd_ = -1; fd_ = -1;
@ -294,14 +294,14 @@ class File {
#else #else
private: private:
FMT_DISALLOW_COPY_AND_ASSIGN(File); FMT_DISALLOW_COPY_AND_ASSIGN(file);
public: public:
File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) { file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) {
other.fd_ = -1; other.fd_ = -1;
} }
File& operator=(File &&other) { file& operator=(file &&other) {
close(); close();
fd_ = other.fd_; fd_ = other.fd_;
other.fd_ = -1; other.fd_ = -1;
@ -310,7 +310,7 @@ class File {
#endif #endif
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~File() FMT_DTOR_NOEXCEPT; FMT_API ~file() FMT_DTOR_NOEXCEPT;
// Returns the file descriptor. // Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; } int descriptor() const FMT_NOEXCEPT { return fd_; }
@ -330,7 +330,7 @@ class File {
// Duplicates a file descriptor with the dup function and returns // Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object. // the duplicate as a file object.
FMT_API static File dup(int fd); FMT_API 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. // necessary.
@ -338,15 +338,15 @@ class File {
// 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. // necessary.
FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT; FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading // Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively. // and writing respectively.
FMT_API static void pipe(File &read_end, File &write_end); FMT_API static void pipe(file &read_end, file &write_end);
// Creates a BufferedFile object associated with this file and detaches // Creates a buffered_file object associated with this file and detaches
// this File object from the file. // this file object from the file.
FMT_API BufferedFile fdopen(const char *mode); FMT_API buffered_file fdopen(const char *mode);
}; };
// Returns the memory page size. // Returns the memory page size.
@ -409,8 +409,8 @@ FMT_END_NAMESPACE
#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 fmt::BufferedFile &move(fmt::BufferedFile &f) { return f; } inline fmt::buffered_file &move(fmt::buffered_file &f) { return f; }
inline fmt::File &move(fmt::File &f) { return f; } inline fmt::file &move(fmt::file &f) { return f; }
} }
#endif #endif

View File

@ -22,11 +22,11 @@ inline null<> gmtime_s(...) { return null<>(); }
// Thread-safe replacement for std::localtime // Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) { inline std::tm localtime(std::time_t time) {
struct LocalTime { struct dispatcher {
std::time_t time_; std::time_t time_;
std::tm tm_; std::tm tm_;
LocalTime(std::time_t t): time_(t) {} dispatcher(std::time_t t): time_(t) {}
bool run() { bool run() {
using namespace fmt::internal; using namespace fmt::internal;
@ -49,7 +49,7 @@ inline std::tm localtime(std::time_t time) {
return tm != FMT_NULL; return tm != FMT_NULL;
} }
}; };
LocalTime lt(time); dispatcher lt(time);
if (lt.run()) if (lt.run())
return lt.tm_; return lt.tm_;
// Too big time values may be unsupported. // Too big time values may be unsupported.
@ -59,11 +59,11 @@ inline std::tm localtime(std::time_t time) {
// Thread-safe replacement for std::gmtime // Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) { inline std::tm gmtime(std::time_t time) {
struct GMTime { struct dispatcher {
std::time_t time_; std::time_t time_;
std::tm tm_; std::tm tm_;
GMTime(std::time_t t): time_(t) {} dispatcher(std::time_t t): time_(t) {}
bool run() { bool run() {
using namespace fmt::internal; using namespace fmt::internal;
@ -85,7 +85,7 @@ inline std::tm gmtime(std::time_t time) {
return tm != FMT_NULL; return tm != FMT_NULL;
} }
}; };
GMTime gt(time); dispatcher gt(time);
if (gt.run()) if (gt.run())
return gt.tm_; return gt.tm_;
// Too big time values may be unsupported. // Too big time values may be unsupported.
@ -94,11 +94,13 @@ inline std::tm gmtime(std::time_t time) {
} }
namespace internal { namespace internal {
inline std::size_t strftime(char *str, std::size_t count, const char *format, const std::tm *time) { inline std::size_t strftime(char *str, std::size_t count, const char *format,
const std::tm *time) {
return std::strftime(str, count, format, time); return std::strftime(str, count, format, time);
} }
inline std::size_t strftime(wchar_t *str, std::size_t count, const wchar_t *format, const std::tm *time) { inline std::size_t strftime(wchar_t *str, std::size_t count,
const wchar_t *format, const std::tm *time) {
return std::wcsftime(str, count, format, time); return std::wcsftime(str, count, format, time);
} }
} }
@ -126,7 +128,8 @@ struct formatter<std::tm, Char> {
std::size_t start = buf.size(); std::size_t start = buf.size();
for (;;) { for (;;) {
std::size_t size = buf.capacity() - start; std::size_t size = buf.capacity() - start;
std::size_t count = internal::strftime(&buf[start], size, &tm_format[0], &tm); std::size_t count =
internal::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) { if (count != 0) {
buf.resize(start + count); buf.resize(start + count);
break; break;

View File

@ -66,19 +66,19 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
BufferedFile::~BufferedFile() FMT_NOEXCEPT { buffered_file::~buffered_file() FMT_NOEXCEPT {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0) if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
report_system_error(errno, "cannot close file"); report_system_error(errno, "cannot close file");
} }
BufferedFile::BufferedFile(cstring_view filename, cstring_view mode) { buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
FMT_RETRY_VAL(file_, FMT_RETRY_VAL(file_,
FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_NULL); FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_NULL);
if (!file_) if (!file_)
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str())); FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
} }
void BufferedFile::close() { void buffered_file::close() {
if (!file_) if (!file_)
return; return;
int result = FMT_SYSTEM(fclose(file_)); int result = FMT_SYSTEM(fclose(file_));
@ -90,14 +90,14 @@ void BufferedFile::close() {
// A macro used to prevent expansion of fileno on broken versions of MinGW. // A macro used to prevent expansion of fileno on broken versions of MinGW.
#define FMT_ARGS #define FMT_ARGS
int BufferedFile::fileno() const { int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1) if (fd == -1)
FMT_THROW(system_error(errno, "cannot get file descriptor")); FMT_THROW(system_error(errno, "cannot get file descriptor"));
return fd; return fd;
} }
File::File(cstring_view path, int oflag) { file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR; int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__) #if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1; fd_ = -1;
@ -109,14 +109,14 @@ File::File(cstring_view path, int oflag) {
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
} }
File::~File() FMT_NOEXCEPT { file::~file() FMT_NOEXCEPT {
// Don't retry close in case of EINTR! // Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0) if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
report_system_error(errno, "cannot close file"); report_system_error(errno, "cannot close file");
} }
void File::close() { void file::close() {
if (fd_ == -1) if (fd_ == -1)
return; return;
// Don't retry close in case of EINTR! // Don't retry close in case of EINTR!
@ -127,7 +127,7 @@ void File::close() {
FMT_THROW(system_error(errno, "cannot close file")); FMT_THROW(system_error(errno, "cannot close file"));
} }
long long File::size() const { long long file::size() const {
#ifdef _WIN32 #ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT // Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// is less than 0x0500 as is the case with some default MinGW builds. // is less than 0x0500 as is the case with some default MinGW builds.
@ -148,12 +148,12 @@ long long File::size() const {
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes")); FMT_THROW(system_error(errno, "cannot get file attributes"));
static_assert(sizeof(long long) >= sizeof(file_stat.st_size), static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
"return type of File::size is not large enough"); "return type of file::size is not large enough");
return file_stat.st_size; return file_stat.st_size;
#endif #endif
} }
std::size_t File::read(void *buffer, std::size_t count) { std::size_t file::read(void *buffer, std::size_t count) {
RWResult result = 0; RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0) if (result < 0)
@ -161,7 +161,7 @@ std::size_t File::read(void *buffer, std::size_t count) {
return internal::to_unsigned(result); return internal::to_unsigned(result);
} }
std::size_t File::write(const void *buffer, std::size_t count) { std::size_t file::write(const void *buffer, std::size_t count) {
RWResult result = 0; RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0) if (result < 0)
@ -169,16 +169,16 @@ std::size_t File::write(const void *buffer, std::size_t count) {
return internal::to_unsigned(result); return internal::to_unsigned(result);
} }
File File::dup(int fd) { file file::dup(int fd) {
// Don't retry as dup doesn't return EINTR. // Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd)); int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1) if (new_fd == -1)
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd)); FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
return File(new_fd); return file(new_fd);
} }
void File::dup2(int fd) { void file::dup2(int fd) {
int result = 0; int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) { if (result == -1) {
@ -187,14 +187,14 @@ void File::dup2(int fd) {
} }
} }
void File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT { void file::dup2(int fd, error_code &ec) FMT_NOEXCEPT {
int result = 0; int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) if (result == -1)
ec = ErrorCode(errno); ec = error_code(errno);
} }
void File::pipe(File &read_end, File &write_end) { 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_end.close(); read_end.close();
@ -213,17 +213,17 @@ void File::pipe(File &read_end, File &write_end) {
FMT_THROW(system_error(errno, "cannot create pipe")); FMT_THROW(system_error(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_end = File(fds[0]); read_end = file(fds[0]);
write_end = File(fds[1]); write_end = file(fds[1]);
} }
BufferedFile File::fdopen(const char *mode) { buffered_file file::fdopen(const char *mode) {
// Don't retry as fdopen doesn't return EINTR. // Don't retry as fdopen doesn't return EINTR.
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode)); FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f) if (!f)
FMT_THROW(system_error(errno, FMT_THROW(system_error(errno,
"cannot associate stream with file descriptor")); "cannot associate stream with file descriptor"));
BufferedFile file(f); buffered_file file(f);
fd_ = -1; fd_ = -1;
return file; return file;
} }

View File

@ -9,8 +9,7 @@ set(CMAKE_REQUIRED_FLAGS ${CPP14_FLAG})
function (generate_source result fragment) function (generate_source result fragment)
set(${result} " set(${result} "
#define FMT_HEADER_ONLY 1 #define FMT_HEADER_ONLY 1
#include \"fmt/posix.h\" #include \"fmt/format.h\"
#include \"fmt/ostream.h\"
int main() { int main() {
${fragment} ${fragment}
} }
@ -58,6 +57,14 @@ expect_compile_error("fmt::format(\"{}\", L\"foo\");")
# mixing UTF-8 with UTF-16/32 can result in an invalid output. # mixing UTF-8 with UTF-16/32 can result in an invalid output.
expect_compile_error("fmt::format(L\"{}\", \"foo\");") expect_compile_error("fmt::format(L\"{}\", \"foo\");")
# Formatting a wide string with a narrow format string is forbidden.
expect_compile_error("
struct S {
operator std::string() const { return std::string(); }
};
fmt::format(\"{}\", S());
")
# Make sure that compiler features detected in the header # Make sure that compiler features detected in the header
# match the features detected in CMake. # match the features detected in CMake.
if (SUPPORTS_USER_DEFINED_LITERALS) if (SUPPORTS_USER_DEFINED_LITERALS)

View File

@ -1072,16 +1072,6 @@ TEST(FormatterTest, FormatStdStringView) {
} }
#endif #endif
struct ConvertibleToString {
std::string s;
ConvertibleToString() : s("foo") {}
operator const std::string &() const { return s; }
};
TEST(FormatterTest, FormatConvertibleToString) {
EXPECT_EQ("foo", format("{}", ConvertibleToString()));
}
struct ConvertibleToStringView { struct ConvertibleToStringView {
operator fmt::string_view() const { return "foo"; } operator fmt::string_view() const { return "foo"; }
}; };
@ -1217,23 +1207,23 @@ TEST(FormatterTest, Examples) {
} }
TEST(FormatIntTest, Data) { TEST(FormatIntTest, Data) {
fmt::FormatInt format_int(42); fmt::format_int format_int(42);
EXPECT_EQ("42", std::string(format_int.data(), format_int.size())); EXPECT_EQ("42", std::string(format_int.data(), format_int.size()));
} }
TEST(FormatIntTest, FormatInt) { TEST(FormatIntTest, FormatInt) {
EXPECT_EQ("42", fmt::FormatInt(42).str()); EXPECT_EQ("42", fmt::format_int(42).str());
EXPECT_EQ(2u, fmt::FormatInt(42).size()); EXPECT_EQ(2u, fmt::format_int(42).size());
EXPECT_EQ("-42", fmt::FormatInt(-42).str()); EXPECT_EQ("-42", fmt::format_int(-42).str());
EXPECT_EQ(3u, fmt::FormatInt(-42).size()); EXPECT_EQ(3u, fmt::format_int(-42).size());
EXPECT_EQ("42", fmt::FormatInt(42ul).str()); EXPECT_EQ("42", fmt::format_int(42ul).str());
EXPECT_EQ("-42", fmt::FormatInt(-42l).str()); EXPECT_EQ("-42", fmt::format_int(-42l).str());
EXPECT_EQ("42", fmt::FormatInt(42ull).str()); EXPECT_EQ("42", fmt::format_int(42ull).str());
EXPECT_EQ("-42", fmt::FormatInt(-42ll).str()); EXPECT_EQ("-42", fmt::format_int(-42ll).str());
std::ostringstream os; std::ostringstream os;
os << std::numeric_limits<int64_t>::max(); os << std::numeric_limits<int64_t>::max();
EXPECT_EQ(os.str(), EXPECT_EQ(os.str(),
fmt::FormatInt(std::numeric_limits<int64_t>::max()).str()); fmt::format_int(std::numeric_limits<int64_t>::max()).str());
} }
template <typename T> template <typename T>

View File

@ -306,20 +306,20 @@ TEST(UtilTest, FormatSystemError) {
#if FMT_USE_FILE_DESCRIPTORS #if FMT_USE_FILE_DESCRIPTORS
using fmt::BufferedFile; using fmt::buffered_file;
using fmt::ErrorCode; using fmt::error_code;
using fmt::File; using fmt::file;
TEST(ErrorCodeTest, Ctor) { TEST(ErrorCodeTest, Ctor) {
EXPECT_EQ(0, ErrorCode().get()); EXPECT_EQ(0, error_code().get());
EXPECT_EQ(42, ErrorCode(42).get()); EXPECT_EQ(42, error_code(42).get());
} }
TEST(OutputRedirectTest, ScopedRedirect) { TEST(OutputRedirectTest, ScopedRedirect) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
{ {
BufferedFile file(write_end.fdopen("w")); buffered_file file(write_end.fdopen("w"));
std::fprintf(file.get(), "[[["); std::fprintf(file.get(), "[[[");
{ {
OutputRedirect redir(file.get()); OutputRedirect redir(file.get());
@ -332,11 +332,11 @@ TEST(OutputRedirectTest, ScopedRedirect) {
// Test that OutputRedirect handles errors in flush correctly. // Test that OutputRedirect handles errors in flush correctly.
TEST(OutputRedirectTest, FlushErrorInCtor) { TEST(OutputRedirectTest, FlushErrorInCtor) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
int write_fd = write_end.descriptor(); int write_fd = write_end.descriptor();
File write_copy = write_end.dup(write_fd); file write_copy = write_end.dup(write_fd);
BufferedFile f = write_end.fdopen("w"); buffered_file f = write_end.fdopen("w");
// Put a character in a file buffer. // Put a character in a file buffer.
EXPECT_EQ('x', fputc('x', f.get())); EXPECT_EQ('x', fputc('x', f.get()));
FMT_POSIX(close(write_fd)); FMT_POSIX(close(write_fd));
@ -348,9 +348,9 @@ TEST(OutputRedirectTest, FlushErrorInCtor) {
} }
TEST(OutputRedirectTest, DupErrorInCtor) { TEST(OutputRedirectTest, DupErrorInCtor) {
BufferedFile f = open_buffered_file(); buffered_file f = open_buffered_file();
int fd = (f.fileno)(); int fd = (f.fileno)();
File copy = File::dup(fd); file copy = file::dup(fd);
FMT_POSIX(close(fd)); FMT_POSIX(close(fd));
scoped_ptr<OutputRedirect> redir; scoped_ptr<OutputRedirect> redir;
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())), EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())),
@ -359,26 +359,26 @@ TEST(OutputRedirectTest, DupErrorInCtor) {
} }
TEST(OutputRedirectTest, RestoreAndRead) { TEST(OutputRedirectTest, RestoreAndRead) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
BufferedFile file(write_end.fdopen("w")); buffered_file file(write_end.fdopen("w"));
std::fprintf(file.get(), "[[["); std::fprintf(file.get(), "[[[");
OutputRedirect redir(file.get()); OutputRedirect redir(file.get());
std::fprintf(file.get(), "censored"); std::fprintf(file.get(), "censored");
EXPECT_EQ("censored", sanitize(redir.restore_and_read())); EXPECT_EQ("censored", sanitize(redir.restore_and_read()));
EXPECT_EQ("", sanitize(redir.restore_and_read())); EXPECT_EQ("", sanitize(redir.restore_and_read()));
std::fprintf(file.get(), "]]]"); std::fprintf(file.get(), "]]]");
file = BufferedFile(); file = buffered_file();
EXPECT_READ(read_end, "[[[]]]"); EXPECT_READ(read_end, "[[[]]]");
} }
// Test that OutputRedirect handles errors in flush correctly. // Test that OutputRedirect handles errors in flush correctly.
TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) { TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
int write_fd = write_end.descriptor(); int write_fd = write_end.descriptor();
File write_copy = write_end.dup(write_fd); file write_copy = write_end.dup(write_fd);
BufferedFile f = write_end.fdopen("w"); buffered_file f = write_end.fdopen("w");
OutputRedirect redir(f.get()); OutputRedirect redir(f.get());
// Put a character in a file buffer. // Put a character in a file buffer.
EXPECT_EQ('x', fputc('x', f.get())); EXPECT_EQ('x', fputc('x', f.get()));
@ -389,11 +389,11 @@ TEST(OutputRedirectTest, FlushErrorInRestoreAndRead) {
} }
TEST(OutputRedirectTest, ErrorInDtor) { TEST(OutputRedirectTest, ErrorInDtor) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
int write_fd = write_end.descriptor(); int write_fd = write_end.descriptor();
File write_copy = write_end.dup(write_fd); file write_copy = write_end.dup(write_fd);
BufferedFile f = write_end.fdopen("w"); buffered_file f = write_end.fdopen("w");
scoped_ptr<OutputRedirect> redir(new OutputRedirect(f.get())); scoped_ptr<OutputRedirect> redir(new OutputRedirect(f.get()));
// Put a character in a file buffer. // Put a character in a file buffer.
EXPECT_EQ('x', fputc('x', f.get())); EXPECT_EQ('x', fputc('x', f.get()));
@ -405,7 +405,7 @@ TEST(OutputRedirectTest, ErrorInDtor) {
FMT_POSIX(close(write_fd)); FMT_POSIX(close(write_fd));
SUPPRESS_ASSERT(redir.reset()); SUPPRESS_ASSERT(redir.reset());
}, format_system_error(EBADF, "cannot flush stream")); }, format_system_error(EBADF, "cannot flush stream"));
write_copy.dup2(write_fd); // "undo" close or dtor of BufferedFile will fail write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail
} }
#endif // FMT_USE_FILE_DESCRIPTORS #endif // FMT_USE_FILE_DESCRIPTORS

View File

@ -9,7 +9,7 @@
#if FMT_USE_FILE_DESCRIPTORS #if FMT_USE_FILE_DESCRIPTORS
using fmt::File; using fmt::file;
void OutputRedirect::flush() { void OutputRedirect::flush() {
#if EOF != -1 #if EOF != -1
@ -30,14 +30,14 @@ void OutputRedirect::restore() {
original_.close(); original_.close();
} }
OutputRedirect::OutputRedirect(FILE *file) : file_(file) { OutputRedirect::OutputRedirect(FILE *f) : file_(f) {
flush(); flush();
int fd = FMT_POSIX(fileno(file)); int fd = FMT_POSIX(fileno(f));
// Create a File object referring to the original file. // Create a file object referring to the original file.
original_ = File::dup(fd); original_ = file::dup(fd);
// Create a pipe. // Create a pipe.
File write_end; file write_end;
File::pipe(read_end_, write_end); file::pipe(read_end_, write_end);
// Connect the passed FILE object to the write end of the pipe. // Connect the passed FILE object to the write end of the pipe.
write_end.dup2(fd); write_end.dup2(fd);
} }
@ -69,7 +69,7 @@ std::string OutputRedirect::restore_and_read() {
return content; return content;
} }
std::string read(File &f, std::size_t count) { std::string read(file &f, std::size_t count) {
std::string buffer(count, '\0'); std::string buffer(count, '\0');
std::size_t n = 0, offset = 0; std::size_t n = 0, offset = 0;
do { do {

View File

@ -74,8 +74,8 @@ std::string format_system_error(int error_code, fmt::string_view message);
class OutputRedirect { class OutputRedirect {
private: private:
FILE *file_; FILE *file_;
fmt::File original_; // Original file passed to redirector. fmt::file original_; // Original file passed to redirector.
fmt::File read_end_; // Read end of the pipe where the output is redirected. fmt::file read_end_; // Read end of the pipe where the output is redirected.
GTEST_DISALLOW_COPY_AND_ASSIGN_(OutputRedirect); GTEST_DISALLOW_COPY_AND_ASSIGN_(OutputRedirect);
@ -145,7 +145,7 @@ class SuppressAssert {
EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message) EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message)
// Attempts to read count characters from a file. // Attempts to read count characters from a file.
std::string read(fmt::File &f, std::size_t count); std::string read(fmt::file &f, std::size_t count);
#define EXPECT_READ(file, expected_content) \ #define EXPECT_READ(file, expected_content) \
EXPECT_EQ(expected_content, read(file, std::strlen(expected_content))) EXPECT_EQ(expected_content, read(file, std::strlen(expected_content)))

View File

@ -25,9 +25,9 @@
#include "gtest-extra.h" #include "gtest-extra.h"
#include "util.h" #include "util.h"
using fmt::BufferedFile; using fmt::buffered_file;
using fmt::ErrorCode; using fmt::error_code;
using fmt::File; using fmt::file;
using testing::internal::scoped_ptr; using testing::internal::scoped_ptr;
using testing::_; using testing::_;
@ -194,7 +194,7 @@ int (test::fileno)(FILE *stream) {
#endif #endif
void write_file(fmt::cstring_view filename, fmt::string_view content) { void write_file(fmt::cstring_view filename, fmt::string_view content) {
fmt::BufferedFile f(filename, "w"); fmt::buffered_file f(filename, "w");
f.print("{}", content); f.print("{}", content);
} }
@ -214,8 +214,8 @@ TEST(UtilTest, GetPageSize) {
TEST(FileTest, OpenRetry) { TEST(FileTest, OpenRetry) {
write_file("test", "there must be something here"); write_file("test", "there must be something here");
scoped_ptr<File> f; scoped_ptr<file> f;
EXPECT_RETRY(f.reset(new File("test", File::RDONLY)), EXPECT_RETRY(f.reset(new file("test", file::RDONLY)),
open, "cannot open file test"); open, "cannot open file test");
#ifndef _WIN32 #ifndef _WIN32
char c = 0; char c = 0;
@ -224,9 +224,9 @@ TEST(FileTest, OpenRetry) {
} }
TEST(FileTest, CloseNoRetryInDtor) { TEST(FileTest, CloseNoRetryInDtor) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
scoped_ptr<File> f(new File(std::move(read_end))); scoped_ptr<file> f(new file(std::move(read_end)));
int saved_close_count = 0; int saved_close_count = 0;
EXPECT_WRITE(stderr, { EXPECT_WRITE(stderr, {
close_count = 1; close_count = 1;
@ -238,8 +238,8 @@ TEST(FileTest, CloseNoRetryInDtor) {
} }
TEST(FileTest, CloseNoRetry) { TEST(FileTest, CloseNoRetry) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
close_count = 1; close_count = 1;
EXPECT_SYSTEM_ERROR(read_end.close(), EINTR, "cannot close file"); EXPECT_SYSTEM_ERROR(read_end.close(), EINTR, "cannot close file");
EXPECT_EQ(2, close_count); EXPECT_EQ(2, close_count);
@ -249,7 +249,7 @@ TEST(FileTest, CloseNoRetry) {
TEST(FileTest, Size) { TEST(FileTest, Size) {
std::string content = "top secret, destroy before reading"; std::string content = "top secret, destroy before reading";
write_file("test", content); write_file("test", content);
File f("test", File::RDONLY); file f("test", file::RDONLY);
EXPECT_GE(f.size(), 0); EXPECT_GE(f.size(), 0);
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size())); EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
#ifdef _WIN32 #ifdef _WIN32
@ -267,7 +267,7 @@ TEST(FileTest, Size) {
TEST(FileTest, MaxSize) { TEST(FileTest, MaxSize) {
write_file("test", ""); write_file("test", "");
File f("test", File::RDONLY); file f("test", file::RDONLY);
fstat_sim = MAX_SIZE; fstat_sim = MAX_SIZE;
EXPECT_GE(f.size(), 0); EXPECT_GE(f.size(), 0);
EXPECT_EQ(max_file_size(), f.size()); EXPECT_EQ(max_file_size(), f.size());
@ -275,8 +275,8 @@ TEST(FileTest, MaxSize) {
} }
TEST(FileTest, ReadRetry) { TEST(FileTest, ReadRetry) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
enum { SIZE = 4 }; enum { SIZE = 4 };
write_end.write("test", SIZE); write_end.write("test", SIZE);
write_end.close(); write_end.close();
@ -288,8 +288,8 @@ TEST(FileTest, ReadRetry) {
} }
TEST(FileTest, WriteRetry) { TEST(FileTest, WriteRetry) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
enum { SIZE = 4 }; enum { SIZE = 4 };
std::size_t count = 0; std::size_t count = 0;
EXPECT_RETRY(count = write_end.write("test", SIZE), EXPECT_RETRY(count = write_end.write("test", SIZE),
@ -306,8 +306,8 @@ TEST(FileTest, WriteRetry) {
#ifdef _WIN32 #ifdef _WIN32
TEST(FileTest, ConvertReadCount) { TEST(FileTest, ConvertReadCount) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
char c; char c;
std::size_t size = UINT_MAX; std::size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t)) if (sizeof(unsigned) != sizeof(std::size_t))
@ -320,8 +320,8 @@ TEST(FileTest, ConvertReadCount) {
} }
TEST(FileTest, ConvertWriteCount) { TEST(FileTest, ConvertWriteCount) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
char c; char c;
std::size_t size = UINT_MAX; std::size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t)) if (sizeof(unsigned) != sizeof(std::size_t))
@ -337,14 +337,14 @@ TEST(FileTest, ConvertWriteCount) {
TEST(FileTest, DupNoRetry) { TEST(FileTest, DupNoRetry) {
int stdout_fd = FMT_POSIX(fileno(stdout)); int stdout_fd = FMT_POSIX(fileno(stdout));
dup_count = 1; dup_count = 1;
EXPECT_SYSTEM_ERROR(File::dup(stdout_fd), EINTR, EXPECT_SYSTEM_ERROR(file::dup(stdout_fd), EINTR,
fmt::format("cannot duplicate file descriptor {}", stdout_fd)); fmt::format("cannot duplicate file descriptor {}", stdout_fd));
dup_count = 0; dup_count = 0;
} }
TEST(FileTest, Dup2Retry) { TEST(FileTest, Dup2Retry) {
int stdout_fd = FMT_POSIX(fileno(stdout)); int stdout_fd = FMT_POSIX(fileno(stdout));
File f1 = File::dup(stdout_fd), f2 = File::dup(stdout_fd); file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2, EXPECT_RETRY(f1.dup2(f2.descriptor()), dup2,
fmt::format("cannot duplicate file descriptor {} to {}", fmt::format("cannot duplicate file descriptor {} to {}",
f1.descriptor(), f2.descriptor())); f1.descriptor(), f2.descriptor()));
@ -352,8 +352,8 @@ TEST(FileTest, Dup2Retry) {
TEST(FileTest, Dup2NoExceptRetry) { TEST(FileTest, Dup2NoExceptRetry) {
int stdout_fd = FMT_POSIX(fileno(stdout)); int stdout_fd = FMT_POSIX(fileno(stdout));
File f1 = File::dup(stdout_fd), f2 = File::dup(stdout_fd); file f1 = file::dup(stdout_fd), f2 = file::dup(stdout_fd);
ErrorCode ec; error_code ec;
dup2_count = 1; dup2_count = 1;
f1.dup2(f2.descriptor(), ec); f1.dup2(f2.descriptor(), ec);
#ifndef _WIN32 #ifndef _WIN32
@ -365,16 +365,16 @@ TEST(FileTest, Dup2NoExceptRetry) {
} }
TEST(FileTest, PipeNoRetry) { TEST(FileTest, PipeNoRetry) {
File read_end, write_end; file read_end, write_end;
pipe_count = 1; pipe_count = 1;
EXPECT_SYSTEM_ERROR( EXPECT_SYSTEM_ERROR(
File::pipe(read_end, write_end), EINTR, "cannot create pipe"); file::pipe(read_end, write_end), EINTR, "cannot create pipe");
pipe_count = 0; pipe_count = 0;
} }
TEST(FileTest, FdopenNoRetry) { TEST(FileTest, FdopenNoRetry) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
fdopen_count = 1; fdopen_count = 1;
EXPECT_SYSTEM_ERROR(read_end.fdopen("r"), EXPECT_SYSTEM_ERROR(read_end.fdopen("r"),
EINTR, "cannot associate stream with file descriptor"); EINTR, "cannot associate stream with file descriptor");
@ -383,8 +383,8 @@ TEST(FileTest, FdopenNoRetry) {
TEST(BufferedFileTest, OpenRetry) { TEST(BufferedFileTest, OpenRetry) {
write_file("test", "there must be something here"); write_file("test", "there must be something here");
scoped_ptr<BufferedFile> f; scoped_ptr<buffered_file> f;
EXPECT_RETRY(f.reset(new BufferedFile("test", "r")), EXPECT_RETRY(f.reset(new buffered_file("test", "r")),
fopen, "cannot open file test"); fopen, "cannot open file test");
#ifndef _WIN32 #ifndef _WIN32
char c = 0; char c = 0;
@ -394,9 +394,9 @@ TEST(BufferedFileTest, OpenRetry) {
} }
TEST(BufferedFileTest, CloseNoRetryInDtor) { TEST(BufferedFileTest, CloseNoRetryInDtor) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
scoped_ptr<BufferedFile> f(new BufferedFile(read_end.fdopen("r"))); scoped_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
int saved_fclose_count = 0; int saved_fclose_count = 0;
EXPECT_WRITE(stderr, { EXPECT_WRITE(stderr, {
fclose_count = 1; fclose_count = 1;
@ -408,9 +408,9 @@ TEST(BufferedFileTest, CloseNoRetryInDtor) {
} }
TEST(BufferedFileTest, CloseNoRetry) { TEST(BufferedFileTest, CloseNoRetry) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
BufferedFile f = read_end.fdopen("r"); buffered_file f = read_end.fdopen("r");
fclose_count = 1; fclose_count = 1;
EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file"); EXPECT_SYSTEM_ERROR(f.close(), EINTR, "cannot close file");
EXPECT_EQ(2, fclose_count); EXPECT_EQ(2, fclose_count);
@ -418,9 +418,9 @@ TEST(BufferedFileTest, CloseNoRetry) {
} }
TEST(BufferedFileTest, FilenoNoRetry) { TEST(BufferedFileTest, FilenoNoRetry) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
BufferedFile f = read_end.fdopen("r"); buffered_file f = read_end.fdopen("r");
fileno_count = 1; fileno_count = 1;
EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor"); EXPECT_SYSTEM_ERROR((f.fileno)(), EINTR, "cannot get file descriptor");
EXPECT_EQ(2, fileno_count); EXPECT_EQ(2, fileno_count);

View File

@ -16,9 +16,9 @@
# undef fileno # undef fileno
#endif #endif
using fmt::BufferedFile; using fmt::buffered_file;
using fmt::ErrorCode; using fmt::error_code;
using fmt::File; using fmt::file;
using testing::internal::scoped_ptr; using testing::internal::scoped_ptr;
@ -36,16 +36,16 @@ bool isclosed(int fd) {
} }
// Opens a file for reading. // Opens a file for reading.
File open_file() { file open_file() {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT)); write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
write_end.close(); write_end.close();
return read_end; return read_end;
} }
// Attempts to write a string to a file. // Attempts to write a string to a file.
void write(File &f, fmt::string_view s) { void write(file &f, fmt::string_view s) {
std::size_t num_chars_left = s.size(); std::size_t num_chars_left = s.size();
const char *ptr = s.data(); const char *ptr = s.data();
do { do {
@ -58,32 +58,32 @@ void write(File &f, fmt::string_view s) {
} }
TEST(BufferedFileTest, DefaultCtor) { TEST(BufferedFileTest, DefaultCtor) {
BufferedFile f; buffered_file f;
EXPECT_TRUE(f.get() == 0); EXPECT_TRUE(f.get() == 0);
} }
TEST(BufferedFileTest, MoveCtor) { TEST(BufferedFileTest, MoveCtor) {
BufferedFile bf = open_buffered_file(); buffered_file bf = open_buffered_file();
FILE *fp = bf.get(); FILE *fp = bf.get();
EXPECT_TRUE(fp != 0); EXPECT_TRUE(fp != 0);
BufferedFile bf2(std::move(bf)); buffered_file bf2(std::move(bf));
EXPECT_EQ(fp, bf2.get()); EXPECT_EQ(fp, bf2.get());
EXPECT_TRUE(bf.get() == 0); EXPECT_TRUE(bf.get() == 0);
} }
TEST(BufferedFileTest, MoveAssignment) { TEST(BufferedFileTest, MoveAssignment) {
BufferedFile bf = open_buffered_file(); buffered_file bf = open_buffered_file();
FILE *fp = bf.get(); FILE *fp = bf.get();
EXPECT_TRUE(fp != 0); EXPECT_TRUE(fp != 0);
BufferedFile bf2; buffered_file bf2;
bf2 = std::move(bf); bf2 = std::move(bf);
EXPECT_EQ(fp, bf2.get()); EXPECT_EQ(fp, bf2.get());
EXPECT_TRUE(bf.get() == 0); EXPECT_TRUE(bf.get() == 0);
} }
TEST(BufferedFileTest, MoveAssignmentClosesFile) { TEST(BufferedFileTest, MoveAssignmentClosesFile) {
BufferedFile bf = open_buffered_file(); buffered_file bf = open_buffered_file();
BufferedFile bf2 = open_buffered_file(); buffered_file bf2 = open_buffered_file();
int old_fd = bf2.fileno(); int old_fd = bf2.fileno();
bf2 = std::move(bf); bf2 = std::move(bf);
EXPECT_TRUE(isclosed(old_fd)); EXPECT_TRUE(isclosed(old_fd));
@ -91,19 +91,19 @@ TEST(BufferedFileTest, MoveAssignmentClosesFile) {
TEST(BufferedFileTest, MoveFromTemporaryInCtor) { TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
FILE *fp = 0; FILE *fp = 0;
BufferedFile f(open_buffered_file(&fp)); buffered_file f(open_buffered_file(&fp));
EXPECT_EQ(fp, f.get()); EXPECT_EQ(fp, f.get());
} }
TEST(BufferedFileTest, MoveFromTemporaryInAssignment) { TEST(BufferedFileTest, MoveFromTemporaryInAssignment) {
FILE *fp = 0; FILE *fp = 0;
BufferedFile f; buffered_file f;
f = open_buffered_file(&fp); f = open_buffered_file(&fp);
EXPECT_EQ(fp, f.get()); EXPECT_EQ(fp, f.get());
} }
TEST(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) { TEST(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) {
BufferedFile f = open_buffered_file(); buffered_file f = open_buffered_file();
int old_fd = f.fileno(); int old_fd = f.fileno();
f = open_buffered_file(); f = open_buffered_file();
EXPECT_TRUE(isclosed(old_fd)); EXPECT_TRUE(isclosed(old_fd));
@ -112,14 +112,14 @@ TEST(BufferedFileTest, MoveFromTemporaryInAssignmentClosesFile) {
TEST(BufferedFileTest, CloseFileInDtor) { TEST(BufferedFileTest, CloseFileInDtor) {
int fd = 0; int fd = 0;
{ {
BufferedFile f = open_buffered_file(); buffered_file f = open_buffered_file();
fd = f.fileno(); fd = f.fileno();
} }
EXPECT_TRUE(isclosed(fd)); EXPECT_TRUE(isclosed(fd));
} }
TEST(BufferedFileTest, CloseErrorInDtor) { TEST(BufferedFileTest, CloseErrorInDtor) {
scoped_ptr<BufferedFile> f(new BufferedFile(open_buffered_file())); scoped_ptr<buffered_file> f(new buffered_file(open_buffered_file()));
EXPECT_WRITE(stderr, { EXPECT_WRITE(stderr, {
// The close function must be called inside EXPECT_WRITE, otherwise // The close function must be called inside EXPECT_WRITE, otherwise
// the system may recycle closed file descriptor when redirecting the // the system may recycle closed file descriptor when redirecting the
@ -131,7 +131,7 @@ TEST(BufferedFileTest, CloseErrorInDtor) {
} }
TEST(BufferedFileTest, Close) { TEST(BufferedFileTest, Close) {
BufferedFile f = open_buffered_file(); buffered_file f = open_buffered_file();
int fd = f.fileno(); int fd = f.fileno();
f.close(); f.close();
EXPECT_TRUE(f.get() == 0); EXPECT_TRUE(f.get() == 0);
@ -139,14 +139,14 @@ TEST(BufferedFileTest, Close) {
} }
TEST(BufferedFileTest, CloseError) { TEST(BufferedFileTest, CloseError) {
BufferedFile f = open_buffered_file(); buffered_file f = open_buffered_file();
FMT_POSIX(close(f.fileno())); FMT_POSIX(close(f.fileno()));
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file"); EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
EXPECT_TRUE(f.get() == 0); EXPECT_TRUE(f.get() == 0);
} }
TEST(BufferedFileTest, Fileno) { TEST(BufferedFileTest, Fileno) {
BufferedFile f; buffered_file f;
#ifndef __COVERITY__ #ifndef __COVERITY__
// fileno on a null FILE pointer either crashes or returns an error. // fileno on a null FILE pointer either crashes or returns an error.
// Disable Coverity because this is intentional. // Disable Coverity because this is intentional.
@ -160,12 +160,12 @@ TEST(BufferedFileTest, Fileno) {
#endif #endif
f = open_buffered_file(); f = open_buffered_file();
EXPECT_TRUE(f.fileno() != -1); EXPECT_TRUE(f.fileno() != -1);
File copy = File::dup(f.fileno()); file copy = file::dup(f.fileno());
EXPECT_READ(copy, FILE_CONTENT); EXPECT_READ(copy, FILE_CONTENT);
} }
TEST(FileTest, DefaultCtor) { TEST(FileTest, DefaultCtor) {
File f; file f;
EXPECT_EQ(-1, f.descriptor()); EXPECT_EQ(-1, f.descriptor());
} }
@ -173,64 +173,64 @@ TEST(FileTest, OpenBufferedFileInCtor) {
FILE *fp = safe_fopen("test-file", "w"); FILE *fp = safe_fopen("test-file", "w");
std::fputs(FILE_CONTENT, fp); std::fputs(FILE_CONTENT, fp);
std::fclose(fp); std::fclose(fp);
File f("test-file", File::RDONLY); file f("test-file", file::RDONLY);
ASSERT_TRUE(isopen(f.descriptor())); ASSERT_TRUE(isopen(f.descriptor()));
} }
TEST(FileTest, OpenBufferedFileError) { TEST(FileTest, OpenBufferedFileError) {
EXPECT_SYSTEM_ERROR(File("nonexistent", File::RDONLY), EXPECT_SYSTEM_ERROR(file("nonexistent", file::RDONLY),
ENOENT, "cannot open file nonexistent"); ENOENT, "cannot open file nonexistent");
} }
TEST(FileTest, MoveCtor) { TEST(FileTest, MoveCtor) {
File f = open_file(); file f = open_file();
int fd = f.descriptor(); int fd = f.descriptor();
EXPECT_NE(-1, fd); EXPECT_NE(-1, fd);
File f2(std::move(f)); file f2(std::move(f));
EXPECT_EQ(fd, f2.descriptor()); EXPECT_EQ(fd, f2.descriptor());
EXPECT_EQ(-1, f.descriptor()); EXPECT_EQ(-1, f.descriptor());
} }
TEST(FileTest, MoveAssignment) { TEST(FileTest, MoveAssignment) {
File f = open_file(); file f = open_file();
int fd = f.descriptor(); int fd = f.descriptor();
EXPECT_NE(-1, fd); EXPECT_NE(-1, fd);
File f2; file f2;
f2 = std::move(f); f2 = std::move(f);
EXPECT_EQ(fd, f2.descriptor()); EXPECT_EQ(fd, f2.descriptor());
EXPECT_EQ(-1, f.descriptor()); EXPECT_EQ(-1, f.descriptor());
} }
TEST(FileTest, MoveAssignmentClosesFile) { TEST(FileTest, MoveAssignmentClosesFile) {
File f = open_file(); file f = open_file();
File f2 = open_file(); file f2 = open_file();
int old_fd = f2.descriptor(); int old_fd = f2.descriptor();
f2 = std::move(f); f2 = std::move(f);
EXPECT_TRUE(isclosed(old_fd)); EXPECT_TRUE(isclosed(old_fd));
} }
File OpenBufferedFile(int &fd) { file OpenBufferedFile(int &fd) {
File f = open_file(); file f = open_file();
fd = f.descriptor(); fd = f.descriptor();
return f; return f;
} }
TEST(FileTest, MoveFromTemporaryInCtor) { TEST(FileTest, MoveFromTemporaryInCtor) {
int fd = 0xdead; int fd = 0xdead;
File f(OpenBufferedFile(fd)); file f(OpenBufferedFile(fd));
EXPECT_EQ(fd, f.descriptor()); EXPECT_EQ(fd, f.descriptor());
} }
TEST(FileTest, MoveFromTemporaryInAssignment) { TEST(FileTest, MoveFromTemporaryInAssignment) {
int fd = 0xdead; int fd = 0xdead;
File f; file f;
f = OpenBufferedFile(fd); f = OpenBufferedFile(fd);
EXPECT_EQ(fd, f.descriptor()); EXPECT_EQ(fd, f.descriptor());
} }
TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) { TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
int fd = 0xdead; int fd = 0xdead;
File f = open_file(); file f = open_file();
int old_fd = f.descriptor(); int old_fd = f.descriptor();
f = OpenBufferedFile(fd); f = OpenBufferedFile(fd);
EXPECT_TRUE(isclosed(old_fd)); EXPECT_TRUE(isclosed(old_fd));
@ -239,14 +239,14 @@ TEST(FileTest, MoveFromTemporaryInAssignmentClosesFile) {
TEST(FileTest, CloseFileInDtor) { TEST(FileTest, CloseFileInDtor) {
int fd = 0; int fd = 0;
{ {
File f = open_file(); file f = open_file();
fd = f.descriptor(); fd = f.descriptor();
} }
EXPECT_TRUE(isclosed(fd)); EXPECT_TRUE(isclosed(fd));
} }
TEST(FileTest, CloseErrorInDtor) { TEST(FileTest, CloseErrorInDtor) {
scoped_ptr<File> f(new File(open_file())); scoped_ptr<file> f(new file(open_file()));
EXPECT_WRITE(stderr, { EXPECT_WRITE(stderr, {
// The close function must be called inside EXPECT_WRITE, otherwise // The close function must be called inside EXPECT_WRITE, otherwise
// the system may recycle closed file descriptor when redirecting the // the system may recycle closed file descriptor when redirecting the
@ -258,7 +258,7 @@ TEST(FileTest, CloseErrorInDtor) {
} }
TEST(FileTest, Close) { TEST(FileTest, Close) {
File f = open_file(); file f = open_file();
int fd = f.descriptor(); int fd = f.descriptor();
f.close(); f.close();
EXPECT_EQ(-1, f.descriptor()); EXPECT_EQ(-1, f.descriptor());
@ -266,19 +266,19 @@ TEST(FileTest, Close) {
} }
TEST(FileTest, CloseError) { TEST(FileTest, CloseError) {
File f = open_file(); file f = open_file();
FMT_POSIX(close(f.descriptor())); FMT_POSIX(close(f.descriptor()));
EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file"); EXPECT_SYSTEM_ERROR_NOASSERT(f.close(), EBADF, "cannot close file");
EXPECT_EQ(-1, f.descriptor()); EXPECT_EQ(-1, f.descriptor());
} }
TEST(FileTest, Read) { TEST(FileTest, Read) {
File f = open_file(); file f = open_file();
EXPECT_READ(f, FILE_CONTENT); EXPECT_READ(f, FILE_CONTENT);
} }
TEST(FileTest, ReadError) { TEST(FileTest, ReadError) {
File f("test-file", File::WRONLY); file f("test-file", file::WRONLY);
char buf; char buf;
// We intentionally read from a file opened in the write-only mode to // We intentionally read from a file opened in the write-only mode to
// cause error. // cause error.
@ -286,23 +286,23 @@ TEST(FileTest, ReadError) {
} }
TEST(FileTest, Write) { TEST(FileTest, Write) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
write(write_end, "test"); write(write_end, "test");
write_end.close(); write_end.close();
EXPECT_READ(read_end, "test"); EXPECT_READ(read_end, "test");
} }
TEST(FileTest, WriteError) { TEST(FileTest, WriteError) {
File f("test-file", File::RDONLY); file f("test-file", file::RDONLY);
// We intentionally write to a file opened in the read-only mode to // We intentionally write to a file opened in the read-only mode to
// cause error. // cause error.
EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file"); EXPECT_SYSTEM_ERROR(f.write(" ", 1), EBADF, "cannot write to file");
} }
TEST(FileTest, Dup) { TEST(FileTest, Dup) {
File f = open_file(); file f = open_file();
File copy = File::dup(f.descriptor()); file copy = file::dup(f.descriptor());
EXPECT_NE(f.descriptor(), copy.descriptor()); EXPECT_NE(f.descriptor(), copy.descriptor());
EXPECT_EQ(FILE_CONTENT, read(copy, std::strlen(FILE_CONTENT))); EXPECT_EQ(FILE_CONTENT, read(copy, std::strlen(FILE_CONTENT)));
} }
@ -310,29 +310,29 @@ TEST(FileTest, Dup) {
#ifndef __COVERITY__ #ifndef __COVERITY__
TEST(FileTest, DupError) { TEST(FileTest, DupError) {
int value = -1; int value = -1;
EXPECT_SYSTEM_ERROR_NOASSERT(File::dup(value), EXPECT_SYSTEM_ERROR_NOASSERT(file::dup(value),
EBADF, "cannot duplicate file descriptor -1"); EBADF, "cannot duplicate file descriptor -1");
} }
#endif #endif
TEST(FileTest, Dup2) { TEST(FileTest, Dup2) {
File f = open_file(); file f = open_file();
File copy = open_file(); file copy = open_file();
f.dup2(copy.descriptor()); f.dup2(copy.descriptor());
EXPECT_NE(f.descriptor(), copy.descriptor()); EXPECT_NE(f.descriptor(), copy.descriptor());
EXPECT_READ(copy, FILE_CONTENT); EXPECT_READ(copy, FILE_CONTENT);
} }
TEST(FileTest, Dup2Error) { TEST(FileTest, Dup2Error) {
File f = open_file(); file f = open_file();
EXPECT_SYSTEM_ERROR_NOASSERT(f.dup2(-1), EBADF, EXPECT_SYSTEM_ERROR_NOASSERT(f.dup2(-1), EBADF,
fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor())); fmt::format("cannot duplicate file descriptor {} to -1", f.descriptor()));
} }
TEST(FileTest, Dup2NoExcept) { TEST(FileTest, Dup2NoExcept) {
File f = open_file(); file f = open_file();
File copy = open_file(); file copy = open_file();
ErrorCode ec; error_code ec;
f.dup2(copy.descriptor(), ec); f.dup2(copy.descriptor(), ec);
EXPECT_EQ(0, ec.get()); EXPECT_EQ(0, ec.get());
EXPECT_NE(f.descriptor(), copy.descriptor()); EXPECT_NE(f.descriptor(), copy.descriptor());
@ -340,15 +340,15 @@ TEST(FileTest, Dup2NoExcept) {
} }
TEST(FileTest, Dup2NoExceptError) { TEST(FileTest, Dup2NoExceptError) {
File f = open_file(); file f = open_file();
ErrorCode ec; error_code ec;
SUPPRESS_ASSERT(f.dup2(-1, ec)); SUPPRESS_ASSERT(f.dup2(-1, ec));
EXPECT_EQ(EBADF, ec.get()); EXPECT_EQ(EBADF, ec.get());
} }
TEST(FileTest, Pipe) { TEST(FileTest, Pipe) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
EXPECT_NE(-1, read_end.descriptor()); EXPECT_NE(-1, read_end.descriptor());
EXPECT_NE(-1, write_end.descriptor()); EXPECT_NE(-1, write_end.descriptor());
write(write_end, "test"); write(write_end, "test");
@ -356,14 +356,14 @@ TEST(FileTest, Pipe) {
} }
TEST(FileTest, Fdopen) { TEST(FileTest, Fdopen) {
File read_end, write_end; file read_end, write_end;
File::pipe(read_end, write_end); file::pipe(read_end, write_end);
int read_fd = read_end.descriptor(); int read_fd = read_end.descriptor();
EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get()))); EXPECT_EQ(read_fd, FMT_POSIX(fileno(read_end.fdopen("r").get())));
} }
TEST(FileTest, FdopenError) { TEST(FileTest, FdopenError) {
File f; file f;
EXPECT_SYSTEM_ERROR_NOASSERT( EXPECT_SYSTEM_ERROR_NOASSERT(
f.fdopen("r"), EBADF, "cannot associate stream with file descriptor"); f.fdopen("r"), EBADF, "cannot associate stream with file descriptor");
} }

View File

@ -478,8 +478,8 @@ TEST(PrintfTest, Examples) {
} }
TEST(PrintfTest, PrintfError) { TEST(PrintfTest, PrintfError) {
fmt::File read_end, write_end; fmt::file read_end, write_end;
fmt::File::pipe(read_end, write_end); fmt::file::pipe(read_end, write_end);
int result = fmt::fprintf(read_end.fdopen("r").get(), "test"); int result = fmt::fprintf(read_end.fdopen("r").get(), "test");
EXPECT_LT(result, 0); EXPECT_LT(result, 0);
} }

View File

@ -402,7 +402,7 @@ TEST(UtilTest, BitCast) {
uint32_t u[2]; uint32_t u[2];
}; };
auto s = fmt::internal::bit_cast<S>(uint64_t(42)); auto s = fmt::internal::bit_cast<S>(uint64_t(42));
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), 42); EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), 42u);
s = fmt::internal::bit_cast<S>(uint64_t(~0ull)); s = fmt::internal::bit_cast<S>(uint64_t(~0ull));
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), ~0ull); EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), ~0ull);
} }

View File

@ -32,12 +32,12 @@ std::string get_system_error(int error_code) {
const char *const FILE_CONTENT = "Don't panic!"; const char *const FILE_CONTENT = "Don't panic!";
fmt::BufferedFile open_buffered_file(FILE **fp) { fmt::buffered_file open_buffered_file(FILE **fp) {
fmt::File read_end, write_end; fmt::file read_end, write_end;
fmt::File::pipe(read_end, write_end); fmt::file::pipe(read_end, write_end);
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT)); write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
write_end.close(); write_end.close();
fmt::BufferedFile f = read_end.fdopen("r"); fmt::buffered_file f = read_end.fdopen("r");
if (fp) if (fp)
*fp = f.get(); *fp = f.get();
return f; return f;

View File

@ -35,7 +35,7 @@ std::string get_system_error(int error_code);
extern const char *const FILE_CONTENT; extern const char *const FILE_CONTENT;
// Opens a buffered file for reading. // Opens a buffered file for reading.
fmt::BufferedFile open_buffered_file(FILE **fp = 0); fmt::buffered_file open_buffered_file(FILE **fp = 0);
inline FILE *safe_fopen(const char *filename, const char *mode) { inline FILE *safe_fopen(const char *filename, const char *mode) {
#if defined(_WIN32) && !defined(__MINGW32__) #if defined(_WIN32) && !defined(__MINGW32__)