Merge branch 'master' into ci

This commit is contained in:
Elias Kosunen 2018-05-23 13:56:39 +03:00 committed by GitHub
commit 01fe59df47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 566 additions and 409 deletions

View File

@ -142,8 +142,8 @@ function(add_headers VAR)
endfunction() endfunction()
# Define the fmt library, its includes and the needed defines. # Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS core.h format.h format-inl.h locale.h ostream.h printf.h add_headers(FMT_HEADERS core.h format.h format-inl.h ostream.h printf.h time.h
time.h ranges.h) ranges.h)
set(FMT_SOURCES src/format.cc) set(FMT_SOURCES src/format.cc)
if (HAVE_OPEN) if (HAVE_OPEN)
add_headers(FMT_HEADERS posix.h) add_headers(FMT_HEADERS posix.h)

View File

@ -1,5 +1,5 @@
5.0.0 - TBD 5.0.0 - 2018-05-21
----------- ------------------
* Added a requirement for partial C++11 support, most importantly variadic * Added a requirement for partial C++11 support, most importantly variadic
templates and type traits, and dropped ``FMT_VARIADIC_*`` emulation macros. templates and type traits, and dropped ``FMT_VARIADIC_*`` emulation macros.
@ -12,18 +12,40 @@
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
.. code:: c++ .. code:: c++
struct S {}; struct Answer {};
namespace fmt { namespace fmt {
template <> template <>
struct formatter<S> { struct formatter<Answer> {
constexpr auto parse(parse_context& ctx) { constexpr auto parse(parse_context& ctx) {
auto it = ctx.begin(); auto it = ctx.begin();
spec = *it; spec = *it;
@ -33,7 +55,7 @@
} }
template <typename FormatContext> template <typename FormatContext>
auto format(S, FormatContext& ctx) { auto format(Answer, FormatContext& ctx) {
return spec == 's' ? return spec == 's' ?
format_to(ctx.begin(), "{}", "fourty-two") : format_to(ctx.begin(), "{}", "fourty-two") :
format_to(ctx.begin(), "{}", 42); format_to(ctx.begin(), "{}", 42);
@ -43,10 +65,10 @@
}; };
} }
std::string s = format(fmt("{:x}"), S()); std::string s = format(fmt("{:x}"), Answer());
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/2jQ1Dv>`__)::
... ...
<source>:12:45: error: expression '<throw-expression>' is not a constant expression <source>:12:45: error: expression '<throw-expression>' is not a constant expression
@ -63,9 +85,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 +134,11 @@
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 (
`#687 <https://github.com/fmtlib/fmt/issues/687>`_,
`#694 <https://github.com/fmtlib/fmt/pull/694>`_).
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:
@ -111,6 +149,34 @@
template <typename... Args> template <typename... Args>
std::string format(string_view format_str, const Args & ... args); std::string format(string_view format_str, const Args & ... args);
* Added experimental support for formatting ranges, containers and tuple-like
types in ``fmt/ranges.h`` (`#735 <https://github.com/fmtlib/fmt/pull/735>`_):
.. code:: c++
#include <fmt/ranges.h>
std::vector<int> v = {1, 2, 3};
fmt::print("{}", v); // prints {1, 2, 3}
Thanks `@Remotion (Remo) <https://github.com/Remotion>`_.
* Implemented ``wchar_t`` date and time formatting
(`#712 <https://github.com/fmtlib/fmt/pull/712>`_):
.. code:: c++
#include <fmt/time.h>
std::time_t t = std::time(nullptr);
auto s = fmt::format(L"The date is {:%Y-%m-%d}.", *std::localtime(&t));
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Provided more wide string overloads
(`#724 <https://github.com/fmtlib/fmt/pull/724>`_).
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Switched from a custom null-terminated string view class to ``string_view`` * Switched from a custom null-terminated string view class to ``string_view``
in the format API and provided ``fmt::string_view`` which implements a subset in the format API and provided ``fmt::string_view`` which implements a subset
of ``std::string_view`` API for pre-C++17 systems. of ``std::string_view`` API for pre-C++17 systems.
@ -126,7 +192,7 @@
fmt::print("{}", std::experimental::string_view("foo")); fmt::print("{}", std::experimental::string_view("foo"));
Thanks `@virgiliofornazin (Virgilio Alexandre Fornazin) Thanks `@virgiliofornazin (Virgilio Alexandre Fornazin)
<https://github.com/virgiliofornazin>`_. <https://github.com/virgiliofornazin>`__.
* Allowed mixing named and automatic arguments: * Allowed mixing named and automatic arguments:
@ -141,30 +207,48 @@
* 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.
* Introduced an inline namespace for symbol versioning.
* 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 +257,95 @@
(`#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>`__,
`#715 <https://github.com/fmtlib/fmt/issues/715>`_,
`#717 <https://github.com/fmtlib/fmt/pull/717>`_,
`#720 <https://github.com/fmtlib/fmt/pull/720>`_,
`#723 <https://github.com/fmtlib/fmt/pull/723>`_,
`#726 <https://github.com/fmtlib/fmt/pull/726>`_,
`#730 <https://github.com/fmtlib/fmt/pull/730>`_,
`#739 <https://github.com/fmtlib/fmt/pull/739>`_).
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>`_,
`@christianparpart (Christian Parpart) <https://github.com/christianparpart>`_,
`@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_,
and `@mwinterb <https://github.com/mwinterb>`_.
* 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 on FreeBSD 12
(`#732 <https://github.com/fmtlib/fmt/pull/732>`_).
Thanks `@dankm <https://github.com/dankm>`_.
* Fixed compilation when there is a mismatch between ``-std`` options between
the library and user code
(`#664 <https://github.com/fmtlib/fmt/issues/664>`_).
* Fixed compilation with GCC 7 and ``-std=c++11``
(`#734 <https://github.com/fmtlib/fmt/issues/734>`_).
* 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>`_).
* Disabled unsafe implicit conversion to ``std::string``
(`#729 <https://github.com/fmtlib/fmt/issues/729>`_).
* Fixed handling of reused format specs (as in ``fmt::join``) for pointers
(`#725 <https://github.com/fmtlib/fmt/pull/725>`_).
Thanks `@mwinterb <https://github.com/mwinterb>`_.
* Fixed installation of ``fmt/ranges.h``
(`#738 <https://github.com/fmtlib/fmt/pull/738>`_).
Thanks `@sv1990 <https://github.com/sv1990>`_.
4.1.0 - 2017-12-20 4.1.0 - 2017-12-20
------------------ ------------------
@ -234,7 +407,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>`_).
@ -340,7 +513,6 @@
(`#494 <https://github.com/fmtlib/fmt/pull/494>`_, (`#494 <https://github.com/fmtlib/fmt/pull/494>`_,
`#499 <https://github.com/fmtlib/fmt/pull/499>`_, `#499 <https://github.com/fmtlib/fmt/pull/499>`_,
`#483 <https://github.com/fmtlib/fmt/pull/483>`_, `#483 <https://github.com/fmtlib/fmt/pull/483>`_,
`#519 <https://github.com/fmtlib/fmt/pull/519>`_,
`#485 <https://github.com/fmtlib/fmt/pull/485>`_, `#485 <https://github.com/fmtlib/fmt/pull/485>`_,
`#482 <https://github.com/fmtlib/fmt/pull/482>`_, `#482 <https://github.com/fmtlib/fmt/pull/482>`_,
`#475 <https://github.com/fmtlib/fmt/pull/475>`_, `#475 <https://github.com/fmtlib/fmt/pull/475>`_,

View File

@ -164,6 +164,10 @@ Utilities
System errors System errors
------------- -------------
fmt does not use ``errno`` to communicate errors to the user, but it may call
system functions which set ``errno``. Users should not make any assumptions about
the value of ``errno`` being preserved by library functions.
.. doxygenclass:: fmt::system_error .. doxygenclass:: fmt::system_error
:members: :members:

View File

@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
from distutils.version import LooseVersion from distutils.version import LooseVersion
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0'] versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0']
def pip_install(package, commit=None, **kwargs): def pip_install(package, commit=None, **kwargs):
"Install package using pip." "Install package using pip."

