Merge branch 'master' into ci

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

View File

@ -142,8 +142,8 @@ function(add_headers VAR)
endfunction()
# 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
time.h ranges.h)
add_headers(FMT_HEADERS core.h format.h format-inl.h ostream.h printf.h time.h
ranges.h)
set(FMT_SOURCES src/format.cc)
if (HAVE_OPEN)
add_headers(FMT_HEADERS posix.h)

View File

@ -1,5 +1,5 @@
5.0.0 - TBD
-----------
5.0.0 - 2018-05-21
------------------
* Added a requirement for partial C++11 support, most importantly variadic
templates and type traits, and dropped ``FMT_VARIADIC_*`` emulation macros.
@ -12,18 +12,40 @@
of the library for standardization in `P0645R2 Text Formatting
<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
compile-time format string processing. For example
.. code:: c++
struct S {};
struct Answer {};
namespace fmt {
template <>
struct formatter<S> {
struct formatter<Answer> {
constexpr auto parse(parse_context& ctx) {
auto it = ctx.begin();
spec = *it;
@ -33,7 +55,7 @@
}
template <typename FormatContext>
auto format(S, FormatContext& ctx) {
auto format(Answer, FormatContext& ctx) {
return spec == 's' ?
format_to(ctx.begin(), "{}", "fourty-two") :
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
<https://godbolt.org/g/ywhrPp>`_)::
gives a compile-time error due to invalid format specifier (`godbolt
<https://godbolt.org/g/2jQ1Dv>`__)::
...
<source>:12:45: error: expression '<throw-expression>' is not a constant expression
@ -63,9 +85,20 @@
std::vector<char> out;
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
<http://fmtlib.net/dev/api.html#output-iterator-support>`_ function for
computing output size:
<http://fmtlib.net/dev/api.html#_CPPv2N3fmt14formatted_sizeE11string_viewDpRK4Args>`_
function for computing the output size:
.. code:: c++
@ -101,6 +134,11 @@
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
distinguish them from variadic ones:
@ -111,6 +149,34 @@
template <typename... 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``
in the format API and provided ``fmt::string_view`` which implements a subset
of ``std::string_view`` API for pre-C++17 systems.
@ -126,7 +192,7 @@
fmt::print("{}", std::experimental::string_view("foo"));
Thanks `@virgiliofornazin (Virgilio Alexandre Fornazin)
<https://github.com/virgiliofornazin>`_.
<https://github.com/virgiliofornazin>`__.
* Allowed mixing named and automatic arguments:
@ -141,30 +207,48 @@
* Disallowed formatting of multibyte strings into a wide character target
(`#606 <https://github.com/fmtlib/fmt/pull/606>`_).
* Added a section describing `the use of header-only target with CMake
<http://fmtlib.net/dev/usage.html#header-only-usage-with-cmake>`_ to the docs
(`#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 (
* Improved documentation (
`#515 <https://github.com/fmtlib/fmt/pull/515>`_,
`#614 <https://github.com/fmtlib/fmt/issues/614>`_,
`#617 <https://github.com/fmtlib/fmt/pull/617>`_).
Thanks `@mihaitodor (Mihai Todor) <https://github.com/mihaitodor>`_.
`#617 <https://github.com/fmtlib/fmt/pull/617>`_,
`#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.
* 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
(`#397 <https://github.com/fmtlib/fmt/pull/397>`_).
Thanks `@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_.
* Renamed ``CHAR_WIDTH`` to ``CHAR_SIZE`` to avoid collision with ISO/IEC TS
18661-1:2014 macro.
* Moved ``fmt/*.h`` to ``include/fmt/*.h`` to prevent irrelevant files and
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
(`#616 <https://github.com/fmtlib/fmt/pull/616>`_).
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
(`#497 <https://github.com/fmtlib/fmt/pull/497>`_).
Thanks `@ginggs (Graham Inggs) <https://github.com/ginggs>`_.
@ -173,6 +257,95 @@
(`#626 <https://github.com/fmtlib/fmt/pull/626>`_).
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
------------------
@ -234,7 +407,7 @@
`@thelostt (Mário Feroldi) <https://github.com/thelostt>`_, and
`@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
(`#536 <https://github.com/fmtlib/fmt/pull/536>`_ and
`#556 <https://github.com/fmtlib/fmt/pull/556>`_).
@ -340,7 +513,6 @@
(`#494 <https://github.com/fmtlib/fmt/pull/494>`_,
`#499 <https://github.com/fmtlib/fmt/pull/499>`_,
`#483 <https://github.com/fmtlib/fmt/pull/483>`_,
`#519 <https://github.com/fmtlib/fmt/pull/519>`_,
`#485 <https://github.com/fmtlib/fmt/pull/485>`_,
`#482 <https://github.com/fmtlib/fmt/pull/482>`_,
`#475 <https://github.com/fmtlib/fmt/pull/475>`_,

View File

@ -164,6 +164,10 @@ Utilities
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
:members:

View File

@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
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):
"Install package using pip."

