Merge branch 'master' into ci
This commit is contained in:
commit
01fe59df47
@ -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)
|
||||||
|
|||||||
222
ChangeLog.rst
222
ChangeLog.rst
@ -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>`_,
|
||||||
|
|||||||
@ -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:
|
||||||
|
|
||||||
|
|||||||
@ -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."
|
||||||
|
|||||||
@ -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.
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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();
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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_
|
|
||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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>");
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
44
src/posix.cc
44
src/posix.cc
@ -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() {
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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));
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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)))
|
||||||
|
|||||||
@ -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, ", ")));
|
||||||
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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__)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user