View File

@ -2,8 +2,7 @@ Overview
======== ========
**fmt** (formerly cppformat) is an open-source formatting library. **fmt** (formerly cppformat) is an open-source formatting library.
It can be used as a safe alternative to printf or as a fast It can be used as a fast and safe alternative to printf and IOStreams.
alternative to C++ IOStreams.
.. raw:: html .. raw:: html
@ -30,9 +29,9 @@ in Python:
.. code:: c++ .. code:: c++
fmt::format("The answer is {}", 42); fmt::format("The answer is {}.", 42);
The ``fmt::format`` function returns a string "The answer is 42". You can use The ``fmt::format`` function returns a string "The answer is 42.". You can use
``fmt::memory_buffer`` to avoid constructing ``std::string``: ``fmt::memory_buffer`` to avoid constructing ``std::string``:
.. code:: c++ .. code:: c++
@ -94,19 +93,25 @@ Safety
------ ------
The library is fully type safe, automatic memory management prevents buffer The library is fully type safe, automatic memory management prevents buffer
overflow, errors in format strings are reported using exceptions. For example, overflow, errors in format strings are reported using exceptions or at compile
the code tim. For example, the code
.. code:: c++ .. code:: c++
fmt::format("The answer is {:d}", "forty-two"); fmt::format("The answer is {:d}", "forty-two");
throws a ``format_error`` exception with description throws a ``format_error`` exception with description "unknown format code 'd' for
"unknown format code 'd' for string", because the argument string", because the argument ``"forty-two"`` is a string while the format code
``"forty-two"`` is a string while the format code ``d`` ``d`` only applies to integers, while
only applies to integers.
Where possible, errors are caught at compile time. For example, the code .. code:: c++
format(fmt("The answer is {:d}"), "forty-two");
reports a compile-time error for the same reason on compilers that support
relaxed ``constexpr``.
The following code
.. code:: c++ .. code:: c++
@ -124,16 +129,10 @@ its numeric value being written to the stream (i.e. 1070 instead of letter 'ю'
which is represented by ``L'\x42e'`` if we use Unicode) which is rarely what is which is represented by ``L'\x42e'`` if we use Unicode) which is rarely what is
needed. needed.
Note that fmt does not use the value of the ``errno`` global to communicate
errors to the user, but it may call system functions which set ``errno``. Since
fmt does not attempt to preserve the value of ``errno``, users should not make
any assumptions about it and always set it to ``0`` before making any system
calls that convey error information via ``errno``.
Compact binary code Compact binary code
------------------- -------------------
Each call to a formatting function results in a compact binary code. For example The library is designed to produce compact per-call compiled code. For example
(`godbolt <https://godbolt.org/g/TZU4KF>`_), (`godbolt <https://godbolt.org/g/TZU4KF>`_),
.. code:: c++ .. code:: c++
@ -167,33 +166,19 @@ compiles to just
Portability Portability
----------- -----------
The library is highly portable. Here is an incomplete list of operating systems The library is highly portable and relies only on a small set of C++11 features:
and compilers where it has been tested and known to work:
* 64-bit (amd64) GNU/Linux with GCC 4.4.3, * variadic templates
`4.6.3 <https://travis-ci.org/fmtlib/fmt>`_, 4.7.2, 4.8.1, and Intel C++ * type traits
Compiler (ICC) 14.0.2 * rvalue references
* decltype
* trailing return types
* deleted functions
* 32-bit (i386) GNU/Linux with GCC 4.4.3, 4.6.3 These are available since GCC 4.4, Clang 2.9 and MSVC 18.0 (2013). For older
compilers use fmt `version 4.x
* Mac OS X with GCC 4.2.1 and Clang 4.2, 5.1.0 <https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be
maintained and only requires C++98.
* 64-bit Windows with Visual C++ 2010, 2013 and
`2015 <https://ci.appveyor.com/project/vitaut/fmt>`_
* 32-bit Windows with Visual C++ 2010
Although the library uses C++11 features when available, it also works with
older compilers and standard library implementations. The only thing to keep in
mind for C++98 portability:
* Variadic templates: minimum GCC 4.4, Clang 2.9 or VS2013. This feature allows
the Format API to accept an unlimited number of arguments. With older
compilers the maximum is 15.
* User-defined literals: minimum GCC 4.7, Clang 3.1 or VS2015. The suffixes
``_format`` and ``_a`` are functionally equivalent to the functions
``fmt::format`` and ``fmt::arg``.
The output of all formatting functions is consistent across platforms. In The output of all formatting functions is consistent across platforms. In
particular, formatting a floating-point infinity always gives ``inf`` while the particular, formatting a floating-point infinity always gives ``inf`` while the
@ -211,7 +196,7 @@ Ease of Use
----------- -----------
fmt has a small self-contained code base with the core library consisting of fmt has a small self-contained code base with the core library consisting of
a single header file and a single source file and no external dependencies. just three header files and no external dependencies.
A permissive BSD `license <https://github.com/fmtlib/fmt#license>`_ allows A permissive BSD `license <https://github.com/fmtlib/fmt#license>`_ allows
using the library both in open-source and commercial projects. using the library both in open-source and commercial projects.