View File

@ -2,8 +2,7 @@ Overview
========
**fmt** (formerly cppformat) is an open-source formatting library.
It can be used as a safe alternative to printf or as a fast
alternative to C++ IOStreams.
It can be used as a fast and safe alternative to printf and IOStreams.
.. raw:: html
@ -30,9 +29,9 @@ in Python:
.. 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``:
.. code:: c++
@ -94,19 +93,25 @@ Safety
------
The library is fully type safe, automatic memory management prevents buffer
overflow, errors in format strings are reported using exceptions. For example,
the code
overflow, errors in format strings are reported using exceptions or at compile
tim. For example, the code
.. code:: c++
fmt::format("The answer is {:d}", "forty-two");
throws a ``format_error`` exception with description
"unknown format code 'd' for string", because the argument
``"forty-two"`` is a string while the format code ``d``
only applies to integers.
throws a ``format_error`` exception with description "unknown format code 'd' for
string", because the argument ``"forty-two"`` is a string while the format code
``d`` only applies to integers, while
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++
@ -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
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
-------------------
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>`_),
.. code:: c++
@ -167,33 +166,19 @@ compiles to just
Portability
-----------
The library is highly portable. Here is an incomplete list of operating systems
and compilers where it has been tested and known to work:
The library is highly portable and relies only on a small set of C++11 features:
* 64-bit (amd64) GNU/Linux with GCC 4.4.3,
`4.6.3 <https://travis-ci.org/fmtlib/fmt>`_, 4.7.2, 4.8.1, and Intel C++
Compiler (ICC) 14.0.2
* variadic templates
* type traits
* rvalue references
* decltype
* trailing return types
* deleted functions
* 32-bit (i386) GNU/Linux with GCC 4.4.3, 4.6.3
* Mac OS X with GCC 4.2.1 and Clang 4.2, 5.1.0
* 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``.
These are available since GCC 4.4, Clang 2.9 and MSVC 18.0 (2013). For older
compilers use fmt `version 4.x
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be
maintained and only requires C++98.
The output of all formatting functions is consistent across platforms. In
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
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
using the library both in open-source and commercial projects.

View File

@ -627,7 +627,8 @@ FMT_MAKE_VALUE(pointer_type, std::nullptr_t, const void*)
// formatting of "[const] volatile char *" which is printed as bool by
// iostreams.
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");
}
@ -640,8 +641,9 @@ inline typename std::enable_if<
template <typename C, typename T, typename Char = typename C::char_type>
inline typename std::enable_if<
!convert_to_int<T, Char>::value &&
!std::is_convertible<T, basic_string_view<Char>>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value,
!std::is_convertible<T, basic_string_view<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
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
// return vformat(format_str, make_format_args(args...));
// 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);
}
template <typename... 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);
}
@ -1296,7 +1298,7 @@ FMT_API void vprint(wstring_view format_str, wformat_args args);
*/
template <typename... 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);
}

View File

@ -9,7 +9,6 @@
#define FMT_FORMAT_INL_H_
#include "format.h"
#include "locale.h"
#include <string.h>
@ -19,6 +18,7 @@
#include <cmath>
#include <cstdarg>
#include <cstddef> // for std::ptrdiff_t
#include <locale>
#if defined(_WIN32) && defined(__MINGW32__)
# include <cstring>
@ -104,14 +104,14 @@ int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
class StrError {
class dispatcher {
private:
int error_code_;
char *&buffer_;
std::size_t buffer_size_;
// 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.
int handle(int result) {
@ -148,14 +148,14 @@ int safe_strerror(
}
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) {}
int run() {
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,
@ -196,6 +196,15 @@ void report_error(FormatFunc func, int error_code,
}
} // 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>
FMT_FUNC Char internal::thousands_sep(locale_provider *lp) {
std::locale loc = lp ? lp->locale().get() : std::locale();

View File

@ -2249,7 +2249,9 @@ void handle_dynamic_spec(
/** The default argument formatter. */
template <typename Range>
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:
typedef typename Range::value_type char_type;
typedef internal::arg_formatter_base<Range> base;
@ -2985,7 +2987,7 @@ FMT_API void report_windows_error(int error_code,
#endif
/** Fast integer formatter. */
class FormatInt {
class format_int {
private:
// Buffer should be large enough to hold all digits (digits10 + 1),
// a sign and a null character.
@ -3026,12 +3028,12 @@ class FormatInt {
}
public:
explicit FormatInt(int value) { format_signed(value); }
explicit FormatInt(long value) { format_signed(value); }
explicit FormatInt(long long value) { format_signed(value); }
explicit FormatInt(unsigned value) : str_(format_decimal(value)) {}
explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {}
explicit FormatInt(unsigned long long value) : str_(format_decimal(value)) {}
explicit format_int(int value) { format_signed(value); }
explicit format_int(long value) { format_signed(value); }
explicit format_int(long long value) { format_signed(value); }
explicit format_int(unsigned value) : str_(format_decimal(value)) {}
explicit format_int(unsigned 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. */
std::size_t size() const {
@ -3658,7 +3660,7 @@ FMT_END_NAMESPACE
#include <fmt/format.h>
// 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
*/
# define fmt(s) FMT_STRING(s)

View File

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

View File

@ -15,7 +15,7 @@ FMT_BEGIN_NAMESPACE
namespace internal {
template <class Char>
class FormatBuf : public std::basic_streambuf<Char> {
class formatbuf : public std::basic_streambuf<Char> {
private:
typedef typename std::basic_streambuf<Char>::int_type int_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_;
public:
FormatBuf(basic_buffer<Char> &buffer) : buffer_(buffer) {}
formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {}
protected:
// 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);
};
// Disable conversion to int if T has an overloaded operator<< which is a free
// function (not a member of std::ostream).
// Checks 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> {
class is_streamable {
private:
template <typename U>
static decltype(
@ -69,7 +69,16 @@ class convert_to_int<T, Char, true> {
typedef decltype(test<T>(0)) result;
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.
@ -90,7 +99,7 @@ void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) {
template <typename Char, typename T>
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);
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
@ -106,8 +115,7 @@ struct format_enum<T,
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct formatter<T, Char,
typename std::enable_if<!internal::format_type<
typename buffer_context<Char>::type, T>::value>::type>
typename std::enable_if<internal::is_streamable<T, Char>::value>::type>
: formatter<basic_string_view<Char>, Char> {
template <typename Context>
@ -140,22 +148,13 @@ inline void vprint(std::basic_ostream<Char> &os,
template <typename... Args>
inline void print(std::ostream &os, string_view format_str,
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...));
#else
vprint(os, format_str, make_format_args<format_context>(args...));
#endif
}
template <typename... Args>
inline void print(std::wostream &os, wstring_view format_str,
const Args & ... args) {
#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 440
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

View File

@ -113,31 +113,31 @@ typedef basic_cstring_view<char> cstring_view;
typedef basic_cstring_view<wchar_t> wcstring_view;
// An error code.
class ErrorCode {
class error_code {
private:
int value_;
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_; }
};
// A buffered file.
class BufferedFile {
class buffered_file {
private:
FILE *file_;
friend class File;
friend class file;
explicit BufferedFile(FILE *f) : file_(f) {}
explicit buffered_file(FILE *f) : file_(f) {}
public:
// Constructs a BufferedFile object which doesn't represent any file.
BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {}
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
// 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
// Emulate a move constructor and a move assignment operator if rvalue
@ -152,22 +152,22 @@ class BufferedFile {
public:
// 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.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) {
buffered_file(buffered_file &f) FMT_NOEXCEPT : file_(f.file_) {
f.file_ = FMT_NULL;
}
// A "move assignment operator" for moving from a temporary.
BufferedFile &operator=(Proxy p) {
buffered_file &operator=(Proxy p) {
close();
file_ = p.file;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
BufferedFile &operator=(BufferedFile &other) {
buffered_file &operator=(buffered_file &other) {
close();
file_ = other.file_;
other.file_ = FMT_NULL;
@ -175,7 +175,7 @@ public:
}
// Returns a proxy object for moving from a temporary:
// BufferedFile file = BufferedFile(...);
// buffered_file file = buffered_file(...);
operator Proxy() FMT_NOEXCEPT {
Proxy p = {file_};
file_ = FMT_NULL;
@ -184,14 +184,14 @@ public:
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
FMT_DISALLOW_COPY_AND_ASSIGN(buffered_file);
public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) {
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = FMT_NULL;
}
BufferedFile& operator=(BufferedFile &&other) {
buffered_file& operator=(buffered_file &&other) {
close();
file_ = other.file_;
other.file_ = FMT_NULL;
@ -200,7 +200,7 @@ public:
#endif
// 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.
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
// 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
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class File {
class file {
private:
int fd_; // File descriptor.
// Constructs a File object with a given descriptor.
explicit File(int fd) : fd_(fd) {}
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
public:
// 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.
};
// Constructs a File object which doesn't represent any file.
File() FMT_NOEXCEPT : fd_(-1) {}
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a File object representing this file.
FMT_API File(cstring_view path, int oflag);
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
@ -262,22 +262,22 @@ class File {
public:
// 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.
File(File &other) FMT_NOEXCEPT : fd_(other.fd_) {
file(file &other) FMT_NOEXCEPT : fd_(other.fd_) {
other.fd_ = -1;
}
// A "move assignment operator" for moving from a temporary.
File &operator=(Proxy p) {
file &operator=(Proxy p) {
close();
fd_ = p.fd;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
File &operator=(File &other) {
file &operator=(file &other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
@ -285,7 +285,7 @@ class File {
}
// Returns a proxy object for moving from a temporary:
// File file = File(...);
// file f = file(...);
operator Proxy() FMT_NOEXCEPT {
Proxy p = {fd_};
fd_ = -1;
@ -294,14 +294,14 @@ class File {
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(File);
FMT_DISALLOW_COPY_AND_ASSIGN(file);
public:
File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) {
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) {
other.fd_ = -1;
}
File& operator=(File &&other) {
file& operator=(file &&other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
@ -310,7 +310,7 @@ class File {
#endif
// 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.
int descriptor() const FMT_NOEXCEPT { return fd_; }
@ -330,7 +330,7 @@ class File {
// Duplicates a file descriptor with the dup function and returns
// 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
// necessary.
@ -338,15 +338,15 @@ class File {
// Makes fd be the copy of this file descriptor, closing fd first if
// 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
// 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
// this File object from the file.
FMT_API BufferedFile fdopen(const char *mode);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char *mode);
};
// Returns the memory page size.
@ -409,8 +409,8 @@ FMT_END_NAMESPACE
#if !FMT_USE_RVALUE_REFERENCES
namespace std {
// For compatibility with C++98.
inline fmt::BufferedFile &move(fmt::BufferedFile &f) { return f; }
inline fmt::File &move(fmt::File &f) { return f; }
inline fmt::buffered_file &move(fmt::buffered_file &f) { return f; }
inline fmt::file &move(fmt::file &f) { return f; }
}
#endif

View File

@ -213,7 +213,9 @@ class basic_printf_context;
*/
template <typename Range>
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:
typedef typename Range::value_type char_type;
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) {
auto vargs = make_format_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);
#else
return vfprintf(f, format_str, vargs);
#endif
}
template <typename... Args>

View File

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

View File

@ -22,11 +22,11 @@ inline null<> gmtime_s(...) { return null<>(); }
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
struct LocalTime {
struct dispatcher {
std::time_t time_;
std::tm tm_;
LocalTime(std::time_t t): time_(t) {}
dispatcher(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
@ -49,7 +49,7 @@ inline std::tm localtime(std::time_t time) {
return tm != FMT_NULL;
}
};
LocalTime lt(time);
dispatcher lt(time);
if (lt.run())
return lt.tm_;
// 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
inline std::tm gmtime(std::time_t time) {
struct GMTime {
struct dispatcher {
std::time_t time_;
std::tm tm_;
GMTime(std::time_t t): time_(t) {}
dispatcher(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
@ -84,7 +84,7 @@ inline std::tm gmtime(std::time_t time) {
return tm != FMT_NULL;
}
};
GMTime gt(time);
dispatcher gt(time);
if (gt.run())
return gt.tm_;
// Too big time values may be unsupported.
@ -92,11 +92,13 @@ inline std::tm gmtime(std::time_t time) {
}
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);
}
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);
}
}
@ -124,7 +126,8 @@ struct formatter<std::tm, Char> {
std::size_t start = buf.size();
for (;;) {
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) {
buf.resize(start + count);
break;

View File

@ -66,19 +66,19 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
FMT_BEGIN_NAMESPACE
BufferedFile::~BufferedFile() FMT_NOEXCEPT {
buffered_file::~buffered_file() FMT_NOEXCEPT {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
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_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_NULL);
if (!file_)
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
}
void BufferedFile::close() {
void buffered_file::close() {
if (!file_)
return;
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.
#define FMT_ARGS
int BufferedFile::fileno() const {
int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1)
FMT_THROW(system_error(errno, "cannot get file descriptor"));
return fd;
}
File::File(cstring_view path, int oflag) {
file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1;
@ -109,14 +109,14 @@ File::File(cstring_view path, int oflag) {
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!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
report_system_error(errno, "cannot close file");
}
void File::close() {
void file::close() {
if (fd_ == -1)
return;
// Don't retry close in case of EINTR!
@ -127,7 +127,7 @@ void File::close() {
FMT_THROW(system_error(errno, "cannot close file"));
}
long long File::size() const {
long long file::size() const {
#ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// 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)
FMT_THROW(system_error(errno, "cannot get file attributes"));
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;
#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;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0)
@ -161,7 +161,7 @@ std::size_t File::read(void *buffer, std::size_t count) {
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;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0)
@ -169,16 +169,16 @@ std::size_t File::write(const void *buffer, std::size_t count) {
return internal::to_unsigned(result);
}
File File::dup(int fd) {
file file::dup(int fd) {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1)
FMT_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;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
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;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
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
// and there are no leaks.
read_end.close();
@ -213,19 +213,19 @@ void File::pipe(File &read_end, File &write_end) {
FMT_THROW(system_error(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = File(fds[0]);
write_end = File(fds[1]);
read_end = file(fds[0]);
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.
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f)
FMT_THROW(system_error(errno,
"cannot associate stream with file descriptor"));
BufferedFile file(f);
buffered_file bf(f);
fd_ = -1;
return file;
return bf;
}
long getpagesize() {

View File

@ -9,8 +9,7 @@ set(CMAKE_REQUIRED_FLAGS ${CPP14_FLAG})
function (generate_source result fragment)
set(${result} "
#define FMT_HEADER_ONLY 1
#include \"fmt/posix.h\"
#include \"fmt/ostream.h\"
#include \"fmt/format.h\"
int main() {
${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.
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
# match the features detected in CMake.
if (SUPPORTS_USER_DEFINED_LITERALS)

View File

@ -1072,16 +1072,6 @@ TEST(FormatterTest, FormatStdStringView) {
}
#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 {
operator fmt::string_view() const { return "foo"; }
};
@ -1217,23 +1207,23 @@ TEST(FormatterTest, Examples) {
}
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()));
}
TEST(FormatIntTest, FormatInt) {
EXPECT_EQ("42", fmt::FormatInt(42).str());
EXPECT_EQ(2u, fmt::FormatInt(42).size());
EXPECT_EQ("-42", fmt::FormatInt(-42).str());
EXPECT_EQ(3u, fmt::FormatInt(-42).size());
EXPECT_EQ("42", fmt::FormatInt(42ul).str());
EXPECT_EQ("-42", fmt::FormatInt(-42l).str());
EXPECT_EQ("42", fmt::FormatInt(42ull).str());
EXPECT_EQ("-42", fmt::FormatInt(-42ll).str());
EXPECT_EQ("42", fmt::format_int(42).str());
EXPECT_EQ(2u, fmt::format_int(42).size());
EXPECT_EQ("-42", fmt::format_int(-42).str());
EXPECT_EQ(3u, fmt::format_int(-42).size());
EXPECT_EQ("42", fmt::format_int(42ul).str());
EXPECT_EQ("-42", fmt::format_int(-42l).str());
EXPECT_EQ("42", fmt::format_int(42ull).str());
EXPECT_EQ("-42", fmt::format_int(-42ll).str());
std::ostringstream os;
os << std::numeric_limits<int64_t>::max();
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>
@ -1404,7 +1394,8 @@ TEST(FormatTest, FixedEnum) {
typedef fmt::back_insert_range<fmt::internal::buffer> buffer_range;
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> {
private:
MOCK_METHOD1(call, void (int value));

View File

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

View File

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

View File

@ -74,8 +74,8 @@ std::string format_system_error(int error_code, fmt::string_view message);
class OutputRedirect {
private:
FILE *file_;
fmt::File original_; // Original file passed to redirector.
fmt::File read_end_; // Read end of the pipe where the output is redirected.
fmt::file original_; // Original file passed to redirector.
fmt::file read_end_; // Read end of the pipe where the output is redirected.
GTEST_DISALLOW_COPY_AND_ASSIGN_(OutputRedirect);
@ -145,7 +145,7 @@ class SuppressAssert {
EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message)
// 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) \
EXPECT_EQ(expected_content, read(file, std::strlen(expected_content)))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -35,7 +35,7 @@ std::string get_system_error(int error_code);
extern const char *const FILE_CONTENT;
// 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) {
#if defined(_WIN32) && !defined(__MINGW32__)