View File

@ -627,7 +627,8 @@ FMT_MAKE_VALUE(pointer_type, std::nullptr_t, const void*)
// formatting of "[const] volatile char *" which is printed as bool by // formatting of "[const] volatile char *" which is printed as bool by
// iostreams. // iostreams.
template <typename C, typename T> template <typename C, typename T>
typed_value<C, pointer_type> make_value(const T *) { typename std::enable_if<!std::is_same<T, typename C::char_type>::value>::type
make_value(const T *) {
static_assert(!sizeof(T), "formatting of non-void pointers is disallowed"); static_assert(!sizeof(T), "formatting of non-void pointers is disallowed");
} }
@ -640,8 +641,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; }
@ -1250,12 +1252,12 @@ inline std::string format(string_view format_str, const Args & ... args) {
// This should be just // This should be just
// return vformat(format_str, make_format_args(args...)); // return vformat(format_str, make_format_args(args...));
// but gcc has trouble optimizing the latter, so break it down. // but gcc has trouble optimizing the latter, so break it down.
format_arg_store<format_context, Args...> as(args...); format_arg_store<format_context, Args...> as{args...};
return vformat(format_str, as); return vformat(format_str, as);
} }
template <typename... Args> template <typename... Args>
inline std::wstring format(wstring_view format_str, const Args & ... args) { inline std::wstring format(wstring_view format_str, const Args & ... args) {
format_arg_store<wformat_context, Args...> as(args...); format_arg_store<wformat_context, Args...> as{args...};
return vformat(format_str, as); return vformat(format_str, as);
} }
@ -1296,7 +1298,7 @@ FMT_API void vprint(wstring_view format_str, wformat_args args);
*/ */
template <typename... Args> template <typename... Args>
inline void print(string_view format_str, const Args & ... args) { inline void print(string_view format_str, const Args & ... args) {
format_arg_store<format_context, Args...> as(args...); format_arg_store<format_context, Args...> as{args...};
vprint(format_str, as); vprint(format_str, as);
} }

View File

@ -9,7 +9,6 @@
#define FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_
#include "format.h" #include "format.h"
#include "locale.h"
#include <string.h> #include <string.h>
@ -19,6 +18,7 @@
#include <cmath> #include <cmath>
#include <cstdarg> #include <cstdarg>
#include <cstddef> // for std::ptrdiff_t #include <cstddef> // for std::ptrdiff_t
#include <locale>
#if defined(_WIN32) && defined(__MINGW32__) #if defined(_WIN32) && defined(__MINGW32__)
# include <cstring> # include <cstring>
@ -104,14 +104,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) {
@ -148,14 +148,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,
@ -196,6 +196,15 @@ void report_error(FormatFunc func, int error_code,
} }
} // namespace } // namespace
class locale {
private:
std::locale locale_;
public:
explicit locale(std::locale loc = std::locale()) : locale_(loc) {}
std::locale get() { return locale_; }
};
template <typename Char> template <typename Char>
FMT_FUNC Char internal::thousands_sep(locale_provider *lp) { FMT_FUNC Char internal::thousands_sep(locale_provider *lp) {
std::locale loc = lp ? lp->locale().get() : std::locale(); std::locale loc = lp ? lp->locale().get() : std::locale();

View File

@ -2249,7 +2249,9 @@ void handle_dynamic_spec(
/** The default argument formatter. */ /** The default argument formatter. */
template <typename Range> template <typename Range>
class arg_formatter: class arg_formatter:
public internal::function<void>, public internal::arg_formatter_base<Range> { public internal::function<
typename internal::arg_formatter_base<Range>::iterator>,
public internal::arg_formatter_base<Range> {
private: private:
typedef typename Range::value_type char_type; typedef typename Range::value_type char_type;
typedef internal::arg_formatter_base<Range> base; typedef internal::arg_formatter_base<Range> base;
@ -2985,7 +2987,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.
@ -3026,12 +3028,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 {
@ -3658,7 +3660,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

@ -1,25 +0,0 @@
// Formatting library for C++ - locale support
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_
#include "format.h"
#include <locale>
FMT_BEGIN_NAMESPACE
class locale {
private:
std::locale locale_;
public:
explicit locale(std::locale loc = std::locale()) : locale_(loc) {}
std::locale get() { return locale_; }
};
FMT_END_NAMESPACE
#endif // FMT_LOCALE_

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
@ -53,10 +53,10 @@ struct test_stream : std::basic_ostream<Char> {
void operator<<(null); void operator<<(null);
}; };
// Disable conversion to int if T has an overloaded operator<< which is a free // Checks if T has an overloaded operator<< which is a free function (not a
// function (not a member of std::ostream). // member of std::ostream).
template <typename T, typename Char> template <typename T, typename Char>
class convert_to_int<T, Char, true> { class is_streamable {
private: private:
template <typename U> template <typename U>
static decltype( static decltype(
@ -69,7 +69,16 @@ class convert_to_int<T, Char, true> {
typedef decltype(test<T>(0)) result; typedef decltype(test<T>(0)) result;
public: public:
static const bool value = !result::value; static const bool value = result::value;
};
// Disable conversion to int if T has an overloaded operator<< which is a free
// function (not a member of std::ostream).
template <typename T, typename Char>
class convert_to_int<T, Char, true> {
public:
static const bool value =
convert_to_int<T, Char, false>::value && !is_streamable<T, Char>::value;
}; };
// Write the content of buf to os. // Write the content of buf to os.
@ -90,7 +99,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;
@ -106,8 +115,7 @@ struct format_enum<T,
// Formats an object of type T that has an overloaded ostream operator<<. // Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<T, Char, struct formatter<T, Char,
typename std::enable_if<!internal::format_type< typename std::enable_if<internal::is_streamable<T, Char>::value>::type>
typename buffer_context<Char>::type, T>::value>::type>
: formatter<basic_string_view<Char>, Char> { : formatter<basic_string_view<Char>, Char> {
template <typename Context> template <typename Context>
@ -140,22 +148,13 @@ inline void vprint(std::basic_ostream<Char> &os,
template <typename... Args> template <typename... Args>
inline void print(std::ostream &os, string_view format_str, inline void print(std::ostream &os, string_view format_str,
const Args & ... args) { const Args & ... args) {
#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 440
// Fix gcc's bugged template deduction
vprint<char>(os, format_str, make_format_args<format_context>(args...)); vprint<char>(os, format_str, make_format_args<format_context>(args...));
#else
vprint(os, format_str, make_format_args<format_context>(args...));
#endif
} }
template <typename... Args> template <typename... Args>
inline void print(std::wostream &os, wstring_view format_str, inline void print(std::wostream &os, wstring_view format_str,
const Args & ... args) { const Args & ... args) {
#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 440
vprint<wchar_t>(os, format_str, make_format_args<wformat_context>(args...)); vprint<wchar_t>(os, format_str, make_format_args<wformat_context>(args...));
#else
vprint(os, format_str, make_format_args<wformat_context>(args...));
#endif
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

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

@ -213,7 +213,9 @@ class basic_printf_context;
*/ */
template <typename Range> template <typename Range>
class printf_arg_formatter: class printf_arg_formatter:
public internal::function<void>, public internal::arg_formatter_base<Range> { public internal::function<
typename internal::arg_formatter_base<Range>::iterator>,
public internal::arg_formatter_base<Range> {
private: private:
typedef typename Range::value_type char_type; typedef typename Range::value_type char_type;
typedef decltype(internal::declval<Range>().begin()) iterator; typedef decltype(internal::declval<Range>().begin()) iterator;
@ -625,12 +627,7 @@ template <typename... Args>
inline int fprintf(std::FILE *f, string_view format_str, const Args & ... args) { inline int fprintf(std::FILE *f, string_view format_str, const Args & ... args) {
auto vargs = make_format_args< auto vargs = make_format_args<
typename printf_context<internal::buffer>::type>(args...); typename printf_context<internal::buffer>::type>(args...);
#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 440
// Fix gcc's bugged template deduction
return vfprintf<char>(f, format_str, vargs); return vfprintf<char>(f, format_str, vargs);
#else
return vfprintf(f, format_str, vargs);
#endif
} }
template <typename... Args> template <typename... Args>

View File

@ -34,18 +34,20 @@ template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> { struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit = static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range. FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range.
Char prefix = '{'; Char prefix;
Char delimiter = ','; Char delimiter;
Char postfix = '}'; Char postfix;
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
}; };
template <typename Char, typename Enable = void> template <typename Char, typename Enable = void>
struct formatting_tuple : formatting_base<Char> { struct formatting_tuple : formatting_base<Char> {
Char prefix = '('; Char prefix;
Char delimiter = ','; Char delimiter;
Char postfix = ')'; Char postfix;
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true; static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false; static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
}; };
@ -54,9 +56,8 @@ namespace internal {
template <typename RangeT, typename OutputIterator> template <typename RangeT, typename OutputIterator>
void copy(const RangeT &range, OutputIterator out) { void copy(const RangeT &range, OutputIterator out) {
for (const auto &it : range) { for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = it; *out++ = *it;
}
} }
template <typename OutputIterator> template <typename OutputIterator>
@ -83,7 +84,7 @@ class is_like_std_string {
public: public:
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(FMT_NULL))>::value;
}; };
template <typename... Ts> template <typename... Ts>
@ -95,8 +96,8 @@ struct is_range_ : std::false_type {};
template <typename T> template <typename T>
struct is_range_<T,typename std::conditional< struct is_range_<T,typename std::conditional<
false, false,
conditional_helper<decltype(std::declval<T>().begin()), conditional_helper<decltype(internal::declval<T>().begin()),
decltype(std::declval<T>().end())>, decltype(internal::declval<T>().end())>,
void>::type> : std::true_type {}; void>::type> : std::true_type {};
template <typename T> template <typename T>
@ -111,13 +112,13 @@ class is_tuple_like_ {
template <typename U> template <typename U>
static auto check(U *p) -> static auto check(U *p) ->
decltype(std::tuple_size<U>::value, decltype(std::tuple_size<U>::value,
std::declval<typename std::tuple_element<0, U>::type>(), int()); internal::declval<typename std::tuple_element<0, U>::type>(), int());
template <typename> template <typename>
static void check(...); static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(FMT_NULL))>::value;
}; };
template <typename T> template <typename T>
@ -125,6 +126,7 @@ struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
is_tuple_like_<T>::value && !is_range_<T>::value; is_tuple_like_<T>::value && !is_range_<T>::value;
}; };
} // namespace internal
// Check for integer_sequence // Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1910 #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1910
@ -213,6 +215,7 @@ struct formatter<TupleT, Char,
return ctx.out(); return ctx.out();
} }
}; };
#endif // FMT_USE_INTEGER_SEQUENCE
template <typename RangeT, typename Char> template <typename RangeT, typename Char>
struct formatter< RangeT, Char, struct formatter< RangeT, Char,
@ -226,11 +229,12 @@ struct formatter< RangeT, Char,
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const RangeT &values, FormatContext &ctx) -> decltype(ctx.out()) { typename FormatContext::iterator format(
const RangeT &values, FormatContext &ctx) {
auto out = ctx.out(); auto out = ctx.out();
internal::copy(formatting.prefix, out); internal::copy(formatting.prefix, out);
std::size_t i = 0; std::size_t i = 0;
for (const auto &it : values) { for (auto it = values.begin(), end = values.end(); it != end; ++it) {
if (i > 0) { if (i > 0) {
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) {
*out++ = ' '; *out++ = ' ';
@ -238,9 +242,9 @@ struct formatter< RangeT, Char,
internal::copy(formatting.delimiter, out); internal::copy(formatting.delimiter, out);
} }
if (formatting.add_delimiter_spaces && i > 0) { if (formatting.add_delimiter_spaces && i > 0) {
format_to(out, " {}", it); format_to(out, " {}", *it);
} else { } else {
format_to(out, "{}", it); format_to(out, "{}", *it);
} }
if (++i > formatting.range_length_limit) { if (++i > formatting.range_length_limit) {
format_to(out, " ... <other elements>"); format_to(out, " ... <other elements>");

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.
@ -58,11 +58,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;
@ -84,7 +84,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.
@ -92,11 +92,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);
} }
} }
@ -124,7 +126,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,19 +213,19 @@ 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 bf(f);
fd_ = -1; fd_ = -1;
return file; return bf;
} }
long getpagesize() { long getpagesize() {

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>
@ -1404,7 +1394,8 @@ TEST(FormatTest, FixedEnum) {
typedef fmt::back_insert_range<fmt::internal::buffer> buffer_range; typedef fmt::back_insert_range<fmt::internal::buffer> buffer_range;
class mock_arg_formatter: class mock_arg_formatter:
public fmt::internal::function<void>, public fmt::internal::function<
fmt::internal::arg_formatter_base<buffer_range>::iterator>,
public fmt::internal::arg_formatter_base<buffer_range> { public fmt::internal::arg_formatter_base<buffer_range> {
private: private:
MOCK_METHOD1(call, void (int value)); MOCK_METHOD1(call, void (int value));

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

@ -158,3 +158,8 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
} while (size != 0); } while (size != 0);
fmt::internal::write(os, buffer); fmt::internal::write(os, buffer);
} }
TEST(OStreamTest, Join) {
int v[3] = {1, 2, 3};
EXPECT_EQ("1, 2, 3", fmt::format("{}", fmt::join(v, v + 3, ", ")));
}

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

@ -30,6 +30,7 @@ TEST(RangesTest, FormatVector2) {
EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", ivf); EXPECT_EQ("{{1, 2}, {3, 5}, {7, 11}}", ivf);
} }
#if FMT_USE_INTEGER_SEQUENCE
TEST(RangesTest, FormatMap) { TEST(RangesTest, FormatMap) {
std::map<std::string, int32_t> simap{{"one", 1}, {"two", 2}}; std::map<std::string, int32_t> simap{{"one", 1}, {"two", 2}};
EXPECT_EQ("{(one, 1), (two, 2)}", fmt::format("{}", simap)); EXPECT_EQ("{(one, 1), (two, 2)}", fmt::format("{}", simap));
@ -46,7 +47,7 @@ TEST(RangesTest, FormatTuple) {
EXPECT_EQ("(42, 3.14159, this is tuple)", fmt::format("{}", tu1)); EXPECT_EQ("(42, 3.14159, this is tuple)", fmt::format("{}", tu1));
} }
/// check if 'if constexpr' is supported. /// Check if 'if constexpr' is supported.
#if (__cplusplus > 201402L) || \ #if (__cplusplus > 201402L) || \
(defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910)
@ -86,3 +87,4 @@ TEST(RangesTest, FormatStruct) {
#endif // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG > #endif // (__cplusplus > 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >
// 201402L && _MSC_VER >= 1910) // 201402L && _MSC_VER >= 1910)
#endif // FMT_USE_INTEGER_SEQUENCE

View File

@ -17,7 +17,6 @@
# include <type_traits> # include <type_traits>
#endif #endif
#include "fmt/locale.h"
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest-extra.h" #include "gtest-extra.h"
#include "mock-allocator.h" #include "mock-allocator.h"
@ -397,13 +396,14 @@ TEST(FixedBufferTest, BufferOverflow) {
EXPECT_THROW_MSG(buffer.resize(11), std::runtime_error, "buffer overflow"); EXPECT_THROW_MSG(buffer.resize(11), std::runtime_error, "buffer overflow");
} }
TEST(UtilTest, BitCast) { struct uint32_pair {
struct S {
uint32_t u[2]; uint32_t u[2];
}; };
auto s = fmt::internal::bit_cast<S>(uint64_t(42));
TEST(UtilTest, BitCast) {
auto s = fmt::internal::bit_cast<uint32_pair>(uint64_t{42});
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), 42ull); EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), 42ull);
s = fmt::internal::bit_cast<S>(uint64_t(~0ull)); s = fmt::internal::bit_cast<uint32_pair>(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__)