Squashed 'externals/fmt/' changes from 3e75ad982..9e554999c

9e554999c Update version
b34d92b05 Bump version
d39ece187 Make rst2md runnable and update changelog
fe2d715ff Update changelog
27b306701 Update changelog
68837079a Update changelog
c98b202eb Update changelog
587a7f663 Update changelog and docs
84e5170c9 Update changelog and deprecate visit
130e412b6 Update changelog and docs
0bbdca5b8 Fix conversion warnings (#989)
77a724480 Implement fill/align/width for strftime-like formatting
3e01376e0 Implement fill/align/width parsing in chrono formatter
1f92f8a9d Remove noexcept
8668639ae Get rid of null_terminating_iterator in format
93fd473b8 Add support for builtin terminal colors. (#974)
61ad543c3 Windows .sln filename changed from FORMAT to FMT
7f7504b3f Clean up docs
37f599b1a Fix docs
8c2e15aed Make printf work in search (#164)
de71db6d4 Fix asan error (#977)
b180b3915 Fix default formatting
24594c747 Disable printing the reset escape code when no style modifiers where applied. (#973)
b0f222471 Implement default chrono formatting
749276072 Add file stream support for stylized text printing. (#967)
f54f3d0fb Move chrono-specific code to a separate header
bf1f1c73e Fix time test
b6bc6ec24 Add default ctor and fix use of constexpr macros in text_style
acfa95d4a Workaround a bug in MSVC's strftime (#965)
628f83058 More chrono formatting
aa3b5aba4 Implement locale-specific minute formatting
639de2175 Workaround more MSVC bugs
3242ddf7b Fix warnings
bd1104046 Workaround a bug in MSVC
81b5c4a5f Add experimental emphasis support (#961)
7c4eb0fbe Fix warnings in time.h
2d624218b Fix another warning
b31680990 Fix a warning
b10ccb83e Add rpclib to projects
0497875ff Stop the orgy of casts
37dc495b9 Simplify MSVC workaround
2ff4996d0 Fix ambiguous complier error C2666 in vs2017.The '+' opeator may cause ambiguity.Avoid implicit conversion.
77656c672 Fix sign-conversion warnings reported by Clang7
ea5e4790b Fix formatting
86681c4bb Update README.rst
e867768ee Do not override user provided compile flag
0c7f5c3ca Update README.rst
e7e2ab107 Make return type of basic_format_args::max_size() consistent.
29352af36 Update README.rst
68214bd90 More time formatting
bcf3fcd67 Clean up bit fiddling for argument packing
9dcf127fa Workaround a bogus MSVC warning
b8b06e3e1 Fix conversion warnings in Grisu
322b2594e Implement more time specifiers
0835f1ba3 Use full paths for fmt.pc.in
a084495d7 Add Ceph to projects
fa1d4dbcf Fix warnings
2b2cfdac1 Update docs
99744f8f8 Suppress unfixable warning
f5fe84923 Specialize formatter for chrono durations
a5a9805a9 First stub at the datetime format parser
645c76a9a Fix dummy warnings
fecb2d6f0 Eliminate msvc compiler warnings (#931)
64690d3a9 Add context_base::arg()
01640f44c Fully qualify dummy_int (#941)
e37d6a984 add make_printf_args and make_wprintf_args functions (#934)
982ee5c69 parse_context -> format_parse_context
b7b854855 thousands_sep -> thousands_sep_impl (#939)
00a8cc832 Fix formatting
33fbb3a7e Fix remaining linker errors.
bd6121596 Disable fmt-impl-test in windows + shared lib.
702b3d161 Fix link error in windows with shared library.
9d4ef9435 Install pdb files.
6c95fb356 Default Context to format_context
16b78ee62 fix incompatibilities with c++2a mode in clang
19e008876 More locale support
f2ee98810 Improve locale support
1385050e2 More formatter tests
03c1b110a Fix gcc 4.4 build
cc805c616 Test enabled formatters
e01579231 Disallow leading zeros in arg-id
34030deca Cleanup warning flags
6b26e3f2d Manifest & Gradle comment
d286c9775 Update for Gradle build
d951f6dfe Get latest Gradle (ver. 4.10.2)
a23d59247 Fix check_format_string (#925)
36161284e Update docs
38f355d87 Revert "find sphinx-build before calling build.py"
324eac1aa Make locales work with any character type
bdda4d603 Simplify compile-time strings
5ee1a4bc8 check for property 'mutable iterator' and SFINAE on it
2dea780fb change type naming and fix sfinae bug
b98e8301d add non-char support for compile-time format check
ccd3e8bbf Make is_constructible public (#918)
437315380 Update usage.rst
73cfd8f32 Fix colored print
ec384302d additional test for print with background color
0a96c032b Parameterize v*printf on string type (#920)
61e6d2e38 Fix core version of vformat_to
ea4010d70 Merge has_to_string_view into is_string
486fff597 Add sprintf_format instantiations and remove syntactic noise
1e3dcbba8 fix: 'format_to_n' compiles 'std::back_inserter' arguments
f0328f8e3 Use char_traits::length in string_view ctor (#914)
895fb9845 Disallow gcc 4.4 failures
20c708bf6 Fix build on gcc 4.4
9d0c9c4bb cmake: output share/fmt.pc
2d2326a76 Fix compilation with older gcc
1ec027230 Get rid of FMT_UNION
2c81c851b Adapt any string-like type to be used by {fmt} just like the standard string types already supported. The adaption is totally non-intrusive.
846c644e8 Workeround broken sprintf in MSVC
13d472bd8 Compute output size for grisu
b71d3fe7a Remove use_grisu
847abb6f8 Fix test
dda47c946 Merge min_digits and max_digits
292462215 Fix naming of basic_format_specs members
bda5f9a55 Replace grisu2_specs with core_format_specs
b1ca608ba Remove unused empty_spec
e8efdef8d Avoid extra copy
98f1c1fe8 Remove unused code
50b18a3c1 Integrate Grisu
699297520 Implement Grisu rounding
4bb76ef0c Remove redundant definition of print
ddd7caf38 Fix locale-dependent formatting (#905)
10e03e695 use found python executable for launching sphinx-build
07200f445 find sphinx-build before calling build.py
08a65c228 Workaround broken constexpr in MSVC2017
167f8fe32 Fix a typo in api.rst
57983423c Remove signbit workaround
7bebb3e12 Clarify overload resolution in docs
939fbe556 Remove basic_fixed_buffer.
61f81a071 minor documentation corrections
f27defc63 Parameterize printf functions on the type of the format string.
6a685571d Make 'std::*::basic_string_view' a valid argument type for 'format_str' parameters.
87a0408c6 Fix ostream.h build
2b5acad4a Remove redundant size argument to write_padded
655ce5338 is_format_string -> is_string
fea712abb Parameterize ostream functions on the type of the format string.
f16a118e8 Fix non-matching char types.
041bf83d9 Improve fmt::format readability
229903239 Document how to write a formatter for a type hierarchy
f5480635c visit -> visit_format_arg
cdf3fa08d Put related code together in fmt/core.h
38325248e Count width in code points (#628)
deb901b9e Parameterize core functions on the type of the format string.
0f98de301 Update docs
c797708fc Workaround strlen being non-constexpr in ARM toolchain
49b4c1e9d Update docs
63a87beba Add to_string_view
4e0c31465 checked_format_args -> checked_args
c3538a1ee Simplify variadic functions further
2d7d0835d Simplify variadic functions
3f4cfa6c6 Implement UTF-8 string support
f8027414f Impelement char8_t support
76a47d41c Cleanup the use of FMT_CHAR
267fdc7a1 Parameterize core functions on the type of the format string.
5bced1242 Parameterize more functions on string type
674999c52 fix vs2017 warning fmt::v5::localtime 'not all control paths return a value'.
e4fea22d1 Make char8_t a strongly-typed enum
66992e90d Clarify that writing to memory_buffer appends (#877)
e864acfdb Fix compilation with intel compilers (ICC/ICPC) v14.0
4cf21f58b constrain templated format_to on proper format string type.
d7f17613f Fix compilation on platforms with exotic double (#878)
e4ca37ccf Parameterize format_to on string type (#880)
d66fa2216 Reduce syntactic noise
48e6dcd0f Implement workarounds for gcc 4.4
0ea3221d3 Remove is_named_arg and add FMT_CHAR
73c53d783 Parameterize 'printf(rgb color, ...)' and 'vprint_rgb(rgb color, ...)' on the type of the format string.
d41be23ac Simplify string_view detection
2def9e4c8 Remove FMT_DTOR_NOEXCEPT
ff6e46ed9 More cleanup
715f2b4c0 Remove require_wchar and internalize no_formatter_error
ec0cdc46f Workaround Windows slowness

git-subtree-dir: externals/fmt
git-subtree-split: 9e554999ce02cf86fcdfe74fe740c4fe3f5a56d5
This commit is contained in:
MerryMage 2020-04-22 21:00:18 +01:00
parent 5f7df9a182
commit 0066ad2d38
41 changed files with 3817 additions and 2059 deletions

1
.gitignore vendored
View file

@ -31,3 +31,4 @@ CMakeFiles
FMT.build FMT.build
Makefile Makefile
run-msbuild.bat run-msbuild.bat
fmt.pc

View file

@ -84,42 +84,41 @@ matrix:
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
# Android # Android
# - language: android - language: android
# android: addons:
# addons: apt:
# apt: update: true
# update: true sources:
# components: - ubuntu-toolchain-r-test
# - tools packages:
# - platform-tools - wget
# - android-21 - unzip
# - sys-img-armeabi-v7a-android-21 - tree
# env: android:
# - ANDROID=true components:
# before_install: - tools
# - git submodule update --init --recursive - platform-tools
# - sudo apt-get install wget unzip tree - android-21
# install: env:
# # Accept SDK Licenses + Install NDK - ANDROID=true
# - yes | sdkmanager --update > /dev/null 2>&1 before_install:
# - sdkmanager ndk-bundle > /dev/null 2>&1 # Download/Install Gradle
# # Download Gradle 4.3.1 - wget https://services.gradle.org/distributions/gradle-4.10.2-bin.zip
# - wget https://services.gradle.org/distributions/gradle-4.3.1-bin.zip - mkdir -p gradle
# - mkdir -p gradle - unzip -q -d ./gradle gradle-4.10.2-bin.zip
# - unzip -q -d ./gradle gradle-4.3.1-bin.zip - export GRADLE=gradle/gradle-4.10.2/bin/gradle
# - export GRADLE=${TRAVIS_BUILD_DIR}/gradle/gradle-4.3.1/bin/gradle - bash $GRADLE --version
# before_script: install:
# - bash $GRADLE --version # Accept SDK Licenses + Install NDK
# - cd ./support - yes | sdkmanager --update > /dev/null 2>&1
# script: - sdkmanager ndk-bundle > /dev/null 2>&1
# - bash $GRADLE clean assemble before_script:
# after_success: - pushd ./support
# - cd ${TRAVIS_BUILD_DIR} script:
# - tree ./libs - bash ../$GRADLE clean assemble
allow_failures: after_success:
# Errors - popd;
- env: COMPILER=g++-4.4 BUILD=Debug STANDARD=11 - tree ./libs
compiler: gcc
before_script: before_script:
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then export CXX=${COMPILER}; fi - if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then export CXX=${COMPILER}; fi

View file

@ -68,16 +68,17 @@ include(CheckCXXCompilerFlag)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
-Wold-style-cast -Wfloat-equal -Wlogical-op -Wundef -Wold-style-cast -Wundef
-Wredundant-decls -Wshadow -Wwrite-strings -Wpointer-arith -Wredundant-decls -Wwrite-strings -Wpointer-arith
-Wcast-qual -Wformat=2 -Wmissing-include-dirs -Wcast-qual -Wformat=2 -Wmissing-include-dirs
-Wcast-align -Wnon-virtual-dtor -Wcast-align -Wnon-virtual-dtor
-Wctor-dtor-privacy -Wdisabled-optimization -Wctor-dtor-privacy -Wdisabled-optimization
-Winvalid-pch -Woverloaded-virtual -Winvalid-pch -Woverloaded-virtual
-Wno-ctor-dtor-privacy -Wno-dangling-else -Wno-float-equal -Wconversion
-Wno-format-nonliteral -Wno-sign-conversion -Wno-shadow) -Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow)
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6) if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept) set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept
-Wno-dangling-else -Wno-unused-local-typedefs)
endif () endif ()
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion
@ -88,29 +89,17 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2 set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
-Wnull-dereference -Wduplicated-cond) -Wnull-dereference -Wduplicated-cond)
endif () endif ()
set(WERROR_FLAG -Werror) set(WERROR_FLAG -Werror)
endif () endif ()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(PEDANTIC_COMPILE_FLAGS -Weverything -Wpedantic set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wno-sign-conversion)
-Wno-weak-vtables -Wno-padded -Wno-gnu-statement-expression check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
-Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-reserved-id-macro
-Wno-global-constructors -Wno-disabled-macro-expansion
-Wno-switch-enum -Wno-documentation-unknown-command
-Wno-unused-member-function
-Wno-format-nonliteral -Wno-missing-noreturn -Wno-undefined-func-template
-Wno-shadow -Wno-sign-conversion -Wno-used-but-marked-unused
-Wno-covered-switch-default -Wno-missing-prototypes
-Wno-missing-variable-declarations -Wno-double-promotion)
set(WERROR_FLAG -Werror)
check_cxx_compiler_flag(-Wno-zero-as-null-pointer-constant HAS_NULLPTR_WARNING)
if (HAS_NULLPTR_WARNING) if (HAS_NULLPTR_WARNING)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
-Wno-zero-as-null-pointer-constant) -Wzero-as-null-pointer-constant)
endif () endif ()
set(WERROR_FLAG -Werror)
endif () endif ()
if (MSVC) if (MSVC)
@ -150,8 +139,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 color.h core.h format.h format-inl.h ostream.h printf.h add_headers(FMT_HEADERS chrono.h color.h core.h format.h format-inl.h locale.h
time.h ranges.h) ostream.h printf.h time.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)
@ -202,6 +191,7 @@ if (FMT_INSTALL)
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.") "Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.")
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake) set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake) set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
set(targets_export_name fmt-targets) set(targets_export_name fmt-targets)
set (INSTALL_TARGETS fmt) set (INSTALL_TARGETS fmt)
@ -215,11 +205,18 @@ if (FMT_INSTALL)
set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
"Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.") "Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.")
set(FMT_PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/share/pkgconfig" CACHE PATH
"Installation directory for pkgconfig (.pc) files, relative to ${CMAKE_INSTALL_PREFIX}.")
# Generate the version, config and target files into the build directory. # Generate the version, config and target files into the build directory.
write_basic_package_version_file( write_basic_package_version_file(
${version_config} ${version_config}
VERSION ${FMT_VERSION} VERSION ${FMT_VERSION}
COMPATIBILITY AnyNewerVersion) COMPATIBILITY AnyNewerVersion)
configure_file(
"${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in"
"${pkgconfig}"
@ONLY)
configure_package_config_file( configure_package_config_file(
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in ${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
${project_config} ${project_config}
@ -239,7 +236,10 @@ if (FMT_INSTALL)
# Install the library and headers. # Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
DESTINATION ${FMT_LIB_DIR}) DESTINATION ${FMT_LIB_DIR})
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}> DESTINATION ${FMT_LIB_DIR} OPTIONAL)
install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR}) install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR})
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif () endif ()
if (FMT_DOC) if (FMT_DOC)

View file

@ -1,3 +1,239 @@
5.3.0 - 2018-12-28
------------------
* Introduced experimental chrono formatting support:
.. code:: c++
#include <fmt/chrono.h>
int main() {
using namespace std::literals::chrono_literals;
fmt::print("Default format: {} {}\n", 42s, 100ms);
fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);
}
prints::
Default format: 42s 100ms
strftime-like format: 03:15:30
* Added experimental support for emphasis (bold, italic, underline,
strikethrough), colored output to a file stream, and improved colored
formatting API
(`#961 <https://github.com/fmtlib/fmt/pull/961>`_,
`#967 <https://github.com/fmtlib/fmt/pull/967>`_,
`#973 <https://github.com/fmtlib/fmt/pull/973>`_):
.. code:: c++
#include <fmt/color.h>
int main() {
print(fg(fmt::color::crimson) | fmt::emphasis::bold,
"Hello, {}!\n", "world");
print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) |
fmt::emphasis::underline, "Hello, {}!\n", "мир");
print(fg(fmt::color::steel_blue) | fmt::emphasis::italic,
"Hello, {}!\n", "世界");
}
prints the following on modern terminals with RGB color support:
.. image:: https://user-images.githubusercontent.com/576385/
50405788-b66e7500-076e-11e9-9592-7324d1f951d8.png
Thanks `@Rakete1111 (Nicolas) <https://github.com/Rakete1111>`_.
* Added support for 4-bit terminal colors
(`#968 <https://github.com/fmtlib/fmt/issues/968>`_,
`#974 <https://github.com/fmtlib/fmt/pull/974>`_)
.. code:: c++
#include <fmt/color.h>
int main() {
print(fg(fmt::terminal_color::red), "stop\n");
}
Note that these colors vary by terminal:
.. image:: https://user-images.githubusercontent.com/576385/
50405925-dbfc7e00-0770-11e9-9b85-333fab0af9ac.png
Thanks `@Rakete1111 (Nicolas) <https://github.com/Rakete1111>`_.
* Parameterized formatting functions on the type of the format string
(`#880 <https://github.com/fmtlib/fmt/issues/880>`_,
`#881 <https://github.com/fmtlib/fmt/pull/881>`_,
`#883 <https://github.com/fmtlib/fmt/pull/883>`_,
`#885 <https://github.com/fmtlib/fmt/pull/885>`_,
`#897 <https://github.com/fmtlib/fmt/pull/897>`_,
`#920 <https://github.com/fmtlib/fmt/issues/920>`_).
Any object of type ``S`` that has an overloaded ``to_string_view(const S&)``
returning ``fmt::string_view`` can be used as a format string:
.. code:: c++
namespace my_ns {
inline string_view to_string_view(const my_string& s) {
return {s.data(), s.length()};
}
}
std::string message = fmt::format(my_string("The answer is {}."), 42);
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Made ``std::string_view`` work as a format string
(`#898 <https://github.com/fmtlib/fmt/pull/898>`_):
.. code:: c++
auto message = fmt::format(std::string_view("The answer is {}."), 42);
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Added wide string support to compile-time format string checks
(`#924 <https://github.com/fmtlib/fmt/pull/924>`_):
.. code:: c++
print(fmt(L"{:f}"), 42); // compile-time error: invalid type specifier
Thanks `@XZiar <https://github.com/XZiar>`_.
* Made colored print functions work with wide strings
(`#867 <https://github.com/fmtlib/fmt/pull/867>`_):
.. code:: c++
#include <fmt/color.h>
int main() {
print(fg(fmt::color::red), L"{}\n", 42);
}
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Introduced experimental Unicode support
(`#628 <https://github.com/fmtlib/fmt/issues/628>`_,
`#891 <https://github.com/fmtlib/fmt/pull/891>`_):
.. code:: c++
using namespace fmt::literals;
auto s = fmt::format("{:*^5}"_u, "🤡"_u); // s == "**🤡**"_u
* Improved locale support:
.. code:: c++
#include <fmt/locale.h>
struct numpunct : std::numpunct<char> {
protected:
char do_thousands_sep() const override { return '~'; }
};
std::locale loc;
auto s = fmt::format(std::locale(loc, new numpunct()), "{:n}", 1234567);
// s == "1~234~567"
* Constrained formatting functions on proper iterator types
(`#921 <https://github.com/fmtlib/fmt/pull/921>`_):
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Added ``make_printf_args`` and ``make_wprintf_args`` functions
(`#934 <https://github.com/fmtlib/fmt/pull/934>`_).
Thanks `@tnovotny <https://github.com/tnovotny>`_.
* Deprecated ``fmt::visit``, ``parse_context``, and ``wparse_context``.
Use ``fmt::visit_format_arg``, ``format_parse_context``, and
``wformat_parse_context`` instead.
* Removed undocumented ``basic_fixed_buffer`` which has been superseded by the
iterator-based API
(`#873 <https://github.com/fmtlib/fmt/issues/873>`_,
`#902 <https://github.com/fmtlib/fmt/pull/902>`_).
Thanks `@superfunc (hollywood programmer) <https://github.com/superfunc>`_.
* Disallowed repeated leading zeros in an argument ID:
.. code:: c++
fmt::print("{000}", 42); // error
* Reintroduced support for gcc 4.4.
* Fixed compilation on platforms with exotic ``double``
(`#878 <https://github.com/fmtlib/fmt/issues/878>`_).
* Improved documentation
(`#164 <https://github.com/fmtlib/fmt/issues/164>`_,
`#877 <https://github.com/fmtlib/fmt/issues/877>`_,
`#901 <https://github.com/fmtlib/fmt/pull/901>`_,
`#906 <https://github.com/fmtlib/fmt/pull/906>`_,
`#979 <https://github.com/fmtlib/fmt/pull/979>`_).
Thanks `@kookjr (Mathew Cucuzella) <https://github.com/kookjr>`_,
`@DarkDimius (Dmitry Petrashko) <https://github.com/DarkDimius>`_,
`@HecticSerenity <https://github.com/HecticSerenity>`_.
* Added pkgconfig support which makes it easier to consume the library from
meson and other build systems
(`#916 <https://github.com/fmtlib/fmt/pull/916>`_).
Thanks `@colemickens (Cole Mickens) <https://github.com/colemickens>`_.
* Various build improvements
(`#909 <https://github.com/fmtlib/fmt/pull/909>`_,
`#926 <https://github.com/fmtlib/fmt/pull/926>`_,
`#937 <https://github.com/fmtlib/fmt/pull/937>`_,
`#953 <https://github.com/fmtlib/fmt/pull/953>`_,
`#959 <https://github.com/fmtlib/fmt/pull/959>`_).
Thanks `@tchaikov (Kefu Chai) <https://github.com/tchaikov>`_,
`@luncliff (Park DongHa) <https://github.com/luncliff>`_,
`@AndreasSchoenle (Andreas Schönle) <https://github.com/AndreasSchoenle>`_,
`@hotwatermorning <https://github.com/hotwatermorning>`_,
`@Zefz (JohanJansen) <https://github.com/Zefz>`_.
* Improved ``string_view`` construction performance
(`#914 <https://github.com/fmtlib/fmt/pull/914>`_).
Thanks `@gabime (Gabi Melman) <https://github.com/gabime>`_.
* Fixed non-matching char types
(`#895 <https://github.com/fmtlib/fmt/pull/895>`_).
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Fixed ``format_to_n`` with ``std::back_insert_iterator``
(`#913 <https://github.com/fmtlib/fmt/pull/913>`_).
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Fixed locale-dependent formatting
(`#905 <https://github.com/fmtlib/fmt/issues/905>`_).
* Fixed various compiler warnings and errors
(`#882 <https://github.com/fmtlib/fmt/pull/882>`_,
`#886 <https://github.com/fmtlib/fmt/pull/886>`_,
`#933 <https://github.com/fmtlib/fmt/pull/933>`_,
`#941 <https://github.com/fmtlib/fmt/pull/941>`_,
`#931 <https://github.com/fmtlib/fmt/issues/931>`_,
`#943 <https://github.com/fmtlib/fmt/pull/943>`_,
`#954 <https://github.com/fmtlib/fmt/pull/954>`_,
`#956 <https://github.com/fmtlib/fmt/pull/956>`_,
`#962 <https://github.com/fmtlib/fmt/pull/962>`_,
`#965 <https://github.com/fmtlib/fmt/issues/965>`_,
`#977 <https://github.com/fmtlib/fmt/issues/977>`_,
`#983 <https://github.com/fmtlib/fmt/pull/983>`_,
`#989 <https://github.com/fmtlib/fmt/pull/989>`_).
Thanks `@Luthaf (Guillaume Fraux) <https://github.com/Luthaf>`_,
`@stevenhoving (Steven Hoving) <https://github.com/stevenhoving>`_,
`@christinaa (Kristina Brooks) <https://github.com/christinaa>`_,
`@lgritz (Larry Gritz) <https://github.com/lgritz>`_,
`@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_,
`@0x8000-0000 (Sign Bit) <https://github.com/0x8000-0000>`_,
`@liuping1997 <https://github.com/liuping1997>`_.
5.2.1 - 2018-09-21 5.2.1 - 2018-09-21
------------------ ------------------

View file

@ -30,7 +30,7 @@ Features
of `str.format <https://docs.python.org/2/library/stdtypes.html#str.format>`_ of `str.format <https://docs.python.org/2/library/stdtypes.html#str.format>`_
in Python. in Python.
* Safe `printf implementation * Safe `printf implementation
<http://fmtlib.net/latest/api.html#printf-formatting-functions>`_ including <http://fmtlib.net/latest/api.html#printf-formatting>`_ including
the POSIX extension for positional arguments. the POSIX extension for positional arguments.
* Support for user-defined types. * Support for user-defined types.
* High speed: performance of the format API is close to that of glibc's `printf * High speed: performance of the format API is close to that of glibc's `printf
@ -126,7 +126,7 @@ Formatting of user-defined types is supported via a simple
template <typename FormatContext> template <typename FormatContext>
auto format(const date &d, FormatContext &ctx) { auto format(const date &d, FormatContext &ctx) {
return format_to(ctx.begin(), "{}-{}-{}", d.year, d.month, d.day); return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day);
} }
}; };
@ -153,19 +153,27 @@ which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_):
report_error("file not found: {}", path); report_error("file not found: {}", path);
Note that ``vreport_error`` is not parameterized on argument types which can Note that ``vreport_error`` is not parameterized on argument types which can
improve compile times and reduce code size compared to fully parameterized version. improve compile times and reduce code size compared to fully parameterized
version.
Projects using this library Projects using this library
--------------------------- ---------------------------
* `0 A.D. <http://play0ad.com/>`_: A free, open-source, cross-platform real-time strategy game * `0 A.D. <http://play0ad.com/>`_: A free, open-source, cross-platform real-time
strategy game
* `AMPL/MP <https://github.com/ampl/mp>`_: * `AMPL/MP <https://github.com/ampl/mp>`_:
An open-source library for mathematical programming An open-source library for mathematical programming
* `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft operations suite * `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft
operations suite
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater vehicle * `Celestia <https://celestia.space/>`_: Real-time 3D visualization of space
* `Ceph <https://ceph.com/>`_: A scalable distributed storage system
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
vehicle
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_: * `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
Player vs Player Gaming Network with tweaks Player vs Player Gaming Network with tweaks
@ -200,7 +208,11 @@ Projects using this library
* `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable * `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster proxy * `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster
proxy
* `rpclib <http://rpclib.net/>`_: A modern C++ msgpack-RPC server and client
library
* `Saddy <https://github.com/mamontov-cpp/saddy-graphics-engine-2d>`_: * `Saddy <https://github.com/mamontov-cpp/saddy-graphics-engine-2d>`_:
Small crossplatform 2D graphic engine Small crossplatform 2D graphic engine
@ -208,11 +220,11 @@ Projects using this library
* `Salesforce Analytics Cloud <http://www.salesforce.com/analytics-cloud/overview/>`_: * `Salesforce Analytics Cloud <http://www.salesforce.com/analytics-cloud/overview/>`_:
Business intelligence software Business intelligence software
* `Scylla <http://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store that can handle * `Scylla <http://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store
1 million transactions per second on a single server that can handle 1 million transactions per second on a single server
* `Seastar <http://www.seastar-project.org/>`_: An advanced, open-source C++ framework for * `Seastar <http://www.seastar-project.org/>`_: An advanced, open-source C++
high-performance server applications on modern hardware framework for high-performance server applications on modern hardware
* `spdlog <https://github.com/gabime/spdlog>`_: Super fast C++ logging library * `spdlog <https://github.com/gabime/spdlog>`_: Super fast C++ logging library
@ -220,7 +232,8 @@ Projects using this library
* `Touch Surgery <https://www.touchsurgery.com/>`_: Surgery simulator * `Touch Surgery <https://www.touchsurgery.com/>`_: Surgery simulator
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source MMORPG framework * `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
MMORPG framework
`More... <https://github.com/search?q=cppformat&type=Code>`_ `More... <https://github.com/search?q=cppformat&type=Code>`_

View file

@ -26,30 +26,29 @@ Core API
and a lightweight subset of formatting functions. and a lightweight subset of formatting functions.
The following functions use :ref:`format string syntax <syntax>` The following functions use :ref:`format string syntax <syntax>`
imilar to that of Python's `str.format similar to that of Python's `str.format
<http://docs.python.org/3/library/stdtypes.html#str.format>`_. <http://docs.python.org/3/library/stdtypes.html#str.format>`_.
They take *format_str* and *args* as arguments. They take *format_str* and *args* as arguments.
*format_str* is a format string that contains literal text and replacement *format_str* is a format string that contains literal text and replacement
fields surrounded by braces ``{}``. The fields are replaced with formatted fields surrounded by braces ``{}``. The fields are replaced with formatted
arguments in the resulting string. arguments in the resulting string. A function taking *format_str* doesn't
participate in an overload resolution if the latter is not a string.
*args* is an argument list representing objects to be formatted. *args* is an argument list representing objects to be formatted.
.. _format: .. _format:
.. doxygenfunction:: format(const String&, const Args&...) .. doxygenfunction:: format(const S&, const Args&...)
.. doxygenfunction:: vformat(const String&, basic_format_args<typename buffer_context<Char>::type>) .. doxygenfunction:: vformat(const S&, basic_format_args<typename buffer_context<Char>::type>)
.. _print: .. _print:
.. doxygenfunction:: print(string_view, const Args&...) .. doxygenfunction:: print(const S&, const Args&...)
.. doxygenfunction:: vprint(string_view, format_args) .. doxygenfunction:: vprint(string_view, format_args)
.. doxygenfunction:: print(std::FILE *, string_view, const Args&...) .. doxygenfunction:: print(std::FILE *, const S&, const Args&...)
.. doxygenfunction:: vprint(std::FILE *, string_view, format_args) .. doxygenfunction:: vprint(std::FILE *, string_view, format_args)
.. doxygenfunction:: print(std::FILE *, wstring_view, const Args&...)
.. doxygenfunction:: vprint(std::FILE *, wstring_view, wformat_args) .. doxygenfunction:: vprint(std::FILE *, wstring_view, wformat_args)
Named arguments Named arguments
@ -149,6 +148,35 @@ You can also reuse existing formatters, for example::
} }
}; };
You can also write a formatter for a hierarchy of classes::
#include <type_traits>
#include <fmt/format.h>
struct A {
virtual ~A() {}
virtual std::string name() const { return "A"; }
};
struct B : A {
virtual std::string name() const { return "B"; }
};
template <typename T>
struct fmt::formatter<T, std::enable_if_t<std::is_base_of<A, T>::value, char>> :
fmt::formatter<std::string> {
template <typename FormatCtx>
auto format(const A& a, FormatCtx& ctx) {
return fmt::formatter<std::string>::format(a.name(), ctx);
}
};
int main() {
B b;
A& a = b;
fmt::print("{}", a); // prints "B"
}
This section shows how to define a custom format function for a user-defined This section shows how to define a custom format function for a user-defined
type. The next section describes how to get ``fmt`` to use a conventional stream type. The next section describes how to get ``fmt`` to use a conventional stream
output ``operator<<`` when one is defined for a user-defined type. output ``operator<<`` when one is defined for a user-defined type.
@ -156,7 +184,7 @@ output ``operator<<`` when one is defined for a user-defined type.
Output iterator support Output iterator support
----------------------- -----------------------
.. doxygenfunction:: fmt::format_to(OutputIt, string_view, const Args&...) .. doxygenfunction:: fmt::format_to(OutputIt, const S&, const Args&...)
.. doxygenfunction:: fmt::format_to_n(OutputIt, std::size_t, string_view, const Args&...) .. doxygenfunction:: fmt::format_to_n(OutputIt, std::size_t, string_view, const Args&...)
.. doxygenstruct:: fmt::format_to_n_result .. doxygenstruct:: fmt::format_to_n_result
:members: :members:
@ -173,12 +201,16 @@ The following user-defined literals are defined in ``fmt/format.h``.
Utilities Utilities
--------- ---------
.. doxygentypedef:: fmt::char_t
.. doxygenfunction:: fmt::formatted_size(string_view, const Args&...) .. doxygenfunction:: fmt::formatted_size(string_view, const Args&...)
.. doxygenfunction:: fmt::to_string(const T&) .. doxygenfunction:: fmt::to_string(const T&)
.. doxygenfunction:: fmt::to_wstring(const T&) .. doxygenfunction:: fmt::to_wstring(const T&)
.. doxygenfunction:: fmt::to_string_view(basic_string_view<Char>)
.. doxygenclass:: fmt::basic_memory_buffer .. doxygenclass:: fmt::basic_memory_buffer
:protected-members: :protected-members:
:members: :members:
@ -248,7 +280,8 @@ custom argument formatter class::
// with the ``x`` format specifier. // with the ``x`` format specifier.
class custom_arg_formatter : public arg_formatter { class custom_arg_formatter : public arg_formatter {
public: public:
custom_arg_formatter(fmt::format_context &ctx, fmt::format_specs &spec) custom_arg_formatter(fmt::format_context &ctx,
fmt::format_specs *spec = nullptr)
: arg_formatter(ctx, spec) {} : arg_formatter(ctx, spec) {}
using arg_formatter::operator(); using arg_formatter::operator();
@ -319,7 +352,7 @@ user-defined types that have overloaded ``operator<<``::
std::string s = fmt::format("The date is {}", date(2012, 12, 9)); std::string s = fmt::format("The date is {}", date(2012, 12, 9));
// s == "The date is 2012-12-9" // s == "The date is 2012-12-9"
.. doxygenfunction:: print(std::ostream&, string_view, const Args&...) .. doxygenfunction:: print(std::basic_ostream<fmt::char_t<S>>&, const S&, const Args&...)
.. _printf-api: .. _printf-api:
@ -333,10 +366,10 @@ the POSIX extension for positional arguments. Unlike their standard
counterparts, the ``fmt`` functions are type-safe and throw an exception if an counterparts, the ``fmt`` functions are type-safe and throw an exception if an
argument type doesn't match its format specification. argument type doesn't match its format specification.
.. doxygenfunction:: printf(string_view, const Args&...) .. doxygenfunction:: printf(const S&, const Args&...)
.. doxygenfunction:: fprintf(std::FILE *, string_view, const Args&...) .. doxygenfunction:: fprintf(std::FILE *, const S&, const Args&...)
.. doxygenfunction:: fprintf(std::ostream&, string_view, const Args&...) .. doxygenfunction:: fprintf(std::basic_ostream<fmt::char_t<S>>&, const S&, const Args&...)
.. doxygenfunction:: sprintf(string_view, const Args&...) .. doxygenfunction:: sprintf(const S&, const Args&...)

View file

@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
from distutils.version import LooseVersion from distutils.version import LooseVersion
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1'] versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0']
def pip_install(package, commit=None, **kwargs): def pip_install(package, commit=None, **kwargs):
"Install package using pip." "Install package using pip."
@ -89,10 +89,12 @@ def build_docs(version='dev', **kwargs):
FMT_USE_VARIADIC_TEMPLATES=1 \ FMT_USE_VARIADIC_TEMPLATES=1 \
FMT_USE_RVALUE_REFERENCES=1 \ FMT_USE_RVALUE_REFERENCES=1 \
FMT_USE_USER_DEFINED_LITERALS=1 \ FMT_USE_USER_DEFINED_LITERALS=1 \
FMT_USE_ALIAS_TEMPLATES=1 \
FMT_API= \ FMT_API= \
"FMT_BEGIN_NAMESPACE=namespace fmt {{" \ "FMT_BEGIN_NAMESPACE=namespace fmt {{" \
"FMT_END_NAMESPACE=}}" \ "FMT_END_NAMESPACE=}}" \
"FMT_STRING_ALIAS=1" "FMT_STRING_ALIAS=1" \
"FMT_ENABLE_IF_T(B, T)=T"
EXCLUDE_SYMBOLS = fmt::internal::* StringValue write_str EXCLUDE_SYMBOLS = fmt::internal::* StringValue write_str
'''.format(include_dir, doxyxml_dir).encode('UTF-8')) '''.format(include_dir, doxyxml_dir).encode('UTF-8'))
if p.returncode != 0: if p.returncode != 0:

View file

@ -40,7 +40,7 @@ The ``fmt::format`` function returns a string "The answer is 42.". You can use
format_to(out, "For a moment, {} happened.", "nothing"); format_to(out, "For a moment, {} happened.", "nothing");
out.data(); // returns a pointer to the formatted data out.data(); // returns a pointer to the formatted data
The ``fmt::print`` function performs formatting and writes the result to a file: The ``fmt::print`` function performs formatting and writes the result to a stream:
.. code:: c++ .. code:: c++
@ -94,7 +94,7 @@ 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 or at compile overflow, errors in format strings are reported using exceptions or at compile
tim. For example, the code time. For example, the code
.. code:: c++ .. code:: c++

View file

@ -45,7 +45,7 @@ You can control generation of the make ``test`` target with the ``FMT_TEST``
CMake option. This can be useful if you include fmt as a subdirectory in CMake option. This can be useful if you include fmt as a subdirectory in
your project but don't want to add fmt's tests to your ``test`` target. your project but don't want to add fmt's tests to your ``test`` target.
If you use Windows and have Visual Studio installed, a :file:`FORMAT.sln` If you use Windows and have Visual Studio installed, a :file:`FMT.sln`
file and several :file:`.vcproj` files will be created. You can then build them file and several :file:`.vcproj` files will be created. You can then build them
using Visual Studio or msbuild. using Visual Studio or msbuild.
@ -74,7 +74,12 @@ or
to exclude it from ``make``, ``make all``, or ``cmake --build .``. to exclude it from ``make``, ``make all``, or ``cmake --build .``.
Settting up your target to use a header-only version of ``fmt`` is equaly easy:: You can detect and use an installed version of {fmt} as follows::
find_package(fmt)
target_link_libraries(<your-target> fmt::fmt)
Setting up your target to use a header-only version of ``fmt`` is equaly easy::
target_link_libraries(<your-target> PRIVATE fmt-header-only) target_link_libraries(<your-target> PRIVATE fmt-header-only)

452
include/fmt/chrono.h Normal file
View file

@ -0,0 +1,452 @@
// Formatting library for C++ - chrono support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_CHRONO_H_
#define FMT_CHRONO_H_
#include "format.h"
#include "locale.h"
#include <chrono>
#include <ctime>
#include <locale>
#include <sstream>
FMT_BEGIN_NAMESPACE
namespace internal{
enum class numeric_system {
standard,
// Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
alternative
};
// Parses a put_time-like format string and invokes handler actions.
template <typename Char, typename Handler>
FMT_CONSTEXPR const Char *parse_chrono_format(
const Char *begin, const Char *end, Handler &&handler) {
auto ptr = begin;
while (ptr != end) {
auto c = *ptr;
if (c == '}') break;
if (c != '%') {
++ptr;
continue;
}
if (begin != ptr)
handler.on_text(begin, ptr);
++ptr; // consume '%'
if (ptr == end)
throw format_error("invalid format");
c = *ptr++;
switch (c) {
case '%':
handler.on_text(ptr - 1, ptr);
break;
case 'n': {
const char newline[] = "\n";
handler.on_text(newline, newline + 1);
break;
}
case 't': {
const char tab[] = "\t";
handler.on_text(tab, tab + 1);
break;
}
// Day of the week:
case 'a':
handler.on_abbr_weekday();
break;
case 'A':
handler.on_full_weekday();
break;
case 'w':
handler.on_dec0_weekday(numeric_system::standard);
break;
case 'u':
handler.on_dec1_weekday(numeric_system::standard);
break;
// Month:
case 'b':
handler.on_abbr_month();
break;
case 'B':
handler.on_full_month();
break;
// Hour, minute, second:
case 'H':
handler.on_24_hour(numeric_system::standard);
break;
case 'I':
handler.on_12_hour(numeric_system::standard);
break;
case 'M':
handler.on_minute(numeric_system::standard);
break;
case 'S':
handler.on_second(numeric_system::standard);
break;
// Other:
case 'c':
handler.on_datetime(numeric_system::standard);
break;
case 'x':
handler.on_loc_date(numeric_system::standard);
break;
case 'X':
handler.on_loc_time(numeric_system::standard);
break;
case 'D':
handler.on_us_date();
break;
case 'F':
handler.on_iso_date();
break;
case 'r':
handler.on_12_hour_time();
break;
case 'R':
handler.on_24_hour_time();
break;
case 'T':
handler.on_iso_time();
break;
case 'p':
handler.on_am_pm();
break;
case 'z':
handler.on_utc_offset();
break;
case 'Z':
handler.on_tz_name();
break;
// Alternative representation:
case 'E': {
if (ptr == end)
throw format_error("invalid format");
c = *ptr++;
switch (c) {
case 'c':
handler.on_datetime(numeric_system::alternative);
break;
case 'x':
handler.on_loc_date(numeric_system::alternative);
break;
case 'X':
handler.on_loc_time(numeric_system::alternative);
break;
default:
throw format_error("invalid format");
}
break;
}
case 'O':
if (ptr == end)
throw format_error("invalid format");
c = *ptr++;
switch (c) {
case 'w':
handler.on_dec0_weekday(numeric_system::alternative);
break;
case 'u':
handler.on_dec1_weekday(numeric_system::alternative);
break;
case 'H':
handler.on_24_hour(numeric_system::alternative);
break;
case 'I':
handler.on_12_hour(numeric_system::alternative);
break;
case 'M':
handler.on_minute(numeric_system::alternative);
break;
case 'S':
handler.on_second(numeric_system::alternative);
break;
default:
throw format_error("invalid format");
}
break;
default:
throw format_error("invalid format");
}
begin = ptr;
}
if (begin != ptr)
handler.on_text(begin, ptr);
return ptr;
}
struct chrono_format_checker {
void report_no_date() { throw format_error("no date"); }
template <typename Char>
void on_text(const Char *, const Char *) {}
void on_abbr_weekday() { report_no_date(); }
void on_full_weekday() { report_no_date(); }
void on_dec0_weekday(numeric_system) { report_no_date(); }
void on_dec1_weekday(numeric_system) { report_no_date(); }
void on_abbr_month() { report_no_date(); }
void on_full_month() { report_no_date(); }
void on_24_hour(numeric_system) {}
void on_12_hour(numeric_system) {}
void on_minute(numeric_system) {}
void on_second(numeric_system) {}
void on_datetime(numeric_system) { report_no_date(); }
void on_loc_date(numeric_system) { report_no_date(); }
void on_loc_time(numeric_system) { report_no_date(); }
void on_us_date() { report_no_date(); }
void on_iso_date() { report_no_date(); }
void on_12_hour_time() {}
void on_24_hour_time() {}
void on_iso_time() {}
void on_am_pm() {}
void on_utc_offset() { report_no_date(); }
void on_tz_name() { report_no_date(); }
};
template <typename Int>
inline int to_int(Int value) {
FMT_ASSERT(value >= (std::numeric_limits<int>::min)() &&
value <= (std::numeric_limits<int>::max)(), "invalid value");
return static_cast<int>(value);
}
template <typename FormatContext, typename OutputIt>
struct chrono_formatter {
FormatContext &context;
OutputIt out;
std::chrono::seconds s;
std::chrono::milliseconds ms;
typedef typename FormatContext::char_type char_type;
explicit chrono_formatter(FormatContext &ctx, OutputIt o)
: context(ctx), out(o) {}
int hour() const { return to_int((s.count() / 3600) % 24); }
int hour12() const {
auto hour = to_int((s.count() / 3600) % 12);
return hour > 0 ? hour : 12;
}
int minute() const { return to_int((s.count() / 60) % 60); }
int second() const { return to_int(s.count() % 60); }
std::tm time() const {
auto time = std::tm();
time.tm_hour = hour();
time.tm_min = minute();
time.tm_sec = second();
return time;
}
void write(int value, int width) {
typedef typename int_traits<int>::main_type main_type;
main_type n = to_unsigned(value);
int num_digits = internal::count_digits(n);
if (width > num_digits)
out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits);
}
void format_localized(const tm &time, const char *format) {
auto locale = context.locale().template get<std::locale>();
auto &facet = std::use_facet<std::time_put<char_type>>(locale);
std::basic_ostringstream<char_type> os;
os.imbue(locale);
facet.put(os, os, ' ', &time, format, format + std::strlen(format));
auto str = os.str();
std::copy(str.begin(), str.end(), out);
}
void on_text(const char_type *begin, const char_type *end) {
std::copy(begin, end, out);
}
// These are not implemented because durations don't have date information.
void on_abbr_weekday() {}
void on_full_weekday() {}
void on_dec0_weekday(numeric_system) {}
void on_dec1_weekday(numeric_system) {}
void on_abbr_month() {}
void on_full_month() {}
void on_datetime(numeric_system) {}
void on_loc_date(numeric_system) {}
void on_loc_time(numeric_system) {}
void on_us_date() {}
void on_iso_date() {}
void on_utc_offset() {}
void on_tz_name() {}
void on_24_hour(numeric_system ns) {
if (ns == numeric_system::standard)
return write(hour(), 2);
auto time = tm();
time.tm_hour = hour();
format_localized(time, "%OH");
}
void on_12_hour(numeric_system ns) {
if (ns == numeric_system::standard)
return write(hour12(), 2);
auto time = tm();
time.tm_hour = hour();
format_localized(time, "%OI");
}
void on_minute(numeric_system ns) {
if (ns == numeric_system::standard)
return write(minute(), 2);
auto time = tm();
time.tm_min = minute();
format_localized(time, "%OM");
}
void on_second(numeric_system ns) {
if (ns == numeric_system::standard) {
write(second(), 2);
if (ms != std::chrono::milliseconds(0)) {
*out++ = '.';
write(to_int(ms.count()), 3);
}
return;
}
auto time = tm();
time.tm_sec = second();
format_localized(time, "%OS");
}
void on_12_hour_time() { format_localized(time(), "%r"); }
void on_24_hour_time() {
write(hour(), 2);
*out++ = ':';
write(minute(), 2);
}
void on_iso_time() {
on_24_hour_time();
*out++ = ':';
write(second(), 2);
}
void on_am_pm() { format_localized(time(), "%p"); }
};
} // namespace internal
template <typename Period> FMT_CONSTEXPR const char *get_units() {
return FMT_NULL;
}
template <> FMT_CONSTEXPR const char *get_units<std::atto>() { return "as"; }
template <> FMT_CONSTEXPR const char *get_units<std::femto>() { return "fs"; }
template <> FMT_CONSTEXPR const char *get_units<std::pico>() { return "ps"; }
template <> FMT_CONSTEXPR const char *get_units<std::nano>() { return "ns"; }
template <> FMT_CONSTEXPR const char *get_units<std::micro>() { return "µs"; }
template <> FMT_CONSTEXPR const char *get_units<std::milli>() { return "ms"; }
template <> FMT_CONSTEXPR const char *get_units<std::centi>() { return "cs"; }
template <> FMT_CONSTEXPR const char *get_units<std::deci>() { return "ds"; }
template <> FMT_CONSTEXPR const char *get_units<std::ratio<1>>() { return "s"; }
template <> FMT_CONSTEXPR const char *get_units<std::deca>() { return "das"; }
template <> FMT_CONSTEXPR const char *get_units<std::hecto>() { return "hs"; }
template <> FMT_CONSTEXPR const char *get_units<std::kilo>() { return "ks"; }
template <> FMT_CONSTEXPR const char *get_units<std::mega>() { return "Ms"; }
template <> FMT_CONSTEXPR const char *get_units<std::giga>() { return "Gs"; }
template <> FMT_CONSTEXPR const char *get_units<std::tera>() { return "Ts"; }
template <> FMT_CONSTEXPR const char *get_units<std::peta>() { return "Ps"; }
template <> FMT_CONSTEXPR const char *get_units<std::exa>() { return "Es"; }
template <> FMT_CONSTEXPR const char *get_units<std::ratio<60>>() {
return "m";
}
template <> FMT_CONSTEXPR const char *get_units<std::ratio<3600>>() {
return "h";
}
template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> {
private:
align_spec spec;
internal::arg_ref<Char> width_ref;
mutable basic_string_view<Char> format_str;
typedef std::chrono::duration<Rep, Period> duration;
struct spec_handler {
formatter &f;
basic_parse_context<Char> &context;
typedef internal::arg_ref<Char> arg_ref_type;
template <typename Id>
FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
context.check_arg_id(arg_id);
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
return arg_ref_type(context.next_arg_id());
}
void on_error(const char *msg) { throw format_error(msg); }
void on_fill(Char fill) { f.spec.fill_ = fill; }
void on_align(alignment align) { f.spec.align_ = align; }
void on_width(unsigned width) { f.spec.width_ = width; }
template <typename Id>
void on_dynamic_width(Id arg_id) {
f.width_ref = make_arg_ref(arg_id);
}
};
public:
formatter() : spec() {}
FMT_CONSTEXPR auto parse(basic_parse_context<Char> &ctx)
-> decltype(ctx.begin()) {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end) return begin;
spec_handler handler{*this, ctx};
begin = internal::parse_align(begin, end, handler);
if (begin == end) return begin;
begin = internal::parse_width(begin, end, handler);
end = parse_chrono_format(begin, end, internal::chrono_format_checker());
format_str = basic_string_view<Char>(&*begin, internal::to_unsigned(end - begin));
return end;
}
template <typename FormatContext>
auto format(const duration &d, FormatContext &ctx)
-> decltype(ctx.out()) {
auto begin = format_str.begin(), end = format_str.end();
memory_buffer buf;
typedef output_range<decltype(ctx.out()), Char> range;
basic_writer<range> w(range(ctx.out()));
if (begin == end || *begin == '}') {
if (const char *unit = get_units<Period>())
format_to(buf, "{}{}", d.count(), unit);
else if (Period::den == 1)
format_to(buf, "{}[{}]s", d.count(), Period::num);
else
format_to(buf, "{}[{}/{}]s", d.count(), Period::num, Period::den);
internal::handle_dynamic_spec<internal::width_checker>(
spec.width_, width_ref, ctx);
} else {
auto out = std::back_inserter(buf);
internal::chrono_formatter<FormatContext, decltype(out)> f(ctx, out);
f.s = std::chrono::duration_cast<std::chrono::seconds>(d);
f.ms = std::chrono::duration_cast<std::chrono::milliseconds>(d - f.s);
parse_chrono_format(begin, end, f);
}
w.write(buf.data(), buf.size(), spec);
return w.out();
}
};
FMT_END_NAMESPACE
#endif // FMT_CHRONO_H_

View file

@ -47,7 +47,6 @@ inline void vprint_colored(color c, wstring_view format, wformat_args args) {
#else #else
// Experimental color support.
enum class color : uint32_t { enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255) alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215) antique_white = 0xFAEBD7, // rgb(250,235,215)
@ -189,9 +188,35 @@ enum class color : uint32_t {
white = 0xFFFFFF, // rgb(255,255,255) white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245) white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0) yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32, // rgb(154,205,50) yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color }; // enum class color
enum class terminal_color : uint8_t {
black = 30,
red,
green,
yellow,
blue,
magenta,
cyan,
white,
bright_black = 90,
bright_red,
bright_green,
bright_yellow,
bright_blue,
bright_magenta,
bright_cyan,
bright_white
}; // enum class terminal_color
enum class emphasis : uint8_t {
bold = 1,
italic = 1 << 1,
underline = 1 << 2,
strikethrough = 1 << 3
}; // enum class emphasis
// rgb is a struct for red, green and blue colors. // rgb is a struct for red, green and blue colors.
// We use rgb as name because some editors will show it as color direct in the // We use rgb as name because some editors will show it as color direct in the
// editor. // editor.
@ -209,66 +234,340 @@ struct rgb {
uint8_t b; uint8_t b;
}; };
void vprint_rgb(rgb fd, string_view format, format_args args);
void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args);
/**
Formats a string and prints it to stdout using ANSI escape sequences to
specify foreground color 'fd'.
Example:
fmt::print(fmt::color::red, "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... Args>
inline void print(rgb fd, string_view format_str, const Args & ... args) {
vprint_rgb(fd, format_str, make_format_args(args...));
}
/**
Formats a string and prints it to stdout using ANSI escape sequences to
specify foreground color 'fd' and background color 'bg'.
Example:
fmt::print(fmt::color::red, fmt::color::black,
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... Args>
inline void print(rgb fd, rgb bg, string_view format_str,
const Args & ... args) {
vprint_rgb(fd, bg, format_str, make_format_args(args...));
}
namespace internal { namespace internal {
FMT_CONSTEXPR void to_esc(uint8_t c, char out[], int offset) {
out[offset + 0] = static_cast<char>('0' + c / 100); // color is a struct of either a rgb color or a terminal color.
out[offset + 1] = static_cast<char>('0' + c / 10 % 10); struct color_type {
out[offset + 2] = static_cast<char>('0' + c % 10); FMT_CONSTEXPR color_type() FMT_NOEXCEPT
: is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT
: is_rgb(true), value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color);
} }
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT
: is_rgb(true), value{} {
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16)
| (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
}
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT
: is_rgb(), value{} {
value.term_color = static_cast<uint8_t>(term_color);
}
bool is_rgb;
union color_union {
uint8_t term_color;
uint32_t rgb_color;
} value;
};
} // namespace internal } // namespace internal
inline void vprint_rgb(rgb fd, string_view format, format_args args) { // Experimental text formatting support.
char escape_fd[] = "\x1b[38;2;000;000;000m"; class text_style {
internal::to_esc(fd.r, escape_fd, 7); public:
internal::to_esc(fd.g, escape_fd, 11); FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
internal::to_esc(fd.b, escape_fd, 15); : set_foreground_color(), set_background_color(), ems(em) {}
std::fputs(escape_fd, stdout); FMT_CONSTEXPR text_style &operator|=(const text_style &rhs) {
vprint(format, args); if (!set_foreground_color) {
std::fputs(internal::data::RESET_COLOR, stdout); set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
throw format_error("can't OR a terminal color");
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
} }
inline void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args) { if (!set_background_color) {
char escape_fd[] = "\x1b[38;2;000;000;000m"; // foreground color set_background_color = rhs.set_background_color;
char escape_bg[] = "\x1b[48;2;000;000;000m"; // background color background_color = rhs.background_color;
internal::to_esc(fd.r, escape_fd, 7); } else if (rhs.set_background_color) {
internal::to_esc(fd.g, escape_fd, 11); if (!background_color.is_rgb || !rhs.background_color.is_rgb)
internal::to_esc(fd.b, escape_fd, 15); throw format_error("can't OR a terminal color");
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
internal::to_esc(bg.r, escape_bg, 7); ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
internal::to_esc(bg.g, escape_bg, 11); static_cast<uint8_t>(rhs.ems));
internal::to_esc(bg.b, escape_bg, 15); return *this;
}
std::fputs(escape_fd, stdout); friend FMT_CONSTEXPR
std::fputs(escape_bg, stdout); text_style operator|(text_style lhs, const text_style &rhs) {
vprint(format, args); return lhs |= rhs;
std::fputs(internal::data::RESET_COLOR, stdout); }
FMT_CONSTEXPR text_style &operator&=(const text_style &rhs) {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
throw format_error("can't AND a terminal color");
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
throw format_error("can't AND a terminal color");
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR
text_style operator&(text_style lhs, const text_style &rhs) {
return lhs &= rhs;
}
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
return set_foreground_color;
}
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
return set_background_color;
}
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
assert(has_foreground() && "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
assert(has_background() && "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
assert(has_emphasis() && "no emphasis specified for this style");
return ems;
}
private:
FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems() {
if (is_foreground) {
foreground_color = text_color;
set_foreground_color = true;
} else {
background_color = text_color;
set_background_color = true;
}
}
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
FMT_NOEXCEPT;
internal::color_type foreground_color;
internal::color_type background_color;
bool set_foreground_color;
bool set_background_color;
emphasis ems;
};
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/true, foreground);
}
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/false, background);
}
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs;
}
namespace internal {
template <typename Char>
struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
const char * esc) FMT_NOEXCEPT {
// If we have a terminal color, we need to output another escape code
// sequence.
if (!text_color.is_rgb) {
bool is_background = esc == internal::data::BACKGROUND_COLOR;
uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
if (is_background)
value += 10u;
std::size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
if (value >= 100u) {
buffer[index++] = static_cast<Char>('1');
value %= 100u;
}
buffer[index++] = static_cast<Char>('0' + value / 10u);
buffer[index++] = static_cast<Char>('0' + value % 10u);
buffer[index++] = static_cast<Char>('m');
buffer[index++] = static_cast<Char>('\0');
return;
}
for (int i = 0; i < 7; i++) {
buffer[i] = static_cast<Char>(esc[i]);
}
rgb color(text_color.value.rgb_color);
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
}
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
uint8_t em_codes[4] = {};
uint8_t em_bits = static_cast<uint8_t>(em);
if (em_bits & static_cast<uint8_t>(emphasis::bold))
em_codes[0] = 1;
if (em_bits & static_cast<uint8_t>(emphasis::italic))
em_codes[1] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::underline))
em_codes[2] = 4;
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9;
std::size_t index = 0;
for (int i = 0; i < 4; ++i) {
if (!em_codes[i])
continue;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
buffer[index++] = static_cast<Char>('m');
}
buffer[index++] = static_cast<Char>(0);
}
FMT_CONSTEXPR operator const Char *() const FMT_NOEXCEPT { return buffer; }
private:
Char buffer[7u + 3u * 4u + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char *out,
char delimiter) FMT_NOEXCEPT {
out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10);
out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter);
}
};
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char>
make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char>
make_background_color(internal::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char>
make_emphasis(emphasis em) FMT_NOEXCEPT {
return ansi_color_escape<Char>(em);
}
template <typename Char>
inline void fputs(const Char *chars, FILE *stream) FMT_NOEXCEPT {
std::fputs(chars, stream);
}
template <>
inline void fputs<wchar_t>(const wchar_t *chars, FILE *stream) FMT_NOEXCEPT {
std::fputws(chars, stream);
}
template <typename Char>
inline void reset_color(FILE *stream) FMT_NOEXCEPT {
fputs(internal::data::RESET_COLOR, stream);
}
template <>
inline void reset_color<wchar_t>(FILE *stream) FMT_NOEXCEPT {
fputs(internal::data::WRESET_COLOR, stream);
}
// The following specialiazation disables using std::FILE as a character type,
// which is needed because or else
// fmt::print(stderr, fmt::emphasis::bold, "");
// would take stderr (a std::FILE *) as the format string.
template <>
struct is_string<std::FILE *> : std::false_type {};
template <>
struct is_string<const std::FILE *> : std::false_type {};
} // namespace internal
template <
typename S, typename Char = typename internal::char_t<S>::type>
void vprint(std::FILE *f, const text_style &ts, const S &format,
basic_format_args<typename buffer_context<Char>::type> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
internal::fputs<Char>(
internal::make_emphasis<Char>(ts.get_emphasis()), f);
}
if (ts.has_foreground()) {
has_style = true;
internal::fputs<Char>(
internal::make_foreground_color<Char>(ts.get_foreground()), f);
}
if (ts.has_background()) {
has_style = true;
internal::fputs<Char>(
internal::make_background_color<Char>(ts.get_background()), f);
}
vprint(f, format, args);
if (has_style) {
internal::reset_color<Char>(f);
}
}
/**
Formats a string and prints it to the specified file stream using ANSI
escape sequences to specify text formatting.
Example:
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename String, typename... Args>
typename std::enable_if<internal::is_string<String>::value>::type print(
std::FILE *f, const text_style &ts, const String &format_str,
const Args &... args) {
internal::check_format_string<Args...>(format_str);
typedef typename internal::char_t<String>::type char_t;
typedef typename buffer_context<char_t>::type context_t;
format_arg_store<context_t, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context_t>(as));
}
/**
Formats a string and prints it to stdout using ANSI escape sequences to
specify text formatting.
Example:
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename String, typename... Args>
typename std::enable_if<internal::is_string<String>::value>::type print(
const text_style &ts, const String &format_str,
const Args &... args) {
return print(stdout, ts, format_str, args...);
} }
#endif #endif

File diff suppressed because it is too large Load diff

View file

@ -136,12 +136,14 @@ int safe_strerror(
ERANGE : result; ERANGE : result;
} }
#if !FMT_MSC_VER
// Fallback to strerror if strerror_r and strerror_s are not available. // Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::null<>) { int fallback(internal::null<>) {
errno = 0; errno = 0;
buffer_ = strerror(error_code_); buffer_ = strerror(error_code_);
return errno; return errno;
} }
#endif
public: public:
dispatcher(int err_code, char *&buf, std::size_t buf_size) dispatcher(int err_code, char *&buf, std::size_t buf_size)
@ -170,7 +172,7 @@ void format_error_code(internal::buffer &out, int error_code,
abs_value = 0 - abs_value; abs_value = 0 - abs_value;
++error_code_size; ++error_code_size;
} }
error_code_size += internal::count_digits(abs_value); error_code_size += internal::to_unsigned(internal::count_digits(abs_value));
writer w(out); writer w(out);
if (message.size() <= inline_buffer_size - error_code_size) { if (message.size() <= inline_buffer_size - error_code_size) {
w.write(message); w.write(message);
@ -192,34 +194,39 @@ void report_error(FormatFunc func, int error_code,
} }
} // namespace } // namespace
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) FMT_FUNC size_t internal::count_code_points(basic_string_view<char8_t> s) {
class locale {
private:
std::locale locale_;
public:
explicit locale(std::locale loc = std::locale()) : locale_(loc) {}
std::locale get() { return locale_; }
};
FMT_FUNC size_t internal::count_code_points(u8string_view s) {
const char8_t *data = s.data(); const char8_t *data = s.data();
int num_code_points = 0; size_t num_code_points = 0;
for (size_t i = 0, size = s.size(); i != size; ++i) { for (size_t i = 0, size = s.size(); i != size; ++i) {
if ((data[i].value & 0xc0) != 0x80) if ((data[i] & 0xc0) != 0x80)
++num_code_points; ++num_code_points;
} }
return num_code_points; return num_code_points;
} }
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
namespace internal {
template <typename Locale>
locale_ref::locale_ref(const Locale &loc) : locale_(&loc) {
static_assert(std::is_same<Locale, std::locale>::value, "");
}
template <typename Locale>
Locale locale_ref::get() const {
static_assert(std::is_same<Locale, std::locale>::value, "");
return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
}
template <typename Char> template <typename Char>
FMT_FUNC Char internal::thousands_sep(locale_provider *lp) { FMT_FUNC Char thousands_sep_impl(locale_ref loc) {
std::locale loc = lp ? lp->locale().get() : std::locale(); return std::use_facet<std::numpunct<Char> >(
return std::use_facet<std::numpunct<Char>>(loc).thousands_sep(); loc.get<std::locale>()).thousands_sep();
}
} }
#else #else
template <typename Char> template <typename Char>
FMT_FUNC Char internal::thousands_sep(locale_provider *lp) { FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
return FMT_STATIC_THOUSANDS_SEPARATOR; return FMT_STATIC_THOUSANDS_SEPARATOR;
} }
#endif #endif
@ -236,19 +243,19 @@ FMT_FUNC void system_error::init(
namespace internal { namespace internal {
template <typename T> template <typename T>
int char_traits<char>::format_float( int char_traits<char>::format_float(
char *buffer, std::size_t size, const char *format, int precision, T value) { char *buf, std::size_t size, const char *format, int precision, T value) {
return precision < 0 ? return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, value) : FMT_SNPRINTF(buf, size, format, value) :
FMT_SNPRINTF(buffer, size, format, precision, value); FMT_SNPRINTF(buf, size, format, precision, value);
} }
template <typename T> template <typename T>
int char_traits<wchar_t>::format_float( int char_traits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format, int precision, wchar_t *buf, std::size_t size, const wchar_t *format, int precision,
T value) { T value) {
return precision < 0 ? return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, value) : FMT_SWPRINTF(buf, size, format, value) :
FMT_SWPRINTF(buffer, size, format, precision, value); FMT_SWPRINTF(buf, size, format, precision, value);
} }
template <typename T> template <typename T>
@ -337,6 +344,8 @@ const int16_t basic_data<T>::POW10_EXPONENTS[] = {
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066
}; };
template <typename T> const char basic_data<T>::FOREGROUND_COLOR[] = "\x1b[38;2;";
template <typename T> const char basic_data<T>::BACKGROUND_COLOR[] = "\x1b[48;2;";
template <typename T> const char basic_data<T>::RESET_COLOR[] = "\x1b[0m"; template <typename T> const char basic_data<T>::RESET_COLOR[] = "\x1b[0m";
template <typename T> const wchar_t basic_data<T>::WRESET_COLOR[] = L"\x1b[0m"; template <typename T> const wchar_t basic_data<T>::WRESET_COLOR[] = L"\x1b[0m";
@ -363,7 +372,7 @@ class fp {
sizeof(significand_type) * char_size; sizeof(significand_type) * char_size;
fp(): f(0), e(0) {} fp(): f(0), e(0) {}
fp(uint64_t f, int e): f(f), e(e) {} fp(uint64_t f_val, int e_val): f(f_val), e(e_val) {}
// Constructs fp from an IEEE754 double. It is a template to prevent compile // Constructs fp from an IEEE754 double. It is a template to prevent compile
// errors on platforms where double is not IEEE754. // errors on platforms where double is not IEEE754.
@ -454,19 +463,28 @@ FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) {
return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]); return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]);
} }
FMT_FUNC bool grisu2_round(
char *buf, int &size, int max_digits, uint64_t delta,
uint64_t remainder, uint64_t exp, uint64_t diff, int &exp10) {
while (remainder < diff && delta - remainder >= exp &&
(remainder + exp < diff || diff - remainder > remainder + exp - diff)) {
--buf[size - 1];
remainder += exp;
}
if (size > max_digits) {
--size;
++exp10;
if (buf[size] >= '5')
return false;
}
return true;
}
// Generates output using Grisu2 digit-gen algorithm. // Generates output using Grisu2 digit-gen algorithm.
FMT_FUNC void grisu2_gen_digits( FMT_FUNC bool grisu2_gen_digits(
const fp &scaled_value, const fp &scaled_upper, uint64_t delta, char *buf, int &size, uint32_t hi, uint64_t lo, int &exp,
char *buffer, size_t &size, int &dec_exp) { uint64_t delta, const fp &one, const fp &diff, int max_digits) {
internal::fp one(1ull << -scaled_upper.e, scaled_upper.e); // Generate digits for the most significant part (hi).
// hi (p1 in Grisu) contains the most significant digits of scaled_upper.
// hi = floor(scaled_upper / one).
uint32_t hi = static_cast<uint32_t>(scaled_upper.f >> -one.e);
// lo (p2 in Grisu) contains the least significants digits of scaled_upper.
// lo = scaled_upper mod 1.
uint64_t lo = scaled_upper.f & (one.f - 1);
size = 0;
auto exp = count_digits(hi); // kappa in Grisu.
while (exp > 0) { while (exp > 0) {
uint32_t digit = 0; uint32_t digit = 0;
// This optimization by miloyip reduces the number of integer divisions by // This optimization by miloyip reduces the number of integer divisions by
@ -486,148 +504,32 @@ FMT_FUNC void grisu2_gen_digits(
FMT_ASSERT(false, "invalid number of digits"); FMT_ASSERT(false, "invalid number of digits");
} }
if (digit != 0 || size != 0) if (digit != 0 || size != 0)
buffer[size++] = static_cast<char>('0' + digit); buf[size++] = static_cast<char>('0' + digit);
--exp; --exp;
uint64_t remainder = (static_cast<uint64_t>(hi) << -one.e) + lo; uint64_t remainder = (static_cast<uint64_t>(hi) << -one.e) + lo;
if (remainder <= delta) { if (remainder <= delta || size > max_digits) {
dec_exp += exp; return grisu2_round(
// TODO: use scaled_value buf, size, max_digits, delta, remainder,
(void)scaled_value; static_cast<uint64_t>(data::POWERS_OF_10_32[exp]) << -one.e,
return; diff.f, exp);
} }
} }
// Generate digits for the least significant part (lo).
for (;;) { for (;;) {
lo *= 10; lo *= 10;
delta *= 10; delta *= 10;
char digit = static_cast<char>(lo >> -one.e); char digit = static_cast<char>(lo >> -one.e);
if (digit != 0 || size != 0) if (digit != 0 || size != 0)
buffer[size++] = static_cast<char>('0' + digit); buf[size++] = static_cast<char>('0' + digit);
lo &= one.f - 1; lo &= one.f - 1;
--exp; --exp;
if (lo < delta) { if (lo < delta || size > max_digits) {
dec_exp += exp; return grisu2_round(buf, size, max_digits, delta, lo, one.f,
return; diff.f * data::POWERS_OF_10_32[-exp], exp);
} }
} }
} }
FMT_FUNC void grisu2_format_positive(double value, char *buffer, size_t &size,
int &dec_exp) {
FMT_ASSERT(value > 0, "value is nonpositive");
fp fp_value(value);
fp lower, upper; // w^- and w^+ in the Grisu paper.
fp_value.compute_boundaries(lower, upper);
// Find a cached power of 10 close to 1 / upper.
const int min_exp = -60; // alpha in Grisu.
auto dec_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
min_exp - (upper.e + fp::significand_size), dec_exp);
dec_exp = -dec_exp;
fp_value.normalize();
fp scaled_value = fp_value * dec_pow;
fp scaled_lower = lower * dec_pow; // \tilde{M}^- in Grisu.
fp scaled_upper = upper * dec_pow; // \tilde{M}^+ in Grisu.
++scaled_lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
--scaled_upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
uint64_t delta = scaled_upper.f - scaled_lower.f;
grisu2_gen_digits(scaled_value, scaled_upper, delta, buffer, size, dec_exp);
}
FMT_FUNC void round(char *buffer, size_t &size, int &exp,
int digits_to_remove) {
size -= to_unsigned(digits_to_remove);
exp += digits_to_remove;
int digit = buffer[size] - '0';
// TODO: proper rounding and carry
if (digit > 5 || (digit == 5 && (digits_to_remove > 1 ||
(buffer[size - 1] - '0') % 2) != 0)) {
++buffer[size - 1];
}
}
// Writes the exponent exp in the form "[+-]d{1,3}" to buffer.
FMT_FUNC char *write_exponent(char *buffer, int exp) {
FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range");
if (exp < 0) {
*buffer++ = '-';
exp = -exp;
} else {
*buffer++ = '+';
}
if (exp >= 100) {
*buffer++ = static_cast<char>('0' + exp / 100);
exp %= 100;
const char *d = data::DIGITS + exp * 2;
*buffer++ = d[0];
*buffer++ = d[1];
} else {
const char *d = data::DIGITS + exp * 2;
*buffer++ = d[0];
*buffer++ = d[1];
}
return buffer;
}
FMT_FUNC void format_exp_notation(
char *buffer, size_t &size, int exp, int precision, bool upper) {
// Insert a decimal point after the first digit and add an exponent.
std::memmove(buffer + 2, buffer + 1, size - 1);
buffer[1] = '.';
exp += static_cast<int>(size) - 1;
int num_digits = precision - static_cast<int>(size) + 1;
if (num_digits > 0) {
std::uninitialized_fill_n(buffer + size + 1, num_digits, '0');
size += to_unsigned(num_digits);
} else if (num_digits < 0) {
round(buffer, size, exp, -num_digits);
}
char *p = buffer + size + 1;
*p++ = upper ? 'E' : 'e';
size = to_unsigned(write_exponent(p, exp) - buffer);
}
// Prettifies the output of the Grisu2 algorithm.
// The number is given as v = buffer * 10^exp.
FMT_FUNC void grisu2_prettify(char *buffer, size_t &size, int exp,
int precision, bool upper) {
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
int int_size = static_cast<int>(size);
int full_exp = int_size + exp;
const int exp_threshold = 21;
if (int_size <= full_exp && full_exp <= exp_threshold) {
// 1234e7 -> 12340000000[.0+]
std::uninitialized_fill_n(buffer + int_size, full_exp - int_size, '0');
char *p = buffer + full_exp;
if (precision > 0) {
*p++ = '.';
std::uninitialized_fill_n(p, precision, '0');
p += precision;
}
size = to_unsigned(p - buffer);
} else if (0 < full_exp && full_exp <= exp_threshold) {
// 1234e-2 -> 12.34[0+]
int fractional_size = -exp;
std::memmove(buffer + full_exp + 1, buffer + full_exp,
to_unsigned(fractional_size));
buffer[full_exp] = '.';
int num_zeros = precision - fractional_size;
if (num_zeros > 0) {
std::uninitialized_fill_n(buffer + size + 1, num_zeros, '0');
size += to_unsigned(num_zeros);
}
++size;
} else if (-6 < full_exp && full_exp <= 0) {
// 1234e-6 -> 0.001234
int offset = 2 - full_exp;
std::memmove(buffer + offset, buffer, size);
buffer[0] = '0';
buffer[1] = '.';
std::uninitialized_fill_n(buffer + 2, -full_exp, '0');
size = to_unsigned(int_size + offset);
} else {
format_exp_notation(buffer, size, exp, precision, upper);
}
}
#if FMT_CLANG_VERSION #if FMT_CLANG_VERSION
# define FMT_FALLTHROUGH [[clang::fallthrough]]; # define FMT_FALLTHROUGH [[clang::fallthrough]];
#elif FMT_GCC_VERSION >= 700 #elif FMT_GCC_VERSION >= 700
@ -636,58 +538,270 @@ FMT_FUNC void grisu2_prettify(char *buffer, size_t &size, int exp,
# define FMT_FALLTHROUGH # define FMT_FALLTHROUGH
#endif #endif
// Formats a nonnegative value using Grisu2 algorithm. Grisu2 doesn't give any struct gen_digits_params {
// guarantees on the shortness of the result. int num_digits;
FMT_FUNC void grisu2_format(double value, char *buffer, size_t &size, char type, bool fixed;
int precision, bool write_decimal_point) { bool upper;
FMT_ASSERT(value >= 0, "value is negative"); bool trailing_zeros;
int dec_exp = 0; // K in Grisu. };
if (value > 0) {
grisu2_format_positive(value, buffer, size, dec_exp); struct prettify_handler {
char *data;
ptrdiff_t size;
buffer &buf;
explicit prettify_handler(buffer &b, ptrdiff_t n)
: data(b.data()), size(n), buf(b) {}
~prettify_handler() {
assert(buf.size() >= to_unsigned(size));
buf.resize(to_unsigned(size));
}
template <typename F>
void insert(ptrdiff_t pos, ptrdiff_t n, F f) {
std::memmove(data + pos + n, data + pos, to_unsigned(size - pos));
f(data + pos);
size += n;
}
void insert(ptrdiff_t pos, char c) {
std::memmove(data + pos + 1, data + pos, to_unsigned(size - pos));
data[pos] = c;
++size;
}
void append(ptrdiff_t n, char c) {
std::uninitialized_fill_n(data + size, n, c);
size += n;
}
void append(char c) { data[size++] = c; }
void remove_trailing(char c) {
while (data[size - 1] == c) --size;
}
};
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
template <typename Handler>
FMT_FUNC void write_exponent(int exp, Handler &&h) {
FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range");
if (exp < 0) {
h.append('-');
exp = -exp;
} else { } else {
*buffer = '0'; h.append('+');
size = 1;
} }
const int default_precision = 6; if (exp >= 100) {
if (precision < 0) h.append(static_cast<char>('0' + exp / 100));
precision = default_precision; exp %= 100;
bool upper = false; const char *d = data::DIGITS + exp * 2;
switch (type) { h.append(d[0]);
case 'G': h.append(d[1]);
upper = true; } else {
FMT_FALLTHROUGH const char *d = data::DIGITS + exp * 2;
case '\0': case 'g': { h.append(d[0]);
int digits_to_remove = static_cast<int>(size) - precision; h.append(d[1]);
if (digits_to_remove > 0) {
round(buffer, size, dec_exp, digits_to_remove);
// Remove trailing zeros.
while (size > 0 && buffer[size - 1] == '0') {
--size;
++dec_exp;
} }
} }
precision = 0;
break; struct fill {
size_t n;
void operator()(char *buf) const {
buf[0] = '0';
buf[1] = '.';
std::uninitialized_fill_n(buf + 2, n, '0');
} }
case 'F': };
upper = true;
FMT_FALLTHROUGH // The number is given as v = f * pow(10, exp), where f has size digits.
case 'f': { template <typename Handler>
int digits_to_remove = -dec_exp - precision; FMT_FUNC void grisu2_prettify(const gen_digits_params &params,
if (digits_to_remove > 0) { int size, int exp, Handler &&handler) {
if (digits_to_remove >= static_cast<int>(size)) if (!params.fixed) {
digits_to_remove = static_cast<int>(size) - 1; // Insert a decimal point after the first digit and add an exponent.
round(buffer, size, dec_exp, digits_to_remove); handler.insert(1, '.');
} exp += size - 1;
break; if (size < params.num_digits)
} handler.append(params.num_digits - size, '0');
case 'e': case 'E': handler.append(params.upper ? 'E' : 'e');
format_exp_notation(buffer, size, dec_exp, precision, type == 'E'); write_exponent(exp, handler);
return; return;
} }
if (write_decimal_point && precision < 1) // pow(10, full_exp - 1) <= v <= pow(10, full_exp).
precision = 1; int full_exp = size + exp;
grisu2_prettify(buffer, size, dec_exp, precision, upper); const int exp_threshold = 21;
if (size <= full_exp && full_exp <= exp_threshold) {
// 1234e7 -> 12340000000[.0+]
handler.append(full_exp - size, '0');
int num_zeros = params.num_digits - full_exp;
if (num_zeros > 0 && params.trailing_zeros) {
handler.append('.');
handler.append(num_zeros, '0');
}
} else if (full_exp > 0) {
// 1234e-2 -> 12.34[0+]
handler.insert(full_exp, '.');
if (!params.trailing_zeros) {
// Remove trailing zeros.
handler.remove_trailing('0');
} else if (params.num_digits > size) {
// Add trailing zeros.
ptrdiff_t num_zeros = params.num_digits - size;
handler.append(num_zeros, '0');
}
} else {
// 1234e-6 -> 0.001234
handler.insert(0, 2 - full_exp, fill{to_unsigned(-full_exp)});
}
}
struct char_counter {
ptrdiff_t size;
template <typename F>
void insert(ptrdiff_t, ptrdiff_t n, F) { size += n; }
void insert(ptrdiff_t, char) { ++size; }
void append(ptrdiff_t n, char) { size += n; }
void append(char) { ++size; }
void remove_trailing(char) {}
};
// Converts format specifiers into parameters for digit generation and computes
// output buffer size for a number in the range [pow(10, exp - 1), pow(10, exp)
// or 0 if exp == 1.
FMT_FUNC gen_digits_params process_specs(const core_format_specs &specs,
int exp, buffer &buf) {
auto params = gen_digits_params();
int num_digits = specs.precision >= 0 ? specs.precision : 6;
switch (specs.type) {
case 'G':
params.upper = true;
FMT_FALLTHROUGH
case '\0': case 'g':
params.trailing_zeros = (specs.flags & HASH_FLAG) != 0;
if (-4 <= exp && exp < num_digits + 1) {
params.fixed = true;
if (!specs.type && params.trailing_zeros && exp >= 0)
num_digits = exp + 1;
}
break;
case 'F':
params.upper = true;
FMT_FALLTHROUGH
case 'f': {
params.fixed = true;
params.trailing_zeros = true;
int adjusted_min_digits = num_digits + exp;
if (adjusted_min_digits > 0)
num_digits = adjusted_min_digits;
break;
}
case 'E':
params.upper = true;
FMT_FALLTHROUGH
case 'e':
++num_digits;
break;
}
params.num_digits = num_digits;
char_counter counter{num_digits};
grisu2_prettify(params, params.num_digits, exp - num_digits, counter);
buf.resize(to_unsigned(counter.size));
return params;
}
template <typename Double>
FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type
grisu2_format(Double value, buffer &buf, core_format_specs specs) {
FMT_ASSERT(value >= 0, "value is negative");
if (value == 0) {
gen_digits_params params = process_specs(specs, 1, buf);
const size_t size = 1;
buf[0] = '0';
grisu2_prettify(params, size, 0, prettify_handler(buf, size));
return true;
}
fp fp_value(value);
fp lower, upper; // w^- and w^+ in the Grisu paper.
fp_value.compute_boundaries(lower, upper);
// Find a cached power of 10 close to 1 / upper and use it to scale upper.
const int min_exp = -60; // alpha in Grisu.
int cached_exp = 0; // K in Grisu.
auto cached_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
min_exp - (upper.e + fp::significand_size), cached_exp);
cached_exp = -cached_exp;
upper = upper * cached_pow; // \tilde{M}^+ in Grisu.
--upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
fp one(1ull << -upper.e, upper.e);
// hi (p1 in Grisu) contains the most significant digits of scaled_upper.
// hi = floor(upper / one).
uint32_t hi = static_cast<uint32_t>(upper.f >> -one.e);
int exp = count_digits(hi); // kappa in Grisu.
gen_digits_params params = process_specs(specs, cached_exp + exp, buf);
fp_value.normalize();
fp scaled_value = fp_value * cached_pow;
lower = lower * cached_pow; // \tilde{M}^- in Grisu.
++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
uint64_t delta = upper.f - lower.f;
fp diff = upper - scaled_value; // wp_w in Grisu.
// lo (p2 in Grisu) contains the least significants digits of scaled_upper.
// lo = supper % one.
uint64_t lo = upper.f & (one.f - 1);
int size = 0;
if (!grisu2_gen_digits(buf.data(), size, hi, lo, exp, delta, one, diff,
params.num_digits)) {
buf.clear();
return false;
}
grisu2_prettify(params, size, cached_exp + exp, prettify_handler(buf, size));
return true;
}
template <typename Double>
void sprintf_format(Double value, internal::buffer &buf,
core_format_specs spec) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
// Build format string.
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
char format[MAX_FORMAT_SIZE];
char *format_ptr = format;
*format_ptr++ = '%';
if (spec.has(HASH_FLAG))
*format_ptr++ = '#';
if (spec.precision >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
}
if (std::is_same<Double, long double>::value)
*format_ptr++ = 'L';
*format_ptr++ = spec.type;
*format_ptr = '\0';
// Format using snprintf.
char *start = FMT_NULL;
for (;;) {
std::size_t buffer_size = buf.capacity();
start = &buf[0];
int result = internal::char_traits<char>::format_float(
start, buffer_size, format, spec.precision, value);
if (result >= 0) {
unsigned n = internal::to_unsigned(result);
if (n < buf.capacity()) {
buf.resize(n);
break; // The buffer is large enough - continue with formatting.
}
buf.reserve(n + 1);
} else {
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buf.reserve(buf.capacity() + 1);
}
}
} }
} // namespace internal } // namespace internal
@ -812,11 +926,6 @@ FMT_FUNC void format_system_error(
format_error_code(out, error_code, message); format_error_code(out, error_code, message);
} }
template <typename Char>
void basic_fixed_buffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow"));
}
FMT_FUNC void internal::error_handler::on_error(const char *message) { FMT_FUNC void internal::error_handler::on_error(const char *message) {
FMT_THROW(format_error(message)); FMT_THROW(format_error(message));
} }
@ -835,13 +944,14 @@ FMT_FUNC void report_windows_error(
FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args) { FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args) {
memory_buffer buffer; memory_buffer buffer;
vformat_to(buffer, format_str, args); internal::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>::type>(args));
std::fwrite(buffer.data(), 1, buffer.size(), f); std::fwrite(buffer.data(), 1, buffer.size(), f);
} }
FMT_FUNC void vprint(std::FILE *f, wstring_view format_str, wformat_args args) { FMT_FUNC void vprint(std::FILE *f, wstring_view format_str, wformat_args args) {
wmemory_buffer buffer; wmemory_buffer buffer;
vformat_to(buffer, format_str, args); internal::vformat_to(buffer, format_str, args);
std::fwrite(buffer.data(), sizeof(wchar_t), buffer.size(), f); std::fwrite(buffer.data(), sizeof(wchar_t), buffer.size(), f);
} }
@ -853,10 +963,6 @@ FMT_FUNC void vprint(wstring_view format_str, wformat_args args) {
vprint(stdout, format_str, args); vprint(stdout, format_str, args);
} }
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
FMT_FUNC locale locale_provider::locale() { return fmt::locale(); }
#endif
FMT_END_NAMESPACE FMT_END_NAMESPACE
#ifdef _MSC_VER #ifdef _MSC_VER

File diff suppressed because it is too large Load diff

77
include/fmt/locale.h Normal file
View file

@ -0,0 +1,77 @@
// Formatting library for C++ - std::locale support
//
// Copyright (c) 2012 - present, 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
namespace internal {
template <typename Char>
typename buffer_context<Char>::type::iterator vformat_to(
const std::locale &loc, basic_buffer<Char> &buf,
basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) {
typedef back_insert_range<basic_buffer<Char> > range;
return vformat_to<arg_formatter<range>>(
buf, to_string_view(format_str), args, internal::locale_ref(loc));
}
template <typename Char>
std::basic_string<Char> vformat(
const std::locale &loc, basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer);
}
}
template <typename S, typename Char = FMT_CHAR(S)>
inline std::basic_string<Char> vformat(
const std::locale &loc, const S &format_str,
basic_format_args<typename buffer_context<Char>::type> args) {
return internal::vformat(loc, to_string_view(format_str), args);
}
template <typename S, typename... Args>
inline std::basic_string<FMT_CHAR(S)> format(
const std::locale &loc, const S &format_str, const Args &... args) {
return internal::vformat(
loc, to_string_view(format_str),
*internal::checked_args<S, Args...>(format_str, args...));
}
template <typename String, typename OutputIt, typename... Args>
inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value,
OutputIt>::type
vformat_to(OutputIt out, const std::locale &loc, const String &format_str,
typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) {
typedef output_range<OutputIt, FMT_CHAR(String)> range;
return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
}
template <typename OutputIt, typename S, typename... Args>
inline typename std::enable_if<
internal::is_string<S>::value &&
internal::is_output_iterator<OutputIt>::value, OutputIt>::type
format_to(OutputIt out, const std::locale &loc, const S &format_str,
const Args &... args) {
internal::check_format_string<Args...>(format_str);
typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context;
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
}
FMT_END_NAMESPACE
#endif // FMT_LOCALE_H_

View file

@ -1,6 +1,6 @@
// Formatting library for C++ - std::ostream support // Formatting library for C++ - std::ostream support
// //
// Copyright (c) 2012 - 2016, Victor Zverovich // Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved. // All rights reserved.
// //
// For the license information refer to format.h. // For the license information refer to format.h.
@ -129,7 +129,7 @@ inline void vprint(std::basic_ostream<Char> &os,
basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<typename buffer_context<Char>::type> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
vformat_to(buffer, format_str, args); internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer); internal::write(os, buffer);
} }
/** /**
@ -141,16 +141,12 @@ inline void vprint(std::basic_ostream<Char> &os,
fmt::print(cerr, "Don't {}!", "panic"); fmt::print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
template <typename... Args> template <typename S, typename... Args>
inline void print(std::ostream &os, string_view format_str, inline typename std::enable_if<internal::is_string<S>::value>::type
print(std::basic_ostream<FMT_CHAR(S)> &os, const S &format_str,
const Args & ... args) { const Args & ... args) {
vprint<char>(os, format_str, make_format_args<format_context>(args...)); internal::checked_args<S, Args...> ca(format_str, args...);
} vprint(os, to_string_view(format_str), *ca);
template <typename... Args>
inline void print(std::wostream &os, wstring_view format_str,
const Args & ... args) {
vprint<wchar_t>(os, format_str, make_format_args<wformat_context>(args...));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View file

@ -137,7 +137,7 @@ class buffered_file {
buffered_file() 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 ~buffered_file() FMT_DTOR_NOEXCEPT; FMT_API ~buffered_file() FMT_NOEXCEPT;
private: private:
buffered_file(const buffered_file &) = delete; buffered_file(const buffered_file &) = delete;
@ -223,7 +223,7 @@ class file {
} }
// 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_NOEXCEPT;
// Returns the file descriptor. // Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; } int descriptor() const FMT_NOEXCEPT { return fd_; }

View file

@ -16,6 +16,130 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
// An iterator that produces a null terminator on *end. This simplifies parsing
// and allows comparing the performance of processing a null-terminated string
// vs string_view.
template <typename Char>
class null_terminating_iterator {
public:
typedef std::ptrdiff_t difference_type;
typedef Char value_type;
typedef const Char* pointer;
typedef const Char& reference;
typedef std::random_access_iterator_tag iterator_category;
null_terminating_iterator() : ptr_(0), end_(0) {}
FMT_CONSTEXPR null_terminating_iterator(const Char *ptr, const Char *end)
: ptr_(ptr), end_(end) {}
template <typename Range>
FMT_CONSTEXPR explicit null_terminating_iterator(const Range &r)
: ptr_(r.begin()), end_(r.end()) {}
FMT_CONSTEXPR null_terminating_iterator &operator=(const Char *ptr) {
assert(ptr <= end_);
ptr_ = ptr;
return *this;
}
FMT_CONSTEXPR Char operator*() const {
return ptr_ != end_ ? *ptr_ : Char();
}
FMT_CONSTEXPR null_terminating_iterator operator++() {
++ptr_;
return *this;
}
FMT_CONSTEXPR null_terminating_iterator operator++(int) {
null_terminating_iterator result(*this);
++ptr_;
return result;
}
FMT_CONSTEXPR null_terminating_iterator operator--() {
--ptr_;
return *this;
}
FMT_CONSTEXPR null_terminating_iterator operator+(difference_type n) {
return null_terminating_iterator(ptr_ + n, end_);
}
FMT_CONSTEXPR null_terminating_iterator operator-(difference_type n) {
return null_terminating_iterator(ptr_ - n, end_);
}
FMT_CONSTEXPR null_terminating_iterator operator+=(difference_type n) {
ptr_ += n;
return *this;
}
FMT_CONSTEXPR difference_type operator-(
null_terminating_iterator other) const {
return ptr_ - other.ptr_;
}
FMT_CONSTEXPR bool operator!=(null_terminating_iterator other) const {
return ptr_ != other.ptr_;
}
bool operator>=(null_terminating_iterator other) const {
return ptr_ >= other.ptr_;
}
// This should be a friend specialization pointer_from<Char> but the latter
// doesn't compile by gcc 5.1 due to a compiler bug.
template <typename CharT>
friend FMT_CONSTEXPR_DECL const CharT *pointer_from(
null_terminating_iterator<CharT> it);
private:
const Char *ptr_;
const Char *end_;
};
template <typename T>
FMT_CONSTEXPR const T *pointer_from(const T *p) { return p; }
template <typename Char>
FMT_CONSTEXPR const Char *pointer_from(null_terminating_iterator<Char> it) {
return it.ptr_;
}
// DEPRECATED: Parses the input as an unsigned integer. This function assumes
// that the first character is a digit and presence of a non-digit character at
// the end.
// it: an iterator pointing to the beginning of the input range.
template <typename Iterator, typename ErrorHandler>
FMT_CONSTEXPR unsigned parse_nonnegative_int(Iterator &it, ErrorHandler &&eh) {
assert('0' <= *it && *it <= '9');
if (*it == '0') {
++it;
return 0;
}
unsigned value = 0;
// Convert to unsigned to prevent a warning.
unsigned max_int = (std::numeric_limits<int>::max)();
unsigned big = max_int / 10;
do {
// Check for overflow.
if (value > big) {
value = max_int + 1;
break;
}
value = value * 10 + unsigned(*it - '0');
// Workaround for MSVC "setup_exception stack overflow" error:
auto next = it;
++next;
it = next;
} while ('0' <= *it && *it <= '9');
if (value > max_int)
eh.on_error("number is too big");
return value;
}
// Checks if a value fits in int - used to avoid warnings about comparing // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
template <bool IsSigned> template <bool IsSigned>
@ -133,7 +257,7 @@ class arg_converter: public function<void> {
// unsigned). // unsigned).
template <typename T, typename Context, typename Char> template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context> &arg, Char type) { void convert_arg(basic_format_arg<Context> &arg, Char type) {
fmt::visit(arg_converter<T, Context>(arg, type), arg); visit_format_arg(arg_converter<T, Context>(arg, type), arg);
} }
// Converts an integer argument to char for printf. // Converts an integer argument to char for printf.
@ -192,8 +316,16 @@ class printf_width_handler: public function<unsigned> {
return 0; return 0;
} }
}; };
template <typename Char, typename Context>
void printf(basic_buffer<Char> &buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format();
}
} // namespace internal } // namespace internal
using internal::printf; // For printing into memory_buffer.
template <typename Range> template <typename Range>
class printf_arg_formatter; class printf_arg_formatter;
@ -222,12 +354,12 @@ class printf_arg_formatter:
context_type &context_; context_type &context_;
void write_null_pointer(char) { void write_null_pointer(char) {
this->spec()->type_ = 0; this->spec()->type = 0;
this->write("(nil)"); this->write("(nil)");
} }
void write_null_pointer(wchar_t) { void write_null_pointer(wchar_t) {
this->spec()->type_ = 0; this->spec()->type = 0;
this->write(L"(nil)"); this->write(L"(nil)");
} }
@ -243,7 +375,8 @@ class printf_arg_formatter:
*/ */
printf_arg_formatter(internal::basic_buffer<char_type> &buffer, printf_arg_formatter(internal::basic_buffer<char_type> &buffer,
format_specs &spec, context_type &ctx) format_specs &spec, context_type &ctx)
: base(back_insert_range<internal::basic_buffer<char_type>>(buffer), &spec), : base(back_insert_range<internal::basic_buffer<char_type>>(buffer), &spec,
ctx.locale()),
context_(ctx) {} context_(ctx) {}
template <typename T> template <typename T>
@ -253,15 +386,15 @@ class printf_arg_formatter:
// use std::is_same instead. // use std::is_same instead.
if (std::is_same<T, bool>::value) { if (std::is_same<T, bool>::value) {
format_specs &fmt_spec = *this->spec(); format_specs &fmt_spec = *this->spec();
if (fmt_spec.type_ != 's') if (fmt_spec.type != 's')
return base::operator()(value ? 1 : 0); return base::operator()(value ? 1 : 0);
fmt_spec.type_ = 0; fmt_spec.type = 0;
this->write(value != 0); this->write(value != 0);
} else if (std::is_same<T, char_type>::value) { } else if (std::is_same<T, char_type>::value) {
format_specs &fmt_spec = *this->spec(); format_specs &fmt_spec = *this->spec();
if (fmt_spec.type_ && fmt_spec.type_ != 'c') if (fmt_spec.type && fmt_spec.type != 'c')
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
fmt_spec.flags_ = 0; fmt_spec.flags = 0;
fmt_spec.align_ = ALIGN_RIGHT; fmt_spec.align_ = ALIGN_RIGHT;
return base::operator()(value); return base::operator()(value);
} else { } else {
@ -280,7 +413,7 @@ class printf_arg_formatter:
iterator operator()(const char *value) { iterator operator()(const char *value) {
if (value) if (value)
base::operator()(value); base::operator()(value);
else if (this->spec()->type_ == 'p') else if (this->spec()->type == 'p')
write_null_pointer(char_type()); write_null_pointer(char_type());
else else
this->write("(null)"); this->write("(null)");
@ -291,7 +424,7 @@ class printf_arg_formatter:
iterator operator()(const wchar_t *value) { iterator operator()(const wchar_t *value) {
if (value) if (value)
base::operator()(value); base::operator()(value);
else if (this->spec()->type_ == 'p') else if (this->spec()->type == 'p')
write_null_pointer(char_type()); write_null_pointer(char_type());
else else
this->write(L"(null)"); this->write(L"(null)");
@ -310,7 +443,7 @@ class printf_arg_formatter:
iterator operator()(const void *value) { iterator operator()(const void *value) {
if (value) if (value)
return base::operator()(value); return base::operator()(value);
this->spec()->type_ = 0; this->spec()->type = 0;
write_null_pointer(char_type()); write_null_pointer(char_type());
return this->out(); return this->out();
} }
@ -394,16 +527,16 @@ void basic_printf_context<OutputIt, Char, AF>::parse_flags(
spec.align_ = ALIGN_LEFT; spec.align_ = ALIGN_LEFT;
break; break;
case '+': case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG; spec.flags |= SIGN_FLAG | PLUS_FLAG;
break; break;
case '0': case '0':
spec.fill_ = '0'; spec.fill_ = '0';
break; break;
case ' ': case ' ':
spec.flags_ |= SIGN_FLAG; spec.flags |= SIGN_FLAG;
break; break;
case '#': case '#':
spec.flags_ |= HASH_FLAG; spec.flags |= HASH_FLAG;
break; break;
default: default:
--it; --it;
@ -453,8 +586,8 @@ unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
spec.width_ = parse_nonnegative_int(it, eh); spec.width_ = parse_nonnegative_int(it, eh);
} else if (*it == '*') { } else if (*it == '*') {
++it; ++it;
spec.width_ = spec.width_ = visit_format_arg(
fmt::visit(internal::printf_width_handler<char_type>(spec), get_arg(it)); internal::printf_width_handler<char_type>(spec), get_arg(it));
} }
return arg_index; return arg_index;
} }
@ -486,19 +619,19 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
++it; ++it;
if ('0' <= *it && *it <= '9') { if ('0' <= *it && *it <= '9') {
internal::error_handler eh; internal::error_handler eh;
spec.precision_ = static_cast<int>(parse_nonnegative_int(it, eh)); spec.precision = static_cast<int>(parse_nonnegative_int(it, eh));
} else if (*it == '*') { } else if (*it == '*') {
++it; ++it;
spec.precision_ = spec.precision =
fmt::visit(internal::printf_precision_handler(), get_arg(it)); visit_format_arg(internal::printf_precision_handler(), get_arg(it));
} else { } else {
spec.precision_ = 0; spec.precision = 0;
} }
} }
format_arg arg = get_arg(it, arg_index); format_arg arg = get_arg(it, arg_index);
if (spec.flag(HASH_FLAG) && fmt::visit(internal::is_zero_int(), arg)) if (spec.has(HASH_FLAG) && visit_format_arg(internal::is_zero_int(), arg))
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG); spec.flags = static_cast<uint_least8_t>(spec.flags & (~internal::to_unsigned<int>(HASH_FLAG)));
if (spec.fill_ == '0') { if (spec.fill_ == '0') {
if (arg.is_arithmetic()) if (arg.is_arithmetic())
spec.align_ = ALIGN_NUMERIC; spec.align_ = ALIGN_NUMERIC;
@ -542,16 +675,17 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
// Parse type. // Parse type.
if (!*it) if (!*it)
FMT_THROW(format_error("invalid format string")); FMT_THROW(format_error("invalid format string"));
spec.type_ = static_cast<char>(*it++); spec.type = static_cast<char>(*it++);
if (arg.is_integral()) { if (arg.is_integral()) {
// Normalize type. // Normalize type.
switch (spec.type_) { switch (spec.type) {
case 'i': case 'u': case 'i': case 'u':
spec.type_ = 'd'; spec.type = 'd';
break; break;
case 'c': case 'c':
// TODO: handle wchar_t better? // TODO: handle wchar_t better?
fmt::visit(internal::char_converter<basic_printf_context>(arg), arg); visit_format_arg(
internal::char_converter<basic_printf_context>(arg), arg);
break; break;
} }
} }
@ -559,35 +693,50 @@ void basic_printf_context<OutputIt, Char, AF>::format() {
start = it; start = it;
// Format argument. // Format argument.
fmt::visit(AF(buffer, spec, *this), arg); visit_format_arg(AF(buffer, spec, *this), arg);
} }
buffer.append(pointer_from(start), pointer_from(it)); buffer.append(pointer_from(start), pointer_from(it));
} }
template <typename Char, typename Context>
void printf(internal::basic_buffer<Char> &buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format();
}
template <typename Buffer> template <typename Buffer>
struct printf_context { struct basic_printf_context_t {
typedef basic_printf_context< typedef basic_printf_context<
std::back_insert_iterator<Buffer>, typename Buffer::value_type> type; std::back_insert_iterator<Buffer>, typename Buffer::value_type> type;
}; };
template <typename ...Args> typedef basic_printf_context_t<internal::buffer>::type printf_context;
inline format_arg_store<printf_context<internal::buffer>::type, Args...> typedef basic_printf_context_t<internal::wbuffer>::type wprintf_context;
make_printf_args(const Args & ... args) {
return format_arg_store<printf_context<internal::buffer>::type, Args...>(
args...);
}
typedef basic_format_args<printf_context<internal::buffer>::type> printf_args;
typedef basic_format_args<printf_context<internal::wbuffer>::type> wprintf_args;
inline std::string vsprintf(string_view format, printf_args args) { typedef basic_format_args<printf_context> printf_args;
memory_buffer buffer; typedef basic_format_args<wprintf_context> wprintf_args;
printf(buffer, format, args);
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst
*/
template<typename... Args>
inline format_arg_store<printf_context, Args...>
make_printf_args(const Args &... args) { return {args...}; }
/**
\rst
Constructs an `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst
*/
template<typename... Args>
inline format_arg_store<wprintf_context, Args...>
make_wprintf_args(const Args &... args) { return {args...}; }
template <typename S, typename Char = FMT_CHAR(S)>
inline std::basic_string<Char>
vsprintf(const S &format,
basic_format_args<typename basic_printf_context_t<
internal::basic_buffer<Char>>::type> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
return to_string(buffer); return to_string(buffer);
} }
@ -600,30 +749,24 @@ inline std::string vsprintf(string_view format, printf_args args) {
std::string message = fmt::sprintf("The answer is %d", 42); std::string message = fmt::sprintf("The answer is %d", 42);
\endrst \endrst
*/ */
template <typename... Args> template <typename S, typename... Args>
inline std::string sprintf(string_view format_str, const Args & ... args) { inline FMT_ENABLE_IF_T(
return vsprintf(format_str, internal::is_string<S>::value, std::basic_string<FMT_CHAR(S)>)
make_format_args<typename printf_context<internal::buffer>::type>(args...)); sprintf(const S &format, const Args & ... args) {
internal::check_format_string<Args...>(format);
typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
typedef typename basic_printf_context_t<buffer>::type context;
format_arg_store<context, Args...> as{ args... };
return vsprintf(to_string_view(format),
basic_format_args<context>(as));
} }
inline std::wstring vsprintf(wstring_view format, wprintf_args args) { template <typename S, typename Char = FMT_CHAR(S)>
wmemory_buffer buffer; inline int vfprintf(std::FILE *f, const S &format,
printf(buffer, format, args); basic_format_args<typename basic_printf_context_t<
return to_string(buffer);
}
template <typename... Args>
inline std::wstring sprintf(wstring_view format_str, const Args & ... args) {
return vsprintf(format_str,
make_format_args<typename printf_context<internal::wbuffer>::type>(args...));
}
template <typename Char>
inline int vfprintf(std::FILE *f, basic_string_view<Char> format,
basic_format_args<typename printf_context<
internal::basic_buffer<Char>>::type> args) { internal::basic_buffer<Char>>::type> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
printf(buffer, format, args); printf(buffer, to_string_view(format), args);
std::size_t size = buffer.size(); std::size_t size = buffer.size();
return std::fwrite( return std::fwrite(
buffer.data(), sizeof(Char), size, f) < size ? -1 : static_cast<int>(size); buffer.data(), sizeof(Char), size, f) < size ? -1 : static_cast<int>(size);
@ -638,26 +781,22 @@ inline int vfprintf(std::FILE *f, basic_string_view<Char> format,
fmt::fprintf(stderr, "Don't %s!", "panic"); fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst \endrst
*/ */
template <typename... Args> template <typename S, typename... Args>
inline int fprintf(std::FILE *f, string_view format_str, const Args & ... args) { inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
auto vargs = make_format_args< fprintf(std::FILE *f, const S &format, const Args & ... args) {
typename printf_context<internal::buffer>::type>(args...); internal::check_format_string<Args...>(format);
return vfprintf<char>(f, format_str, vargs); typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
typedef typename basic_printf_context_t<buffer>::type context;
format_arg_store<context, Args...> as{ args... };
return vfprintf(f, to_string_view(format),
basic_format_args<context>(as));
} }
template <typename... Args> template <typename S, typename Char = FMT_CHAR(S)>
inline int fprintf(std::FILE *f, wstring_view format_str, inline int vprintf(const S &format,
const Args & ... args) { basic_format_args<typename basic_printf_context_t<
return vfprintf(f, format_str, internal::basic_buffer<Char>>::type> args) {
make_format_args<typename printf_context<internal::wbuffer>::type>(args...)); return vfprintf(stdout, to_string_view(format), args);
}
inline int vprintf(string_view format, printf_args args) {
return vfprintf(stdout, format, args);
}
inline int vprintf(wstring_view format, wprintf_args args) {
return vfprintf(stdout, format, args);
} }
/** /**
@ -669,30 +808,24 @@ inline int vprintf(wstring_view format, wprintf_args args) {
fmt::printf("Elapsed time: %.2f seconds", 1.23); fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst \endrst
*/ */
template <typename... Args> template <typename S, typename... Args>
inline int printf(string_view format_str, const Args & ... args) { inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
return vprintf(format_str, printf(const S &format_str, const Args & ... args) {
make_format_args<typename printf_context<internal::buffer>::type>(args...)); internal::check_format_string<Args...>(format_str);
typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
typedef typename basic_printf_context_t<buffer>::type context;
format_arg_store<context, Args...> as{ args... };
return vprintf(to_string_view(format_str),
basic_format_args<context>(as));
} }
template <typename... Args> template <typename S, typename Char = FMT_CHAR(S)>
inline int printf(wstring_view format_str, const Args & ... args) { inline int vfprintf(std::basic_ostream<Char> &os,
return vprintf(format_str, const S &format,
make_format_args<typename printf_context<internal::wbuffer>::type>(args...)); basic_format_args<typename basic_printf_context_t<
} internal::basic_buffer<Char>>::type> args) {
basic_memory_buffer<Char> buffer;
inline int vfprintf(std::ostream &os, string_view format_str, printf(buffer, to_string_view(format), args);
printf_args args) {
memory_buffer buffer;
printf(buffer, format_str, args);
internal::write(os, buffer);
return static_cast<int>(buffer.size());
}
inline int vfprintf(std::wostream &os, wstring_view format_str,
wprintf_args args) {
wmemory_buffer buffer;
printf(buffer, format_str, args);
internal::write(os, buffer); internal::write(os, buffer);
return static_cast<int>(buffer.size()); return static_cast<int>(buffer.size());
} }
@ -706,20 +839,16 @@ inline int vfprintf(std::wostream &os, wstring_view format_str,
fmt::fprintf(cerr, "Don't %s!", "panic"); fmt::fprintf(cerr, "Don't %s!", "panic");
\endrst \endrst
*/ */
template <typename... Args> template <typename S, typename... Args>
inline int fprintf(std::ostream &os, string_view format_str, inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
const Args & ... args) { fprintf(std::basic_ostream<FMT_CHAR(S)> &os,
auto vargs = make_format_args< const S &format_str, const Args & ... args) {
typename printf_context<internal::buffer>::type>(args...); internal::check_format_string<Args...>(format_str);
return vfprintf(os, format_str, vargs); typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
} typedef typename basic_printf_context_t<buffer>::type context;
format_arg_store<context, Args...> as{ args... };
template <typename... Args> return vfprintf(os, to_string_view(format_str),
inline int fprintf(std::wostream &os, wstring_view format_str, basic_format_args<context>(as));
const Args & ... args) {
auto vargs = make_format_args<
typename printf_context<internal::buffer>::type>(args...);
return vfprintf(os, format_str, vargs);
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View file

@ -1,6 +1,6 @@
// Formatting library for C++ - time formatting // Formatting library for C++ - time formatting
// //
// Copyright (c) 2012 - 2016, Victor Zverovich // Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved. // All rights reserved.
// //
// For the license information refer to format.h. // For the license information refer to format.h.
@ -10,6 +10,7 @@
#include "format.h" #include "format.h"
#include <ctime> #include <ctime>
#include <locale>
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
@ -22,7 +23,7 @@ inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); } inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); } inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); } inline null<> gmtime_s(...) { return null<>(); }
} } // namespace internal
// 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) {
@ -46,18 +47,20 @@ inline std::tm localtime(std::time_t time) {
bool fallback(int res) { return res == 0; } bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) { bool fallback(internal::null<>) {
using namespace fmt::internal; using namespace fmt::internal;
std::tm *tm = std::localtime(&time_); std::tm *tm = std::localtime(&time_);
if (tm) tm_ = *tm; if (tm) tm_ = *tm;
return tm != FMT_NULL; return tm != FMT_NULL;
} }
#endif
}; };
dispatcher lt(time); dispatcher lt(time);
if (lt.run())
return lt.tm_;
// Too big time values may be unsupported. // Too big time values may be unsupported.
if (!lt.run())
FMT_THROW(format_error("time_t value out of range")); FMT_THROW(format_error("time_t value out of range"));
return lt.tm_;
} }
// Thread-safe replacement for std::gmtime // Thread-safe replacement for std::gmtime
@ -82,17 +85,19 @@ inline std::tm gmtime(std::time_t time) {
bool fallback(int res) { return res == 0; } bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) { bool fallback(internal::null<>) {
std::tm *tm = std::gmtime(&time_); std::tm *tm = std::gmtime(&time_);
if (tm) tm_ = *tm; if (tm) tm_ = *tm;
return tm != FMT_NULL; return tm != FMT_NULL;
} }
#endif
}; };
dispatcher gt(time); dispatcher gt(time);
if (gt.run())
return gt.tm_;
// Too big time values may be unsupported. // Too big time values may be unsupported.
if (!gt.run())
FMT_THROW(format_error("time_t value out of range")); FMT_THROW(format_error("time_t value out of range"));
return gt.tm_;
} }
namespace internal { namespace internal {
@ -111,22 +116,21 @@ template <typename Char>
struct formatter<std::tm, Char> { struct formatter<std::tm, Char> {
template <typename ParseContext> template <typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
auto it = internal::null_terminating_iterator<Char>(ctx); auto it = ctx.begin();
if (*it == ':') if (it != ctx.end() && *it == ':')
++it; ++it;
auto end = it; auto end = it;
while (*end && *end != '}') while (end != ctx.end() && *end != '}')
++end; ++end;
tm_format.reserve(end - it + 1); tm_format.reserve(internal::to_unsigned(end - it + 1));
using internal::pointer_from; tm_format.append(it, end);
tm_format.append(pointer_from(it), pointer_from(end));
tm_format.push_back('\0'); tm_format.push_back('\0');
return pointer_from(end); return end;
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) { auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) {
internal::basic_buffer<Char> &buf = internal::get_container(ctx.out()); basic_memory_buffer<Char> buf;
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;
@ -146,7 +150,7 @@ struct formatter<std::tm, Char> {
const std::size_t MIN_GROWTH = 10; const std::size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
} }
return ctx.out(); return std::copy(buf.begin(), buf.end(), ctx.out());
} }
basic_memory_buffer<Char> tm_format; basic_memory_buffer<Char> tm_format;

View file

@ -9,16 +9,16 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template struct internal::basic_data<void>; template struct internal::basic_data<void>;
template FMT_API internal::locale_ref::locale_ref(const std::locale &loc);
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
// Explicit instantiations for char. // Explicit instantiations for char.
template FMT_API char internal::thousands_sep(locale_provider *lp); template FMT_API char internal::thousands_sep_impl(locale_ref);
template void internal::basic_buffer<char>::append(const char *, const char *); template FMT_API void internal::basic_buffer<char>::append(const char *, const char *);
template void basic_fixed_buffer<char>::grow(std::size_t); template FMT_API void internal::arg_map<format_context>::init(
template void internal::arg_map<format_context>::init(
const basic_format_args<format_context> &args); const basic_format_args<format_context> &args);
template FMT_API int internal::char_traits<char>::format_float( template FMT_API int internal::char_traits<char>::format_float(
@ -30,16 +30,22 @@ template FMT_API int internal::char_traits<char>::format_float(
template FMT_API std::string internal::vformat<char>( template FMT_API std::string internal::vformat<char>(
string_view, basic_format_args<format_context>); string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to(
internal::buffer &, string_view, basic_format_args<format_context>);
template FMT_API void internal::sprintf_format(
double, internal::buffer &, core_format_specs);
template FMT_API void internal::sprintf_format(
long double, internal::buffer &, core_format_specs);
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.
template FMT_API wchar_t internal::thousands_sep(locale_provider *); template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template void internal::basic_buffer<wchar_t>::append( template FMT_API void internal::basic_buffer<wchar_t>::append(
const wchar_t *, const wchar_t *); const wchar_t *, const wchar_t *);
template void basic_fixed_buffer<wchar_t>::grow(std::size_t); template FMT_API void internal::arg_map<wformat_context>::init(
template void internal::arg_map<wformat_context>::init(
const basic_format_args<wformat_context> &); const basic_format_args<wformat_context> &);
template FMT_API int internal::char_traits<wchar_t>::format_float( template FMT_API int internal::char_traits<wchar_t>::format_float(

View file

@ -1 +1 @@
<manifest package="fmt" /> <manifest package="net.fmtlib" />

View file

@ -1,14 +1,24 @@
// General gradle arguments for root project // General gradle arguments for root project
buildscript { buildscript {
repositories { repositories {
google() google()
jcenter() jcenter()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.0.1' //
// https://developer.android.com/studio/releases/gradle-plugin
//
// Notice that 3.1.3 here is the version of [Android Gradle Plugin]
// Accroding to URL above you will need Gradle 4.4 or higher
//
classpath 'com.android.tools.build:gradle:3.1.3'
} }
} }
repositories {
google()
jcenter()
}
// Output: Shared library (.so) for Android // Output: Shared library (.so) for Android
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
@ -24,20 +34,17 @@ android {
splits { splits {
abi { abi {
enable true enable true
// Be general, as much as possible ...
// universalApk true
// Specify platforms for Application // Specify platforms for Application
reset() reset()
include "x86", "x86_64", "armeabi-v7a", "arm64-v8a" include "arm64-v8a", "armeabi-v7a", "x86_64"
} }
} }
defaultConfig { defaultConfig {
minSdkVersion 21 // Android 5.0+ minSdkVersion 21 // Android 5.0+
targetSdkVersion 25 // Follow Compile SDK targetSdkVersion 25 // Follow Compile SDK
versionCode 16 // Follow release count versionCode 20 // Follow release count
versionName "4.1.0" // Follow Official version versionName "5.2.1" // Follow Official version
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild { externalNativeBuild {
@ -46,7 +53,7 @@ android {
arguments "-DBUILD_SHARED_LIBS=true" // Build shared object arguments "-DBUILD_SHARED_LIBS=true" // Build shared object
arguments "-DFMT_TEST=false" // Skip test arguments "-DFMT_TEST=false" // Skip test
arguments "-DFMT_DOC=false" // Skip document arguments "-DFMT_DOC=false" // Skip document
cppFlags "-std=c++14" cppFlags "-std=c++17"
} }
} }
println("Gradle CMake Plugin: ") println("Gradle CMake Plugin: ")

11
support/cmake/fmt.pc.in Normal file
View file

@ -0,0 +1,11 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: fmt
Description: A modern formatting library
Version: @FMT_VERSION@
Libs: -L${libdir} -lfmt
Cflags: -I${includedir}

10
support/rst2md.py Normal file → Executable file
View file

@ -1,6 +1,7 @@
#!/usr/bin/env python
# reStructuredText (RST) to GitHub-flavored Markdown converter # reStructuredText (RST) to GitHub-flavored Markdown converter
import re import re, sys
from docutils import core, nodes, writers from docutils import core, nodes, writers
@ -35,6 +36,9 @@ class Translator(nodes.NodeVisitor):
self.version = re.match(r'(\d+\.\d+\.\d+).*', node.children[0]).group(1) self.version = re.match(r'(\d+\.\d+\.\d+).*', node.children[0]).group(1)
raise nodes.SkipChildren raise nodes.SkipChildren
def visit_title_reference(self, node):
raise Exception(node)
def depart_title(self, node): def depart_title(self, node):
pass pass
@ -149,3 +153,7 @@ class MDWriter(writers.Writer):
def convert(rst_path): def convert(rst_path):
"""Converts RST file to Markdown.""" """Converts RST file to Markdown."""
return core.publish_file(source_path=rst_path, writer=MDWriter()) return core.publish_file(source_path=rst_path, writer=MDWriter())
if __name__ == '__main__':
convert(sys.argv[1])

View file

@ -85,10 +85,14 @@ function(add_fmt_test name)
endfunction() endfunction()
add_fmt_test(assert-test) add_fmt_test(assert-test)
add_fmt_test(chrono-test)
add_fmt_test(core-test) add_fmt_test(core-test)
add_fmt_test(gtest-extra-test) add_fmt_test(gtest-extra-test)
add_fmt_test(format-test mock-allocator.h) add_fmt_test(format-test mock-allocator.h)
if (NOT (MSVC AND BUILD_SHARED_LIBS))
add_fmt_test(format-impl-test) add_fmt_test(format-impl-test)
endif ()
add_fmt_test(locale-test)
add_fmt_test(ostream-test) add_fmt_test(ostream-test)
add_fmt_test(printf-test) add_fmt_test(printf-test)
add_fmt_test(time-test) add_fmt_test(time-test)
@ -165,8 +169,11 @@ if (FMT_PEDANTIC)
"-DCXX_STANDARD_FLAG=${CXX_STANDARD_FLAG}" "-DCXX_STANDARD_FLAG=${CXX_STANDARD_FLAG}"
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}" "-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}") "-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}")
endif ()
# test if the targets are findable from the build directory # These tests are disabled on Windows because they take too long.
if (FMT_PEDANTIC AND NOT WIN32)
# Test if the targets are found from the build directory.
add_test(find-package-test ${CMAKE_CTEST_COMMAND} add_test(find-package-test ${CMAKE_CTEST_COMMAND}
-C ${CMAKE_BUILD_TYPE} -C ${CMAKE_BUILD_TYPE}
--build-and-test --build-and-test
@ -181,7 +188,7 @@ if (FMT_PEDANTIC)
"-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}" "-DPEDANTIC_COMPILE_FLAGS=${PEDANTIC_COMPILE_FLAGS}"
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
# test if the targets are findable when add_subdirectory is used # Test if the targets are found when add_subdirectory is used.
add_test(add-subdirectory-test ${CMAKE_CTEST_COMMAND} add_test(add-subdirectory-test ${CMAKE_CTEST_COMMAND}
-C ${CMAKE_BUILD_TYPE} -C ${CMAKE_BUILD_TYPE}
--build-and-test --build-and-test

185
test/chrono-test.cc Normal file
View file

@ -0,0 +1,185 @@
// Formatting library for C++ - time formatting tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/chrono.h"
#include "gtest-extra.h"
#include <iomanip>
std::tm make_tm() {
auto time = std::tm();
time.tm_mday = 1;
return time;
}
std::tm make_hour(int h) {
auto time = make_tm();
time.tm_hour = h;
return time;
}
std::tm make_minute(int m) {
auto time = make_tm();
time.tm_min = m;
return time;
}
std::tm make_second(int s) {
auto time = make_tm();
time.tm_sec = s;
return time;
}
std::string format_tm(const std::tm &time, const char *spec,
const std::locale &loc) {
auto &facet = std::use_facet<std::time_put<char>>(loc);
std::ostringstream os;
os.imbue(loc);
facet.put(os, os, ' ', &time, spec, spec + std::strlen(spec));
return os.str();
}
#define EXPECT_TIME(spec, time, duration) { \
std::locale loc("ja_JP.utf8"); \
EXPECT_EQ(format_tm(time, spec, loc), \
fmt::format(loc, "{:" spec "}", duration)); \
}
TEST(ChronoTest, FormatDefault) {
EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42)));
EXPECT_EQ("42as",
fmt::format("{}", std::chrono::duration<int, std::atto>(42)));
EXPECT_EQ("42fs",
fmt::format("{}", std::chrono::duration<int, std::femto>(42)));
EXPECT_EQ("42ps",
fmt::format("{}", std::chrono::duration<int, std::pico>(42)));
EXPECT_EQ("42ns", fmt::format("{}", std::chrono::nanoseconds(42)));
EXPECT_EQ("42µs", fmt::format("{}", std::chrono::microseconds(42)));
EXPECT_EQ("42ms", fmt::format("{}", std::chrono::milliseconds(42)));
EXPECT_EQ("42cs",
fmt::format("{}", std::chrono::duration<int, std::centi>(42)));
EXPECT_EQ("42ds",
fmt::format("{}", std::chrono::duration<int, std::deci>(42)));
EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42)));
EXPECT_EQ("42das",
fmt::format("{}", std::chrono::duration<int, std::deca>(42)));
EXPECT_EQ("42hs",
fmt::format("{}", std::chrono::duration<int, std::hecto>(42)));
EXPECT_EQ("42ks",
fmt::format("{}", std::chrono::duration<int, std::kilo>(42)));
EXPECT_EQ("42Ms",
fmt::format("{}", std::chrono::duration<int, std::mega>(42)));
EXPECT_EQ("42Gs",
fmt::format("{}", std::chrono::duration<int, std::giga>(42)));
EXPECT_EQ("42Ts",
fmt::format("{}", std::chrono::duration<int, std::tera>(42)));
EXPECT_EQ("42Ps",
fmt::format("{}", std::chrono::duration<int, std::peta>(42)));
EXPECT_EQ("42Es",
fmt::format("{}", std::chrono::duration<int, std::exa>(42)));
EXPECT_EQ("42m", fmt::format("{}", std::chrono::minutes(42)));
EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42)));
EXPECT_EQ("42[15]s",
fmt::format("{}",
std::chrono::duration<int, std::ratio<15, 1>>(42)));
EXPECT_EQ("42[15/4]s",
fmt::format("{}",
std::chrono::duration<int, std::ratio<15, 4>>(42)));
}
TEST(ChronoTest, Align) {
auto s = std::chrono::seconds(42);
EXPECT_EQ("42s ", fmt::format("{:5}", s));
EXPECT_EQ("42s ", fmt::format("{:{}}", s, 5));
EXPECT_EQ(" 42s", fmt::format("{:>5}", s));
EXPECT_EQ("**42s**", fmt::format("{:*^7}", s));
EXPECT_EQ("03:25:45 ",
fmt::format("{:12%H:%M:%S}", std::chrono::seconds(12345)));
EXPECT_EQ(" 03:25:45",
fmt::format("{:>12%H:%M:%S}", std::chrono::seconds(12345)));
EXPECT_EQ("~~03:25:45~~",
fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345)));
}
TEST(ChronoTest, FormatSpecs) {
EXPECT_EQ("%", fmt::format("{:%%}", std::chrono::seconds(0)));
EXPECT_EQ("\n", fmt::format("{:%n}", std::chrono::seconds(0)));
EXPECT_EQ("\t", fmt::format("{:%t}", std::chrono::seconds(0)));
EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(0)));
EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(60)));
EXPECT_EQ("42", fmt::format("{:%S}", std::chrono::seconds(42)));
EXPECT_EQ("01.234", fmt::format("{:%S}", std::chrono::milliseconds(1234)));
EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(0)));
EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(60)));
EXPECT_EQ("42", fmt::format("{:%M}", std::chrono::minutes(42)));
EXPECT_EQ("01", fmt::format("{:%M}", std::chrono::seconds(61)));
EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(0)));
EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(24)));
EXPECT_EQ("14", fmt::format("{:%H}", std::chrono::hours(14)));
EXPECT_EQ("01", fmt::format("{:%H}", std::chrono::minutes(61)));
EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(0)));
EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(12)));
EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(24)));
EXPECT_EQ("04", fmt::format("{:%I}", std::chrono::hours(4)));
EXPECT_EQ("02", fmt::format("{:%I}", std::chrono::hours(14)));
EXPECT_EQ("03:25:45",
fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345)));
EXPECT_EQ("03:25", fmt::format("{:%R}", std::chrono::seconds(12345)));
EXPECT_EQ("03:25:45", fmt::format("{:%T}", std::chrono::seconds(12345)));
}
TEST(ChronoTest, InvalidSpecs) {
auto sec = std::chrono::seconds(0);
EXPECT_THROW_MSG(fmt::format("{:%a}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%A}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%c}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%x}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%Ex}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%X}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%EX}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%D}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%F}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%Ec}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%w}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%u}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%b}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%B}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%z}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%Z}", sec), fmt::format_error, "no date");
EXPECT_THROW_MSG(fmt::format("{:%q}", sec), fmt::format_error,
"invalid format");
EXPECT_THROW_MSG(fmt::format("{:%Eq}", sec), fmt::format_error,
"invalid format");
EXPECT_THROW_MSG(fmt::format("{:%Oq}", sec), fmt::format_error,
"invalid format");
}
TEST(ChronoTest, Locale) {
const char *loc_name = "ja_JP.utf8";
bool has_locale = false;
std::locale loc;
try {
loc = std::locale(loc_name);
has_locale = true;
} catch (const std::runtime_error &) {}
if (!has_locale) {
fmt::print("{} locale is missing.\n", loc_name);
return;
}
EXPECT_TIME("%OH", make_hour(14), std::chrono::hours(14));
EXPECT_TIME("%OI", make_hour(14), std::chrono::hours(14));
EXPECT_TIME("%OM", make_minute(42), std::chrono::minutes(42));
EXPECT_TIME("%OS", make_second(42), std::chrono::seconds(42));
auto time = make_tm();
time.tm_hour = 3;
time.tm_min = 25;
time.tm_sec = 45;
auto sec = std::chrono::seconds(12345);
EXPECT_TIME("%r", time, sec);
EXPECT_TIME("%p", time, sec);
}

View file

@ -13,6 +13,7 @@
#include <limits> #include <limits>
#include <string> #include <string>
#include <type_traits> #include <type_traits>
#include <memory>
#include "test-assert.h" #include "test-assert.h"
@ -64,6 +65,7 @@ struct formatter<test_struct, Char> {
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 470
TEST(BufferTest, Noncopyable) { TEST(BufferTest, Noncopyable) {
EXPECT_FALSE(std::is_copy_constructible<basic_buffer<char> >::value); EXPECT_FALSE(std::is_copy_constructible<basic_buffer<char> >::value);
#if !FMT_MSC_VER #if !FMT_MSC_VER
@ -79,11 +81,12 @@ TEST(BufferTest, Nonmoveable) {
EXPECT_FALSE(std::is_move_assignable<basic_buffer<char> >::value); EXPECT_FALSE(std::is_move_assignable<basic_buffer<char> >::value);
#endif #endif
} }
#endif
// A test buffer with a dummy grow method. // A test buffer with a dummy grow method.
template <typename T> template <typename T>
struct test_buffer : basic_buffer<T> { struct test_buffer : basic_buffer<T> {
void grow(std::size_t capacity) { this->set(nullptr, capacity); } void grow(std::size_t capacity) { this->set(FMT_NULL, capacity); }
}; };
template <typename T> template <typename T>
@ -103,7 +106,7 @@ struct mock_buffer : basic_buffer<T> {
TEST(BufferTest, Ctor) { TEST(BufferTest, Ctor) {
{ {
mock_buffer<int> buffer; mock_buffer<int> buffer;
EXPECT_EQ(nullptr, &buffer[0]); EXPECT_EQ(FMT_NULL, &buffer[0]);
EXPECT_EQ(static_cast<size_t>(0), buffer.size()); EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity()); EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
} }
@ -218,14 +221,16 @@ struct custom_context {
const char *format(const T &, custom_context& ctx) { const char *format(const T &, custom_context& ctx) {
ctx.called = true; ctx.called = true;
return nullptr; return FMT_NULL;
} }
}; };
}; };
bool called; bool called;
fmt::parse_context parse_context() { return fmt::parse_context(""); } fmt::format_parse_context parse_context() {
return fmt::format_parse_context("");
}
void advance_to(const char *) {} void advance_to(const char *) {}
}; };
@ -364,8 +369,8 @@ TEST(ArgTest, WStringArg) {
} }
TEST(ArgTest, PointerArg) { TEST(ArgTest, PointerArg) {
void *p = nullptr; void *p = FMT_NULL;
const void *cp = nullptr; const void *cp = FMT_NULL;
CHECK_ARG_(char, cp, p); CHECK_ARG_(char, cp, p);
CHECK_ARG_(wchar_t, cp, p); CHECK_ARG_(wchar_t, cp, p);
CHECK_ARG(cp, ); CHECK_ARG(cp, );
@ -376,7 +381,7 @@ struct check_custom {
fmt::basic_format_arg<fmt::format_context>::handle h) const { fmt::basic_format_arg<fmt::format_context>::handle h) const {
struct test_buffer : fmt::internal::basic_buffer<char> { struct test_buffer : fmt::internal::basic_buffer<char> {
char data[10]; char data[10];
test_buffer() : basic_buffer(data, 0, 10) {} test_buffer() : fmt::internal::basic_buffer<char>(data, 0, 10) {}
void grow(std::size_t) {} void grow(std::size_t) {}
} buffer; } buffer;
fmt::internal::basic_buffer<char> &base = buffer; fmt::internal::basic_buffer<char> &base = buffer;
@ -451,6 +456,76 @@ TEST(CoreTest, IsEnumConvertibleToInt) {
EXPECT_TRUE((fmt::convert_to_int<enum_with_underlying_type, char>::value)); EXPECT_TRUE((fmt::convert_to_int<enum_with_underlying_type, char>::value));
} }
namespace my_ns {
template <typename Char>
class my_string {
public:
my_string(const Char *s) : s_(s) {}
const Char * data() const FMT_NOEXCEPT { return s_.data(); }
std::size_t length() const FMT_NOEXCEPT { return s_.size(); }
operator const Char*() const { return s_.c_str(); }
private:
std::basic_string<Char> s_;
};
template <typename Char>
inline fmt::basic_string_view<Char>
to_string_view(const my_string<Char> &s) FMT_NOEXCEPT {
return { s.data(), s.length() };
}
struct non_string {};
}
namespace FakeQt {
class QString {
public:
QString(const wchar_t *s) : s_(std::make_shared<std::wstring>(s)) {}
const wchar_t *utf16() const FMT_NOEXCEPT { return s_->data(); }
int size() const FMT_NOEXCEPT { return static_cast<int>(s_->size()); }
#ifdef FMT_STRING_VIEW
operator FMT_STRING_VIEW<wchar_t>() const FMT_NOEXCEPT { return *s_; }
#endif
private:
std::shared_ptr<std::wstring> s_;
};
inline fmt::basic_string_view<wchar_t> to_string_view(
const QString &s) FMT_NOEXCEPT {
return {s.utf16(),
static_cast<std::size_t>(s.size())};
}
}
template <typename T>
class IsStringTest : public testing::Test {};
typedef ::testing::Types<char, wchar_t, char16_t, char32_t> StringCharTypes;
TYPED_TEST_CASE(IsStringTest, StringCharTypes);
namespace {
template <typename Char>
struct derived_from_string_view : fmt::basic_string_view<Char> {};
}
TYPED_TEST(IsStringTest, IsString) {
EXPECT_TRUE((fmt::internal::is_string<TypeParam *>::value));
EXPECT_TRUE((fmt::internal::is_string<const TypeParam *>::value));
EXPECT_TRUE((fmt::internal::is_string<TypeParam[2]>::value));
EXPECT_TRUE((fmt::internal::is_string<const TypeParam[2]>::value));
EXPECT_TRUE((fmt::internal::is_string<std::basic_string<TypeParam>>::value));
EXPECT_TRUE(
(fmt::internal::is_string<fmt::basic_string_view<TypeParam>>::value));
EXPECT_TRUE(
(fmt::internal::is_string<derived_from_string_view<TypeParam>>::value));
#ifdef FMT_STRING_VIEW
EXPECT_TRUE((fmt::internal::is_string<FMT_STRING_VIEW<TypeParam>>::value));
#endif
EXPECT_TRUE((fmt::internal::is_string<my_ns::my_string<TypeParam>>::value));
EXPECT_FALSE((fmt::internal::is_string<my_ns::non_string>::value));
EXPECT_TRUE((fmt::internal::is_string<FakeQt::QString>::value));
}
TEST(CoreTest, Format) { TEST(CoreTest, Format) {
// This should work without including fmt/format.h. // This should work without including fmt/format.h.
#ifdef FMT_FORMAT_H_ #ifdef FMT_FORMAT_H_
@ -458,3 +533,81 @@ TEST(CoreTest, Format) {
#endif #endif
EXPECT_EQ(fmt::format("{}", 42), "42"); EXPECT_EQ(fmt::format("{}", 42), "42");
} }
TEST(CoreTest, FormatTo) {
// This should work without including fmt/format.h.
#ifdef FMT_FORMAT_H_
# error fmt/format.h must not be included in the core test
#endif
std::string s;
fmt::format_to(std::back_inserter(s), "{}", 42);
EXPECT_EQ(s, "42");
}
TEST(CoreTest, ToStringViewForeignStrings) {
using namespace my_ns;
using namespace FakeQt;
EXPECT_EQ(to_string_view(my_string<char>("42")), "42");
EXPECT_EQ(to_string_view(my_string<wchar_t>(L"42")), L"42");
EXPECT_EQ(to_string_view(QString(L"42")), L"42");
fmt::internal::type type =
fmt::internal::get_type<fmt::format_context, my_string<char>>::value;
EXPECT_EQ(type, fmt::internal::string_type);
type =
fmt::internal::get_type<fmt::wformat_context, my_string<wchar_t>>::value;
EXPECT_EQ(type, fmt::internal::string_type);
type = fmt::internal::get_type<fmt::wformat_context, QString>::value;
EXPECT_EQ(type, fmt::internal::string_type);
// Does not compile: only wide format contexts are compatible with QString!
// type = fmt::internal::get_type<fmt::format_context, QString>::value;
}
TEST(CoreTest, FormatForeignStrings) {
using namespace my_ns;
using namespace FakeQt;
EXPECT_EQ(fmt::format(my_string<char>("{}"), 42), "42");
EXPECT_EQ(fmt::format(my_string<wchar_t>(L"{}"), 42), L"42");
EXPECT_EQ(fmt::format(QString(L"{}"), 42), L"42");
EXPECT_EQ(fmt::format(QString(L"{}"), my_string<wchar_t>(L"42")), L"42");
EXPECT_EQ(fmt::format(my_string<wchar_t>(L"{}"), QString(L"42")), L"42");
}
struct implicitly_convertible_to_string_view {
operator fmt::string_view() const { return "foo"; }
};
TEST(FormatterTest, FormatImplicitlyConvertibleToStringView) {
EXPECT_EQ("foo", fmt::format("{}", implicitly_convertible_to_string_view()));
}
// std::is_constructible is broken in MSVC until version 2015.
#if FMT_USE_EXPLICIT && (!FMT_MSC_VER || FMT_MSC_VER >= 1900)
struct explicitly_convertible_to_string_view {
explicit operator fmt::string_view() const { return "foo"; }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringView) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_view()));
}
struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
EXPECT_EQ(L"foo",
fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
}
struct explicitly_convertible_to_string_like {
template <
typename String,
typename = typename std::enable_if<
std::is_constructible<String, const char*, std::size_t>::value>::type>
FMT_EXPLICIT operator String() const { return String("foo", 3u); }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#endif

View file

@ -26,8 +26,8 @@ class custom_arg_formatter :
using base::operator(); using base::operator();
iterator operator()(double value) { iterator operator()(double value) {
// Comparing a float to 0.0 is safe // Comparing a float to 0.0 is safe.
if (round(value * pow(10, spec()->precision())) == 0.0) if (round(value * pow(10, spec()->precision)) == 0.0)
value = 0; value = 0;
return base::operator()(value); return base::operator()(value);
} }

View file

@ -24,6 +24,12 @@
#undef min #undef min
#undef max #undef max
#if FMT_HAS_CPP_ATTRIBUTE(noreturn)
# define FMT_NORETURN [[noreturn]]
#else
# define FMT_NORETURN
#endif
using fmt::internal::fp; using fmt::internal::fp;
template <bool is_iec559> template <bool is_iec559>
@ -96,6 +102,11 @@ TEST(FPTest, GetCachedPower) {
} }
} }
TEST(FPTest, Grisu2FormatCompilesWithNonIEEEDouble) {
fmt::memory_buffer buf;
grisu2_format(4.2f, buf, fmt::core_format_specs());
}
template <typename T> template <typename T>
struct ValueExtractor: fmt::internal::function<T> { struct ValueExtractor: fmt::internal::function<T> {
T operator()(T value) { T operator()(T value) {
@ -118,16 +129,17 @@ TEST(FormatTest, ArgConverter) {
TEST(FormatTest, FormatNegativeNaN) { TEST(FormatTest, FormatNegativeNaN) {
double nan = std::numeric_limits<double>::quiet_NaN(); double nan = std::numeric_limits<double>::quiet_NaN();
if (fmt::internal::fputil::isnegative(-nan)) if (std::signbit(-nan))
EXPECT_EQ("-nan", fmt::format("{}", -nan)); EXPECT_EQ("-nan", fmt::format("{}", -nan));
else else
fmt::print("Warning: compiler doesn't handle negative NaN correctly"); fmt::print("Warning: compiler doesn't handle negative NaN correctly");
} }
TEST(FormatTest, StrError) { TEST(FormatTest, StrError) {
char *message = nullptr; char *message = FMT_NULL;
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = nullptr, 0), "invalid buffer"); EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = FMT_NULL, 0),
"invalid buffer");
EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = buffer, 0), EXPECT_ASSERT(fmt::safe_strerror(EDOM, message = buffer, 0),
"invalid buffer"); "invalid buffer");
buffer[0] = 'x'; buffer[0] = 'x';
@ -140,7 +152,7 @@ TEST(FormatTest, StrError) {
#endif #endif
int result = fmt::safe_strerror(error_code, message = buffer, BUFFER_SIZE); int result = fmt::safe_strerror(error_code, message = buffer, BUFFER_SIZE);
EXPECT_EQ(0, result); EXPECT_EQ(result, 0);
std::size_t message_size = std::strlen(message); std::size_t message_size = std::strlen(message);
EXPECT_GE(BUFFER_SIZE - 1u, message_size); EXPECT_GE(BUFFER_SIZE - 1u, message_size);
EXPECT_EQ(get_system_error(error_code), message); EXPECT_EQ(get_system_error(error_code), message);
@ -195,8 +207,40 @@ TEST(FormatTest, CountCodePoints) {
} }
TEST(ColorsTest, Colors) { TEST(ColorsTest, Colors) {
EXPECT_WRITE(stdout, fmt::print(fmt::rgb(255,20,30), "rgb(255,20,30)"), EXPECT_WRITE(stdout, fmt::print(fg(fmt::rgb(255, 20, 30)), "rgb(255,20,30)"),
"\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m"); "\x1b[38;2;255;020;030mrgb(255,20,30)\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(fmt::color::blue, "blue"), EXPECT_WRITE(stdout, fmt::print(fg(fmt::color::blue), "blue"),
"\x1b[38;2;000;000;255mblue\x1b[0m"); "\x1b[38;2;000;000;255mblue\x1b[0m");
EXPECT_WRITE(
stdout,
fmt::print(fg(fmt::color::blue) | bg(fmt::color::red), "two color"),
"\x1b[38;2;000;000;255m\x1b[48;2;255;000;000mtwo color\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::bold, "bold"),
"\x1b[1mbold\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::italic, "italic"),
"\x1b[3mitalic\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(fmt::emphasis::underline, "underline"),
"\x1b[4munderline\x1b[0m");
EXPECT_WRITE(stdout,
fmt::print(fmt::emphasis::strikethrough, "strikethrough"),
"\x1b[9mstrikethrough\x1b[0m");
EXPECT_WRITE(
stdout,
fmt::print(fg(fmt::color::blue) | fmt::emphasis::bold, "blue/bold"),
"\x1b[1m\x1b[38;2;000;000;255mblue/bold\x1b[0m");
EXPECT_WRITE(stderr, fmt::print(stderr, fmt::emphasis::bold, "bold error"),
"\x1b[1mbold error\x1b[0m");
EXPECT_WRITE(stderr, fmt::print(stderr, fg(fmt::color::blue), "blue log"),
"\x1b[38;2;000;000;255mblue log\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(fmt::text_style(), "hi"), "hi");
EXPECT_WRITE(stdout, fmt::print(fg(fmt::terminal_color::red), "tred"),
"\x1b[31mtred\x1b[0m");
EXPECT_WRITE(stdout, fmt::print(bg(fmt::terminal_color::cyan), "tcyan"),
"\x1b[46mtcyan\x1b[0m");
EXPECT_WRITE(stdout,
fmt::print(fg(fmt::terminal_color::bright_green), "tbgreen"),
"\x1b[92mtbgreen\x1b[0m");
EXPECT_WRITE(stdout,
fmt::print(bg(fmt::terminal_color::bright_magenta), "tbmagenta"),
"\x1b[105mtbmagenta\x1b[0m");
} }

View file

@ -12,6 +12,7 @@
#include <cstring> #include <cstring>
#include <list> #include <list>
#include <memory> #include <memory>
#include <string>
#include <stdint.h> #include <stdint.h>
// Check if fmt/format.h compiles with windows.h included before it. // Check if fmt/format.h compiles with windows.h included before it.
@ -44,6 +45,38 @@ using testing::StrictMock;
namespace { namespace {
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 408
template <typename Char, typename T>
bool check_enabled_formatter() {
static_assert(
std::is_default_constructible<fmt::formatter<T, Char>>::value, "");
return true;
}
template <typename Char, typename... T>
void check_enabled_formatters() {
auto dummy = {check_enabled_formatter<Char, T>()...};
(void)dummy;
}
TEST(FormatterTest, TestFormattersEnabled) {
check_enabled_formatters<char,
bool, char, signed char, unsigned char, short, unsigned short,
int, unsigned, long, unsigned long, long long, unsigned long long,
float, double, long double, void*, const void*,
char*, const char*, std::string>();
check_enabled_formatters<wchar_t,
bool, wchar_t, signed char, unsigned char, short, unsigned short,
int, unsigned, long, unsigned long, long long, unsigned long long,
float, double, long double, void*, const void*,
wchar_t*, const wchar_t*, std::wstring>();
#if FMT_USE_NULLPTR
check_enabled_formatters<char, std::nullptr_t>();
check_enabled_formatters<wchar_t, std::nullptr_t>();
#endif
}
#endif
// Format value using the standard library. // Format value using the standard library.
template <typename Char, typename T> template <typename Char, typename T>
void std_format(const T &value, std::basic_string<Char> &result) { void std_format(const T &value, std::basic_string<Char> &result) {
@ -160,13 +193,16 @@ TEST(UtilTest, ParseNonnegativeInt) {
fmt::print("Skipping parse_nonnegative_int test\n"); fmt::print("Skipping parse_nonnegative_int test\n");
return; return;
} }
const char *s = "10000000000"; fmt::string_view s = "10000000000";
auto begin = s.begin(), end = s.end();
EXPECT_THROW_MSG( EXPECT_THROW_MSG(
parse_nonnegative_int(s, fmt::internal::error_handler()), parse_nonnegative_int(begin, end, fmt::internal::error_handler()),
fmt::format_error, "number is too big"); fmt::format_error, "number is too big");
s = "2147483649"; s = "2147483649";
begin = s.begin();
end = s.end();
EXPECT_THROW_MSG( EXPECT_THROW_MSG(
parse_nonnegative_int(s, fmt::internal::error_handler()), parse_nonnegative_int(begin, end, fmt::internal::error_handler()),
fmt::format_error, "number is too big"); fmt::format_error, "number is too big");
} }
@ -185,6 +221,35 @@ TEST(IteratorTest, TruncatingIterator) {
EXPECT_EQ(it.base(), p + 1); EXPECT_EQ(it.base(), p + 1);
} }
TEST(IteratorTest, TruncatingBackInserter) {
std::string buffer;
auto bi = std::back_inserter(buffer);
fmt::internal::truncating_iterator<decltype(bi)> it(bi, 2);
*it++ = '4';
*it++ = '2';
*it++ = '1';
EXPECT_EQ(buffer.size(), 2);
EXPECT_EQ(buffer, "42");
}
TEST(IteratorTest, IsOutputIterator) {
EXPECT_TRUE(fmt::internal::is_output_iterator<char*>::value);
EXPECT_FALSE(fmt::internal::is_output_iterator<const char*>::value);
EXPECT_FALSE(fmt::internal::is_output_iterator<std::string>::value);
EXPECT_TRUE(fmt::internal::is_output_iterator<
std::back_insert_iterator<std::string>>::value);
EXPECT_TRUE(fmt::internal::is_output_iterator<
std::string::iterator>::value);
EXPECT_FALSE(fmt::internal::is_output_iterator<
std::string::const_iterator>::value);
EXPECT_FALSE(fmt::internal::is_output_iterator<std::list<char>>::value);
EXPECT_TRUE(fmt::internal::is_output_iterator<
std::list<char>::iterator>::value);
EXPECT_FALSE(fmt::internal::is_output_iterator<
std::list<char>::const_iterator>::value);
EXPECT_FALSE(fmt::internal::is_output_iterator<uint32_pair>::value);
}
TEST(MemoryBufferTest, Ctor) { TEST(MemoryBufferTest, Ctor) {
basic_memory_buffer<char, 123> buffer; basic_memory_buffer<char, 123> buffer;
EXPECT_EQ(static_cast<size_t>(0), buffer.size()); EXPECT_EQ(static_cast<size_t>(0), buffer.size());
@ -212,7 +277,7 @@ TEST(AllocatorTest, allocator_ref) {
test_allocator_ref ref2(ref); test_allocator_ref ref2(ref);
check_forwarding(alloc, ref2); check_forwarding(alloc, ref2);
test_allocator_ref ref3; test_allocator_ref ref3;
EXPECT_EQ(nullptr, ref3.get()); EXPECT_EQ(FMT_NULL, ref3.get());
ref3 = ref; ref3 = ref;
check_forwarding(alloc, ref3); check_forwarding(alloc, ref3);
} }
@ -228,7 +293,7 @@ static void check_move_buffer(const char *str,
EXPECT_EQ(str, std::string(&buffer2[0], buffer2.size())); EXPECT_EQ(str, std::string(&buffer2[0], buffer2.size()));
EXPECT_EQ(5u, buffer2.capacity()); EXPECT_EQ(5u, buffer2.capacity());
// Move should transfer allocator. // Move should transfer allocator.
EXPECT_EQ(nullptr, buffer.get_allocator().get()); EXPECT_EQ(FMT_NULL, buffer.get_allocator().get());
EXPECT_EQ(alloc, buffer2.get_allocator().get()); EXPECT_EQ(alloc, buffer2.get_allocator().get());
} }
@ -313,7 +378,7 @@ TEST(MemoryBufferTest, Grow) {
TEST(MemoryBufferTest, Allocator) { TEST(MemoryBufferTest, Allocator) {
typedef allocator_ref< mock_allocator<char> > TestAllocator; typedef allocator_ref< mock_allocator<char> > TestAllocator;
basic_memory_buffer<char, 10, TestAllocator> buffer; basic_memory_buffer<char, 10, TestAllocator> buffer;
EXPECT_EQ(nullptr, buffer.get_allocator().get()); EXPECT_EQ(FMT_NULL, buffer.get_allocator().get());
StrictMock< mock_allocator<char> > alloc; StrictMock< mock_allocator<char> > alloc;
char mem; char mem;
{ {
@ -351,29 +416,6 @@ TEST(MemoryBufferTest, ExceptionInDeallocate) {
EXPECT_CALL(alloc, deallocate(&mem2[0], 2 * size)); EXPECT_CALL(alloc, deallocate(&mem2[0], 2 * size));
} }
TEST(FixedBufferTest, Ctor) {
char array[10] = "garbage";
fmt::basic_fixed_buffer<char> buffer(array, sizeof(array));
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(10u, buffer.capacity());
EXPECT_EQ(array, buffer.data());
}
TEST(FixedBufferTest, CompileTimeSizeCtor) {
char array[10] = "garbage";
fmt::basic_fixed_buffer<char> buffer(array);
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(10u, buffer.capacity());
EXPECT_EQ(array, buffer.data());
}
TEST(FixedBufferTest, BufferOverflow) {
char array[10];
fmt::basic_fixed_buffer<char> buffer(array);
buffer.resize(10);
EXPECT_THROW_MSG(buffer.resize(11), std::runtime_error, "buffer overflow");
}
#ifdef _WIN32 #ifdef _WIN32
TEST(UtilTest, UTF16ToUTF8) { TEST(UtilTest, UTF16ToUTF8) {
std::string s = "ёжик"; std::string s = "ёжик";
@ -476,7 +518,7 @@ TEST(UtilTest, FormatSystemError) {
fmt::print("warning: std::allocator allocates {} chars", max_size); fmt::print("warning: std::allocator allocates {} chars", max_size);
return; return;
} }
fmt::format_system_error(message, EDOM, fmt::string_view(nullptr, max_size)); fmt::format_system_error(message, EDOM, fmt::string_view(FMT_NULL, max_size));
EXPECT_EQ(fmt::format("error {}", EDOM), to_string(message)); EXPECT_EQ(fmt::format("error {}", EDOM), to_string(message));
} }
@ -564,17 +606,6 @@ TEST(StringViewTest, Ctor) {
EXPECT_EQ(4u, string_view(std::string("defg")).size()); EXPECT_EQ(4u, string_view(std::string("defg")).size());
} }
// GCC 4.6 doesn't have std::is_copy_*.
#if FMT_GCC_VERSION && FMT_GCC_VERSION >= 407
TEST(WriterTest, NotCopyConstructible) {
EXPECT_FALSE(std::is_copy_constructible<fmt::writer>::value);
}
TEST(WriterTest, NotCopyAssignable) {
EXPECT_FALSE(std::is_copy_assignable<fmt::writer>::value);
}
#endif
TEST(WriterTest, Data) { TEST(WriterTest, Data) {
memory_buffer buf; memory_buffer buf;
fmt::writer w(buf); fmt::writer w(buf);
@ -682,9 +713,15 @@ TEST(FormatToTest, Format) {
EXPECT_EQ("part1part2", s); EXPECT_EQ("part1part2", s);
} }
TEST(FormatToTest, WideString) {
std::vector<wchar_t> buf;
fmt::format_to(std::back_inserter(buf), L"{}{}", 42, L'\0');
EXPECT_STREQ(buf.data(), L"42");
}
TEST(FormatToTest, FormatToNonbackInsertIteratorWithSignAndNumericAlignment) { TEST(FormatToTest, FormatToNonbackInsertIteratorWithSignAndNumericAlignment) {
char buffer[16] = {}; char buffer[16] = {};
fmt::format_to(buffer, "{: =+}", 42.0); fmt::format_to(fmt::internal::make_checked(buffer, 16), "{: =+}", 42.0);
EXPECT_STREQ("+42", buffer); EXPECT_STREQ("+42", buffer);
} }
@ -737,6 +774,7 @@ TEST(FormatterTest, ArgErrors) {
EXPECT_THROW_MSG(format("{?}"), format_error, "invalid format string"); EXPECT_THROW_MSG(format("{?}"), format_error, "invalid format string");
EXPECT_THROW_MSG(format("{0"), format_error, "invalid format string"); EXPECT_THROW_MSG(format("{0"), format_error, "invalid format string");
EXPECT_THROW_MSG(format("{0}"), format_error, "argument index out of range"); EXPECT_THROW_MSG(format("{0}"), format_error, "argument index out of range");
EXPECT_THROW_MSG(format("{00}", 42), format_error, "invalid format string");
char format_str[BUFFER_SIZE]; char format_str[BUFFER_SIZE];
safe_sprintf(format_str, "{%u", INT_MAX); safe_sprintf(format_str, "{%u", INT_MAX);
@ -906,6 +944,7 @@ TEST(FormatterTest, Fill) {
EXPECT_EQ("abc**", format("{0:*<5}", "abc")); EXPECT_EQ("abc**", format("{0:*<5}", "abc"));
EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface))); EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface)));
EXPECT_EQ("foo=", format("{:}=", "foo")); EXPECT_EQ("foo=", format("{:}=", "foo"));
EXPECT_EQ(std::string("\0\0\0*", 4), format(string_view("{:\0>4}", 6), '*'));
} }
TEST(FormatterTest, PlusSign) { TEST(FormatterTest, PlusSign) {
@ -1013,7 +1052,7 @@ TEST(FormatterTest, HashFlag) {
EXPECT_EQ("0x42", format("{0:#x}", 0x42ull)); EXPECT_EQ("0x42", format("{0:#x}", 0x42ull));
EXPECT_EQ("042", format("{0:#o}", 042ull)); EXPECT_EQ("042", format("{0:#o}", 042ull));
if (fmt::internal::use_grisu()) if (FMT_USE_GRISU)
EXPECT_EQ("-42.0", format("{0:#}", -42.0)); EXPECT_EQ("-42.0", format("{0:#}", -42.0));
else else
EXPECT_EQ("-42.0000", format("{0:#}", -42.0)); EXPECT_EQ("-42.0000", format("{0:#}", -42.0));
@ -1395,6 +1434,8 @@ TEST(FormatterTest, FormatIntLocale) {
EXPECT_EQ("123", format("{:n}", 123)); EXPECT_EQ("123", format("{:n}", 123));
EXPECT_EQ("1,234", format("{:n}", 1234)); EXPECT_EQ("1,234", format("{:n}", 1234));
EXPECT_EQ("1,234,567", format("{:n}", 1234567)); EXPECT_EQ("1,234,567", format("{:n}", 1234567));
EXPECT_EQ("4,294,967,295",
format("{:n}", std::numeric_limits<uint32_t>::max()));
} }
struct ConvertibleToLongLong { struct ConvertibleToLongLong {
@ -1411,13 +1452,14 @@ TEST(FormatterTest, FormatFloat) {
TEST(FormatterTest, FormatDouble) { TEST(FormatterTest, FormatDouble) {
check_unknown_types(1.2, "eEfFgGaA", "double"); check_unknown_types(1.2, "eEfFgGaA", "double");
EXPECT_EQ("0", format("{0:}", 0.0)); EXPECT_EQ("0", format("{:}", 0.0));
EXPECT_EQ("0.000000", format("{0:f}", 0.0)); EXPECT_EQ("0.000000", format("{:f}", 0.0));
EXPECT_EQ("392.65", format("{0:}", 392.65)); EXPECT_EQ("0", format("{:g}", 0.0));
EXPECT_EQ("392.65", format("{0:g}", 392.65)); EXPECT_EQ("392.65", format("{:}", 392.65));
EXPECT_EQ("392.65", format("{0:G}", 392.65)); EXPECT_EQ("392.65", format("{:g}", 392.65));
EXPECT_EQ("392.650000", format("{0:f}", 392.65)); EXPECT_EQ("392.65", format("{:G}", 392.65));
EXPECT_EQ("392.650000", format("{0:F}", 392.65)); EXPECT_EQ("392.650000", format("{:f}", 392.65));
EXPECT_EQ("392.650000", format("{:F}", 392.65));
char buffer[BUFFER_SIZE]; char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "%e", 392.65); safe_sprintf(buffer, "%e", 392.65);
EXPECT_EQ(buffer, format("{0:e}", 392.65)); EXPECT_EQ(buffer, format("{0:e}", 392.65));
@ -1430,6 +1472,12 @@ TEST(FormatterTest, FormatDouble) {
EXPECT_EQ(buffer, format("{:A}", -42.0)); EXPECT_EQ(buffer, format("{:A}", -42.0));
} }
TEST(FormatterTest, FormatDoubleBigPrecision) {
// sprintf with big precision is broken in MSVC2013, so only test on Grisu.
if (FMT_USE_GRISU)
EXPECT_EQ(format("0.{:0<1000}", ""), format("{:.1000f}", 0.0));
}
TEST(FormatterTest, FormatNaN) { TEST(FormatterTest, FormatNaN) {
double nan = std::numeric_limits<double>::quiet_NaN(); double nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ("nan", format("{}", nan)); EXPECT_EQ("nan", format("{}", nan));
@ -1498,7 +1546,7 @@ TEST(FormatterTest, FormatCString) {
EXPECT_EQ("test", format("{0:s}", "test")); EXPECT_EQ("test", format("{0:s}", "test"));
char nonconst[] = "nonconst"; char nonconst[] = "nonconst";
EXPECT_EQ("nonconst", format("{0}", nonconst)); EXPECT_EQ("nonconst", format("{0}", nonconst));
EXPECT_THROW_MSG(format("{0}", static_cast<const char*>(nullptr)), EXPECT_THROW_MSG(format("{0}", static_cast<const char*>(FMT_NULL)),
format_error, "string pointer is null"); format_error, "string pointer is null");
} }
@ -1520,7 +1568,7 @@ TEST(FormatterTest, FormatUCharString) {
TEST(FormatterTest, FormatPointer) { TEST(FormatterTest, FormatPointer) {
check_unknown_types(reinterpret_cast<void*>(0x1234), "p", "pointer"); check_unknown_types(reinterpret_cast<void*>(0x1234), "p", "pointer");
EXPECT_EQ("0x0", format("{0}", static_cast<void*>(nullptr))); EXPECT_EQ("0x0", format("{0}", static_cast<void*>(FMT_NULL)));
EXPECT_EQ("0x1234", format("{0}", reinterpret_cast<void*>(0x1234))); EXPECT_EQ("0x1234", format("{0}", reinterpret_cast<void*>(0x1234)));
EXPECT_EQ("0x1234", format("{0:p}", reinterpret_cast<void*>(0x1234))); EXPECT_EQ("0x1234", format("{0:p}", reinterpret_cast<void*>(0x1234)));
EXPECT_EQ("0x" + std::string(sizeof(void*) * CHAR_BIT / 4, 'f'), EXPECT_EQ("0x" + std::string(sizeof(void*) * CHAR_BIT / 4, 'f'),
@ -1546,45 +1594,6 @@ TEST(FormatterTest, FormatStdStringView) {
} }
#endif #endif
struct implicitly_convertible_to_string_view {
operator fmt::string_view() const { return "foo"; }
};
TEST(FormatterTest, FormatImplicitlyConvertibleToStringView) {
EXPECT_EQ("foo", format("{}", implicitly_convertible_to_string_view()));
}
// std::is_constructible is broken in MSVC until version 2015.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1900
struct explicitly_convertible_to_string_view {
explicit operator fmt::string_view() const { return "foo"; }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringView) {
EXPECT_EQ("foo", format("{}", explicitly_convertible_to_string_view()));
}
struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
EXPECT_EQ(L"foo", format(L"{}", explicitly_convertible_to_wstring_view()));
}
struct explicitly_convertible_to_string_like {
template <
typename String,
typename = typename std::enable_if<
std::is_constructible<String, const char*, std::size_t>::value>::type>
explicit operator String() const { return String("foo", 3u); }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("foo", format("{}", explicitly_convertible_to_string_like()));
}
#endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <> template <>
struct formatter<Date> { struct formatter<Date> {
@ -1628,7 +1637,8 @@ TEST(FormatterTest, CustomFormat) {
TEST(FormatterTest, CustomFormatTo) { TEST(FormatterTest, CustomFormatTo) {
char buf[10] = {}; char buf[10] = {};
auto end = fmt::format_to(buf, "{}", Answer()); auto end = &*fmt::format_to(
fmt::internal::make_checked(buf, 10), "{}", Answer());
EXPECT_EQ(end, buf + 2); EXPECT_EQ(end, buf + 2);
EXPECT_STREQ(buf, "42"); EXPECT_STREQ(buf, "42");
} }
@ -1661,7 +1671,7 @@ TEST(FormatterTest, FormatExamples) {
FILE *ftest = safe_fopen(filename, "r"); FILE *ftest = safe_fopen(filename, "r");
if (ftest) fclose(ftest); if (ftest) fclose(ftest);
int error_code = errno; int error_code = errno;
EXPECT_TRUE(ftest == nullptr); EXPECT_TRUE(ftest == FMT_NULL);
EXPECT_SYSTEM_ERROR({ EXPECT_SYSTEM_ERROR({
FILE *f = safe_fopen(filename, "r"); FILE *f = safe_fopen(filename, "r");
if (!f) if (!f)
@ -1778,7 +1788,7 @@ TEST(FormatTest, Variadic) {
} }
TEST(FormatTest, Dynamic) { TEST(FormatTest, Dynamic) {
using ctx = fmt::format_context; typedef fmt::format_context ctx;
std::vector<fmt::basic_format_arg<ctx>> args; std::vector<fmt::basic_format_arg<ctx>> args;
args.emplace_back(fmt::internal::make_arg<ctx>(42)); args.emplace_back(fmt::internal::make_arg<ctx>(42));
args.emplace_back(fmt::internal::make_arg<ctx>("abc1")); args.emplace_back(fmt::internal::make_arg<ctx>("abc1"));
@ -1830,7 +1840,7 @@ TEST(StrTest, Convert) {
EXPECT_EQ("2012-12-9", s); EXPECT_EQ("2012-12-9", s);
} }
static std::string vformat_message(int id, const char *format, fmt::format_args args) { std::string vformat_message(int id, const char *format, fmt::format_args args) {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
format_to(buffer, "[{}] ", id); format_to(buffer, "[{}] ", id);
vformat_to(buffer, format, args); vformat_to(buffer, format, args);
@ -1895,6 +1905,7 @@ TEST(FormatTest, UdlTemplate) {
EXPECT_EQ("foo", "foo"_format()); EXPECT_EQ("foo", "foo"_format());
EXPECT_EQ(" 42", "{0:10}"_format(42)); EXPECT_EQ(" 42", "{0:10}"_format(42));
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42)); EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42));
} }
#endif // FMT_USE_USER_DEFINED_LITERALS #endif // FMT_USE_USER_DEFINED_LITERALS
@ -1906,6 +1917,7 @@ TEST(FormatTest, Enum) {
TEST(FormatTest, EnumFormatterUnambiguous) { TEST(FormatTest, EnumFormatterUnambiguous) {
fmt::formatter<TestEnum> f; fmt::formatter<TestEnum> f;
ASSERT_GE(sizeof(f), 0); // use f to avoid compiler warning
} }
#if FMT_HAS_FEATURE(cxx_strong_enums) #if FMT_HAS_FEATURE(cxx_strong_enums)
@ -1930,7 +1942,7 @@ class mock_arg_formatter:
typedef buffer_range range; typedef buffer_range range;
mock_arg_formatter(fmt::format_context &ctx, fmt::format_specs *s = FMT_NULL) mock_arg_formatter(fmt::format_context &ctx, fmt::format_specs *s = FMT_NULL)
: base(fmt::internal::get_container(ctx.out()), s) { : base(fmt::internal::get_container(ctx.out()), s, ctx.locale()) {
EXPECT_CALL(*this, call(42)); EXPECT_CALL(*this, call(42));
} }
@ -2080,9 +2092,10 @@ struct test_arg_id_handler {
FMT_CONSTEXPR void on_error(const char *) { res = ERROR; } FMT_CONSTEXPR void on_error(const char *) { res = ERROR; }
}; };
FMT_CONSTEXPR test_arg_id_handler parse_arg_id(const char* s) { template <size_t N>
FMT_CONSTEXPR test_arg_id_handler parse_arg_id(const char (&s)[N]) {
test_arg_id_handler h; test_arg_id_handler h;
fmt::internal::parse_arg_id(s, h); fmt::internal::parse_arg_id(s, s + N, h);
return h; return h;
} }
@ -2142,9 +2155,10 @@ struct test_format_specs_handler {
FMT_CONSTEXPR void on_error(const char *) { res = ERROR; } FMT_CONSTEXPR void on_error(const char *) { res = ERROR; }
}; };
FMT_CONSTEXPR test_format_specs_handler parse_test_specs(const char *s) { template <size_t N>
FMT_CONSTEXPR test_format_specs_handler parse_test_specs(const char (&s)[N]) {
test_format_specs_handler h; test_format_specs_handler h;
fmt::internal::parse_format_specs(s, h); fmt::internal::parse_format_specs(s, s + N, h);
return h; return h;
} }
@ -2188,61 +2202,64 @@ struct test_context {
FMT_CONSTEXPR test_context error_handler() { return *this; } FMT_CONSTEXPR test_context error_handler() { return *this; }
}; };
FMT_CONSTEXPR fmt::format_specs parse_specs(const char *s) { template <size_t N>
FMT_CONSTEXPR fmt::format_specs parse_specs(const char (&s)[N]) {
fmt::format_specs specs; fmt::format_specs specs;
test_context ctx{}; test_context ctx{};
fmt::internal::specs_handler<test_context> h(specs, ctx); fmt::internal::specs_handler<test_context> h(specs, ctx);
parse_format_specs(s, h); parse_format_specs(s, s + N, h);
return specs; return specs;
} }
TEST(FormatTest, ConstexprSpecsHandler) { TEST(FormatTest, ConstexprSpecsHandler) {
static_assert(parse_specs("<").align() == fmt::ALIGN_LEFT, ""); static_assert(parse_specs("<").align() == fmt::ALIGN_LEFT, "");
static_assert(parse_specs("*^").fill() == '*', ""); static_assert(parse_specs("*^").fill() == '*', "");
static_assert(parse_specs("+").flag(fmt::PLUS_FLAG), ""); static_assert(parse_specs("+").has(fmt::PLUS_FLAG), "");
static_assert(parse_specs("-").flag(fmt::MINUS_FLAG), ""); static_assert(parse_specs("-").has(fmt::MINUS_FLAG), "");
static_assert(parse_specs(" ").flag(fmt::SIGN_FLAG), ""); static_assert(parse_specs(" ").has(fmt::SIGN_FLAG), "");
static_assert(parse_specs("#").flag(fmt::HASH_FLAG), ""); static_assert(parse_specs("#").has(fmt::HASH_FLAG), "");
static_assert(parse_specs("0").align() == fmt::ALIGN_NUMERIC, ""); static_assert(parse_specs("0").align() == fmt::ALIGN_NUMERIC, "");
static_assert(parse_specs("42").width() == 42, ""); static_assert(parse_specs("42").width() == 42, "");
static_assert(parse_specs("{}").width() == 11, ""); static_assert(parse_specs("{}").width() == 11, "");
static_assert(parse_specs("{0}").width() == 22, ""); static_assert(parse_specs("{0}").width() == 22, "");
static_assert(parse_specs(".42").precision() == 42, ""); static_assert(parse_specs(".42").precision == 42, "");
static_assert(parse_specs(".{}").precision() == 11, ""); static_assert(parse_specs(".{}").precision == 11, "");
static_assert(parse_specs(".{0}").precision() == 22, ""); static_assert(parse_specs(".{0}").precision == 22, "");
static_assert(parse_specs("d").type() == 'd', ""); static_assert(parse_specs("d").type == 'd', "");
} }
template <size_t N>
FMT_CONSTEXPR fmt::internal::dynamic_format_specs<char> FMT_CONSTEXPR fmt::internal::dynamic_format_specs<char>
parse_dynamic_specs(const char *s) { parse_dynamic_specs(const char (&s)[N]) {
fmt::internal::dynamic_format_specs<char> specs; fmt::internal::dynamic_format_specs<char> specs;
test_context ctx{}; test_context ctx{};
fmt::internal::dynamic_specs_handler<test_context> h(specs, ctx); fmt::internal::dynamic_specs_handler<test_context> h(specs, ctx);
parse_format_specs(s, h); parse_format_specs(s, s + N, h);
return specs; return specs;
} }
TEST(FormatTest, ConstexprDynamicSpecsHandler) { TEST(FormatTest, ConstexprDynamicSpecsHandler) {
static_assert(parse_dynamic_specs("<").align() == fmt::ALIGN_LEFT, ""); static_assert(parse_dynamic_specs("<").align() == fmt::ALIGN_LEFT, "");
static_assert(parse_dynamic_specs("*^").fill() == '*', ""); static_assert(parse_dynamic_specs("*^").fill() == '*', "");
static_assert(parse_dynamic_specs("+").flag(fmt::PLUS_FLAG), ""); static_assert(parse_dynamic_specs("+").has(fmt::PLUS_FLAG), "");
static_assert(parse_dynamic_specs("-").flag(fmt::MINUS_FLAG), ""); static_assert(parse_dynamic_specs("-").has(fmt::MINUS_FLAG), "");
static_assert(parse_dynamic_specs(" ").flag(fmt::SIGN_FLAG), ""); static_assert(parse_dynamic_specs(" ").has(fmt::SIGN_FLAG), "");
static_assert(parse_dynamic_specs("#").flag(fmt::HASH_FLAG), ""); static_assert(parse_dynamic_specs("#").has(fmt::HASH_FLAG), "");
static_assert(parse_dynamic_specs("0").align() == fmt::ALIGN_NUMERIC, ""); static_assert(parse_dynamic_specs("0").align() == fmt::ALIGN_NUMERIC, "");
static_assert(parse_dynamic_specs("42").width() == 42, ""); static_assert(parse_dynamic_specs("42").width() == 42, "");
static_assert(parse_dynamic_specs("{}").width_ref.index == 33, ""); static_assert(parse_dynamic_specs("{}").width_ref.index == 33, "");
static_assert(parse_dynamic_specs("{42}").width_ref.index == 42, ""); static_assert(parse_dynamic_specs("{42}").width_ref.index == 42, "");
static_assert(parse_dynamic_specs(".42").precision() == 42, ""); static_assert(parse_dynamic_specs(".42").precision == 42, "");
static_assert(parse_dynamic_specs(".{}").precision_ref.index == 33, ""); static_assert(parse_dynamic_specs(".{}").precision_ref.index == 33, "");
static_assert(parse_dynamic_specs(".{42}").precision_ref.index == 42, ""); static_assert(parse_dynamic_specs(".{42}").precision_ref.index == 42, "");
static_assert(parse_dynamic_specs("d").type() == 'd', ""); static_assert(parse_dynamic_specs("d").type == 'd', "");
} }
FMT_CONSTEXPR test_format_specs_handler check_specs(const char *s) { template <size_t N>
FMT_CONSTEXPR test_format_specs_handler check_specs(const char (&s)[N]) {
fmt::internal::specs_checker<test_format_specs_handler> fmt::internal::specs_checker<test_format_specs_handler>
checker(test_format_specs_handler(), fmt::internal::double_type); checker(test_format_specs_handler(), fmt::internal::double_type);
parse_format_specs(s, checker); parse_format_specs(s, s + N, checker);
return checker; return checker;
} }
@ -2271,20 +2288,21 @@ struct test_format_string_handler {
template <typename T> template <typename T>
FMT_CONSTEXPR void on_arg_id(T) {} FMT_CONSTEXPR void on_arg_id(T) {}
template <typename Iterator> FMT_CONSTEXPR void on_replacement_field(const char *) {}
FMT_CONSTEXPR void on_replacement_field(Iterator) {}
template <typename Iterator> FMT_CONSTEXPR const char *on_format_specs(const char *begin, const char*) {
FMT_CONSTEXPR Iterator on_format_specs(Iterator it) { return it; } return begin;
}
FMT_CONSTEXPR void on_error(const char *) { error = true; } FMT_CONSTEXPR void on_error(const char *) { error = true; }
bool error = false; bool error = false;
}; };
FMT_CONSTEXPR bool parse_string(fmt::string_view s) { template <size_t N>
FMT_CONSTEXPR bool parse_string(const char (&s)[N]) {
test_format_string_handler h; test_format_string_handler h;
fmt::internal::parse_format_string<true>(s, h); fmt::internal::parse_format_string<true>(fmt::string_view(s, N - 1), h);
return !h.error; return !h.error;
} }
@ -2330,8 +2348,8 @@ FMT_CONSTEXPR bool equal(const char *s1, const char *s2) {
template <typename... Args> template <typename... Args>
FMT_CONSTEXPR bool test_error(const char *fmt, const char *expected_error) { FMT_CONSTEXPR bool test_error(const char *fmt, const char *expected_error) {
const char *actual_error = nullptr; const char *actual_error = FMT_NULL;
fmt::internal::check_format_string<char, test_error_handler, Args...>( fmt::internal::do_check_format_string<char, test_error_handler, Args...>(
string_view(fmt, len(fmt)), test_error_handler(actual_error)); string_view(fmt, len(fmt)), test_error_handler(actual_error));
return equal(actual_error, expected_error); return equal(actual_error, expected_error);
} }
@ -2342,7 +2360,7 @@ FMT_CONSTEXPR bool test_error(const char *fmt, const char *expected_error) {
static_assert(test_error<__VA_ARGS__>(fmt, error), "") static_assert(test_error<__VA_ARGS__>(fmt, error), "")
TEST(FormatTest, FormatStringErrors) { TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR_NOARGS("foo", nullptr); EXPECT_ERROR_NOARGS("foo", FMT_NULL);
EXPECT_ERROR_NOARGS("}", "unmatched '}' in format string"); EXPECT_ERROR_NOARGS("}", "unmatched '}' in format string");
EXPECT_ERROR("{0:s", "unknown format specifier", Date); EXPECT_ERROR("{0:s", "unknown format specifier", Date);
#ifndef _MSC_VER #ifndef _MSC_VER
@ -2395,23 +2413,46 @@ TEST(FormatTest, FormatStringErrors) {
"cannot switch from automatic to manual argument indexing", "cannot switch from automatic to manual argument indexing",
int, int); int, int);
} }
TEST(FormatTest, VFormatTo) {
typedef fmt::format_context context;
fmt::basic_format_arg<context> arg = fmt::internal::make_arg<context>(42);
fmt::basic_format_args<context> args(&arg, 1);
std::string s;
fmt::vformat_to(std::back_inserter(s), "{}", args);
EXPECT_EQ("42", s);
s.clear();
fmt::vformat_to(std::back_inserter(s), FMT_STRING("{}"), args);
EXPECT_EQ("42", s);
typedef fmt::wformat_context wcontext;
fmt::basic_format_arg<wcontext> warg = fmt::internal::make_arg<wcontext>(42);
fmt::basic_format_args<wcontext> wargs(&warg, 1);
std::wstring w;
fmt::vformat_to(std::back_inserter(w), L"{}", wargs);
EXPECT_EQ(L"42", w);
w.clear();
fmt::vformat_to(std::back_inserter(w), FMT_STRING(L"{}"), wargs);
EXPECT_EQ(L"42", w);
}
#endif // FMT_USE_CONSTEXPR #endif // FMT_USE_CONSTEXPR
TEST(FormatTest, ConstructU8StringViewFromCString) { TEST(FormatTest, ConstructU8StringViewFromCString) {
fmt::u8string_view s("ab"); fmt::u8string_view s("ab");
EXPECT_EQ(s.size(), 2u); EXPECT_EQ(s.size(), 2u);
const fmt::char8_t *data = s.data(); const fmt::char8_t *data = s.data();
EXPECT_EQ(data[0].value, 'a'); EXPECT_EQ(data[0], 'a');
EXPECT_EQ(data[1].value, 'b'); EXPECT_EQ(data[1], 'b');
} }
TEST(FormatTest, ConstructU8StringViewFromDataAndSize) { TEST(FormatTest, ConstructU8StringViewFromDataAndSize) {
fmt::u8string_view s("foobar", 3); fmt::u8string_view s("foobar", 3);
EXPECT_EQ(s.size(), 3u); EXPECT_EQ(s.size(), 3u);
const fmt::char8_t *data = s.data(); const fmt::char8_t *data = s.data();
EXPECT_EQ(data[0].value, 'f'); EXPECT_EQ(data[0], 'f');
EXPECT_EQ(data[1].value, 'o'); EXPECT_EQ(data[1], 'o');
EXPECT_EQ(data[2].value, 'o'); EXPECT_EQ(data[2], 'o');
} }
#if FMT_USE_USER_DEFINED_LITERALS #if FMT_USE_USER_DEFINED_LITERALS
@ -2420,7 +2461,12 @@ TEST(FormatTest, U8StringViewLiteral) {
fmt::u8string_view s = "ab"_u; fmt::u8string_view s = "ab"_u;
EXPECT_EQ(s.size(), 2u); EXPECT_EQ(s.size(), 2u);
const fmt::char8_t *data = s.data(); const fmt::char8_t *data = s.data();
EXPECT_EQ(data[0].value, 'a'); EXPECT_EQ(data[0], 'a');
EXPECT_EQ(data[1].value, 'b'); EXPECT_EQ(data[1], 'b');
EXPECT_EQ(format("{:*^5}"_u, "🤡"_u), "**🤡**"_u);
} }
#endif #endif
TEST(FormatTest, FormatU8String) {
EXPECT_EQ(format(fmt::u8string_view("{}"), 42), fmt::u8string_view("42"));
}

View file

@ -311,8 +311,8 @@ using fmt::error_code;
using fmt::file; using fmt::file;
TEST(ErrorCodeTest, Ctor) { TEST(ErrorCodeTest, Ctor) {
EXPECT_EQ(0, error_code().get()); EXPECT_EQ(error_code().get(), 0);
EXPECT_EQ(42, error_code(42).get()); EXPECT_EQ(error_code(42).get(), 42);
} }
TEST(OutputRedirectTest, ScopedRedirect) { TEST(OutputRedirectTest, ScopedRedirect) {
@ -340,10 +340,10 @@ TEST(OutputRedirectTest, FlushErrorInCtor) {
// 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));
scoped_ptr<OutputRedirect> redir{nullptr}; scoped_ptr<OutputRedirect> redir{FMT_NULL};
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())), EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())),
EBADF, "cannot flush stream"); EBADF, "cannot flush stream");
redir.reset(nullptr); redir.reset(FMT_NULL);
write_copy.dup2(write_fd); // "undo" close or dtor will fail write_copy.dup2(write_fd); // "undo" close or dtor will fail
} }
@ -352,7 +352,7 @@ TEST(OutputRedirectTest, DupErrorInCtor) {
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{nullptr}; scoped_ptr<OutputRedirect> redir{FMT_NULL};
EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())), EXPECT_SYSTEM_ERROR_NOASSERT(redir.reset(new OutputRedirect(f.get())),
EBADF, fmt::format("cannot duplicate file descriptor {}", fd)); EBADF, fmt::format("cannot duplicate file descriptor {}", fd));
copy.dup2(fd); // "undo" close or dtor will fail copy.dup2(fd); // "undo" close or dtor will fail
@ -403,7 +403,7 @@ TEST(OutputRedirectTest, ErrorInDtor) {
// output in EXPECT_STDERR and the second close will break output // output in EXPECT_STDERR and the second close will break output
// redirection. // redirection.
FMT_POSIX(close(write_fd)); FMT_POSIX(close(write_fd));
SUPPRESS_ASSERT(redir.reset(nullptr)); SUPPRESS_ASSERT(redir.reset(FMT_NULL));
}, format_system_error(EBADF, "cannot flush stream")); }, format_system_error(EBADF, "cannot flush stream"));
write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail write_copy.dup2(write_fd); // "undo" close or dtor of buffered_file will fail
} }

View file

@ -155,7 +155,7 @@ std::string read(fmt::file &f, std::size_t count);
template <typename Mock> template <typename Mock>
struct ScopedMock : testing::StrictMock<Mock> { struct ScopedMock : testing::StrictMock<Mock> {
ScopedMock() { Mock::instance = this; } ScopedMock() { Mock::instance = this; }
~ScopedMock() { Mock::instance = nullptr; } ~ScopedMock() { Mock::instance = FMT_NULL; }
}; };
#endif // FMT_GTEST_EXTRA_H_ #endif // FMT_GTEST_EXTRA_H_

34
test/locale-test.cc Normal file
View file

@ -0,0 +1,34 @@
// Formatting library for C++ - locale tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/locale.h"
#include "gmock.h"
template <typename Char>
struct numpunct : std::numpunct<Char> {
protected:
Char do_thousands_sep() const FMT_OVERRIDE { return '~'; }
};
TEST(LocaleTest, Format) {
std::locale loc(std::locale(), new numpunct<char>());
EXPECT_EQ("1,234,567", fmt::format(std::locale(), "{:n}", 1234567));
EXPECT_EQ("1~234~567", fmt::format(loc, "{:n}", 1234567));
fmt::format_arg_store<fmt::format_context, int> as{1234567};
EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:n}", fmt::format_args(as)));
std::string s;
fmt::format_to(std::back_inserter(s), loc, "{:n}", 1234567);
EXPECT_EQ("1~234~567", s);
}
TEST(LocaleTest, WFormat) {
std::locale loc(std::locale(), new numpunct<wchar_t>());
EXPECT_EQ(L"1,234,567", fmt::format(std::locale(), L"{:n}", 1234567));
EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:n}", 1234567));
fmt::format_arg_store<fmt::wformat_context, int> as{1234567};
EXPECT_EQ(L"1~234~567", fmt::vformat(loc, L"{:n}", fmt::wformat_args(as)));
}

View file

@ -28,13 +28,13 @@ class allocator_ref {
void move(allocator_ref &other) { void move(allocator_ref &other) {
alloc_ = other.alloc_; alloc_ = other.alloc_;
other.alloc_ = nullptr; other.alloc_ = FMT_NULL;
} }
public: public:
typedef typename Allocator::value_type value_type; typedef typename Allocator::value_type value_type;
explicit allocator_ref(Allocator *alloc = nullptr) : alloc_(alloc) {} explicit allocator_ref(Allocator *alloc = FMT_NULL) : alloc_(alloc) {}
allocator_ref(const allocator_ref &other) : alloc_(other.alloc_) {} allocator_ref(const allocator_ref &other) : alloc_(other.alloc_) {}
allocator_ref(allocator_ref &&other) { move(other); } allocator_ref(allocator_ref &&other) { move(other); }

View file

@ -146,12 +146,11 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
} os(streambuf); } os(streambuf);
testing::InSequence sequence; testing::InSequence sequence;
const char *data = nullptr; const char *data = FMT_NULL;
std::size_t size = max_size;
do {
typedef std::make_unsigned<std::streamsize>::type ustreamsize; typedef std::make_unsigned<std::streamsize>::type ustreamsize;
ustreamsize n = std::min<ustreamsize>( ustreamsize size = max_size;
size, fmt::internal::to_unsigned(max_streamsize)); do {
auto n = std::min(size, fmt::internal::to_unsigned(max_streamsize));
EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n))) EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n)))
.WillOnce(testing::Return(max_streamsize)); .WillOnce(testing::Return(max_streamsize));
data += n; data += n;

View file

@ -133,7 +133,7 @@ int test::dup2(int fildes, int fildes2) {
} }
FILE *test::fdopen(int fildes, const char *mode) { FILE *test::fdopen(int fildes, const char *mode) {
EMULATE_EINTR(fdopen, nullptr); EMULATE_EINTR(fdopen, FMT_NULL);
return ::FMT_POSIX(fdopen(fildes, mode)); return ::FMT_POSIX(fdopen(fildes, mode));
} }
@ -162,7 +162,7 @@ int test::pipe(int *pfds, unsigned psize, int textmode) {
#endif #endif
FILE *test::fopen(const char *filename, const char *mode) { FILE *test::fopen(const char *filename, const char *mode) {
EMULATE_EINTR(fopen, nullptr); EMULATE_EINTR(fopen, FMT_NULL);
return ::fopen(filename, mode); return ::fopen(filename, mode);
} }
@ -216,7 +216,7 @@ 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{nullptr}; scoped_ptr<file> f{FMT_NULL};
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
@ -232,7 +232,7 @@ TEST(FileTest, CloseNoRetryInDtor) {
int saved_close_count = 0; int saved_close_count = 0;
EXPECT_WRITE(stderr, { EXPECT_WRITE(stderr, {
close_count = 1; close_count = 1;
f.reset(nullptr); f.reset(FMT_NULL);
saved_close_count = close_count; saved_close_count = close_count;
close_count = 0; close_count = 0;
}, format_system_error(EINTR, "cannot close file") + "\n"); }, format_system_error(EINTR, "cannot close file") + "\n");
@ -385,7 +385,7 @@ 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<buffered_file> f{nullptr}; scoped_ptr<buffered_file> f{FMT_NULL};
EXPECT_RETRY(f.reset(new buffered_file("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
@ -402,7 +402,7 @@ TEST(BufferedFileTest, CloseNoRetryInDtor) {
int saved_fclose_count = 0; int saved_fclose_count = 0;
EXPECT_WRITE(stderr, { EXPECT_WRITE(stderr, {
fclose_count = 1; fclose_count = 1;
f.reset(nullptr); f.reset(FMT_NULL);
saved_fclose_count = fclose_count; saved_fclose_count = fclose_count;
fclose_count = 0; fclose_count = 0;
}, format_system_error(EINTR, "cannot close file") + "\n"); }, format_system_error(EINTR, "cannot close file") + "\n");
@ -440,7 +440,7 @@ TEST(ScopedMock, Scope) {
TestMock &copy = mock; TestMock &copy = mock;
static_cast<void>(copy); static_cast<void>(copy);
} }
EXPECT_EQ(nullptr, TestMock::instance); EXPECT_EQ(FMT_NULL, TestMock::instance);
} }
#ifdef FMT_LOCALE #ifdef FMT_LOCALE
@ -515,7 +515,7 @@ TEST(LocaleTest, Locale) {
#endif #endif
ScopedMock<LocaleMock> mock; ScopedMock<LocaleMock> mock;
LocaleType impl = reinterpret_cast<LocaleType>(42); LocaleType impl = reinterpret_cast<LocaleType>(42);
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr)) EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), FMT_NULL))
.WillOnce(Return(impl)); .WillOnce(Return(impl));
EXPECT_CALL(mock, freelocale(impl)); EXPECT_CALL(mock, freelocale(impl));
fmt::Locale locale; fmt::Locale locale;

View file

@ -59,26 +59,26 @@ static void write(file &f, fmt::string_view s) {
TEST(BufferedFileTest, DefaultCtor) { TEST(BufferedFileTest, DefaultCtor) {
buffered_file f; buffered_file f;
EXPECT_TRUE(f.get() == nullptr); EXPECT_TRUE(f.get() == FMT_NULL);
} }
TEST(BufferedFileTest, MoveCtor) { TEST(BufferedFileTest, MoveCtor) {
buffered_file bf = open_buffered_file(); buffered_file bf = open_buffered_file();
FILE *fp = bf.get(); FILE *fp = bf.get();
EXPECT_TRUE(fp != nullptr); EXPECT_TRUE(fp != FMT_NULL);
buffered_file bf2(std::move(bf)); buffered_file bf2(std::move(bf));
EXPECT_EQ(fp, bf2.get()); EXPECT_EQ(fp, bf2.get());
EXPECT_TRUE(bf.get() == nullptr); EXPECT_TRUE(bf.get() == FMT_NULL);
} }
TEST(BufferedFileTest, MoveAssignment) { TEST(BufferedFileTest, MoveAssignment) {
buffered_file bf = open_buffered_file(); buffered_file bf = open_buffered_file();
FILE *fp = bf.get(); FILE *fp = bf.get();
EXPECT_TRUE(fp != nullptr); EXPECT_TRUE(fp != FMT_NULL);
buffered_file 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() == nullptr); EXPECT_TRUE(bf.get() == FMT_NULL);
} }
TEST(BufferedFileTest, MoveAssignmentClosesFile) { TEST(BufferedFileTest, MoveAssignmentClosesFile) {
@ -90,13 +90,13 @@ TEST(BufferedFileTest, MoveAssignmentClosesFile) {
} }
TEST(BufferedFileTest, MoveFromTemporaryInCtor) { TEST(BufferedFileTest, MoveFromTemporaryInCtor) {
FILE *fp = nullptr; FILE *fp = FMT_NULL;
buffered_file 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 = nullptr; FILE *fp = FMT_NULL;
buffered_file f; buffered_file f;
f = open_buffered_file(&fp); f = open_buffered_file(&fp);
EXPECT_EQ(fp, f.get()); EXPECT_EQ(fp, f.get());
@ -126,7 +126,7 @@ TEST(BufferedFileTest, CloseErrorInDtor) {
// output in EXPECT_STDERR and the second close will break output // output in EXPECT_STDERR and the second close will break output
// redirection. // redirection.
FMT_POSIX(close(f->fileno())); FMT_POSIX(close(f->fileno()));
SUPPRESS_ASSERT(f.reset(nullptr)); SUPPRESS_ASSERT(f.reset(FMT_NULL));
}, format_system_error(EBADF, "cannot close file") + "\n"); }, format_system_error(EBADF, "cannot close file") + "\n");
} }
@ -134,7 +134,7 @@ TEST(BufferedFileTest, Close) {
buffered_file 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() == nullptr); EXPECT_TRUE(f.get() == FMT_NULL);
EXPECT_TRUE(isclosed(fd)); EXPECT_TRUE(isclosed(fd));
} }
@ -142,7 +142,7 @@ TEST(BufferedFileTest, CloseError) {
buffered_file 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() == nullptr); EXPECT_TRUE(f.get() == FMT_NULL);
} }
TEST(BufferedFileTest, Fileno) { TEST(BufferedFileTest, Fileno) {
@ -253,7 +253,7 @@ TEST(FileTest, CloseErrorInDtor) {
// output in EXPECT_STDERR and the second close will break output // output in EXPECT_STDERR and the second close will break output
// redirection. // redirection.
FMT_POSIX(close(f->descriptor())); FMT_POSIX(close(f->descriptor()));
SUPPRESS_ASSERT(f.reset(nullptr)); SUPPRESS_ASSERT(f.reset(FMT_NULL));
}, format_system_error(EBADF, "cannot close file") + "\n"); }, format_system_error(EBADF, "cannot close file") + "\n");
} }
@ -334,7 +334,7 @@ TEST(FileTest, Dup2NoExcept) {
file copy = open_file(); file copy = open_file();
error_code ec; error_code ec;
f.dup2(copy.descriptor(), ec); f.dup2(copy.descriptor(), ec);
EXPECT_EQ(0, ec.get()); EXPECT_EQ(ec.get(), 0);
EXPECT_NE(f.descriptor(), copy.descriptor()); EXPECT_NE(f.descriptor(), copy.descriptor());
EXPECT_READ(copy, FILE_CONTENT); EXPECT_READ(copy, FILE_CONTENT);
} }

View file

@ -32,22 +32,33 @@ static std::wstring make_positional(fmt::wstring_view format) {
return s; return s;
} }
// A wrapper around fmt::sprintf to workaround bogus warnings about invalid
// format strings in MSVC.
template <typename... Args>
std::string test_sprintf(fmt::string_view format, const Args &... args) {
return fmt::sprintf(format, args...);
}
template <typename... Args>
std::wstring test_sprintf(fmt::wstring_view format, const Args &... args) {
return fmt::sprintf(format, args...);
}
#define EXPECT_PRINTF(expected_output, format, arg) \ #define EXPECT_PRINTF(expected_output, format, arg) \
EXPECT_EQ(expected_output, fmt::sprintf(format, arg)) \ EXPECT_EQ(expected_output, test_sprintf(format, arg)) \
<< "format: " << format; \ << "format: " << format; \
EXPECT_EQ(expected_output, fmt::sprintf(make_positional(format), arg)) EXPECT_EQ(expected_output, fmt::sprintf(make_positional(format), arg))
TEST(PrintfTest, NoArgs) { TEST(PrintfTest, NoArgs) {
EXPECT_EQ("test", fmt::sprintf("test")); EXPECT_EQ("test", test_sprintf("test"));
EXPECT_EQ(L"test", fmt::sprintf(L"test")); EXPECT_EQ(L"test", fmt::sprintf(L"test"));
} }
TEST(PrintfTest, Escape) { TEST(PrintfTest, Escape) {
EXPECT_EQ("%", fmt::sprintf("%%")); EXPECT_EQ("%", test_sprintf("%%"));
EXPECT_EQ("before %", fmt::sprintf("before %%")); EXPECT_EQ("before %", test_sprintf("before %%"));
EXPECT_EQ("% after", fmt::sprintf("%% after")); EXPECT_EQ("% after", test_sprintf("%% after"));
EXPECT_EQ("before % after", fmt::sprintf("before %% after")); EXPECT_EQ("before % after", test_sprintf("before %% after"));
EXPECT_EQ("%s", fmt::sprintf("%%s")); EXPECT_EQ("%s", test_sprintf("%%s"));
EXPECT_EQ(L"%", fmt::sprintf(L"%%")); EXPECT_EQ(L"%", fmt::sprintf(L"%%"));
EXPECT_EQ(L"before %", fmt::sprintf(L"before %%")); EXPECT_EQ(L"before %", fmt::sprintf(L"before %%"));
EXPECT_EQ(L"% after", fmt::sprintf(L"%% after")); EXPECT_EQ(L"% after", fmt::sprintf(L"%% after"));
@ -56,60 +67,60 @@ TEST(PrintfTest, Escape) {
} }
TEST(PrintfTest, PositionalArgs) { TEST(PrintfTest, PositionalArgs) {
EXPECT_EQ("42", fmt::sprintf("%1$d", 42)); EXPECT_EQ("42", test_sprintf("%1$d", 42));
EXPECT_EQ("before 42", fmt::sprintf("before %1$d", 42)); EXPECT_EQ("before 42", test_sprintf("before %1$d", 42));
EXPECT_EQ("42 after", fmt::sprintf("%1$d after",42)); EXPECT_EQ("42 after", test_sprintf("%1$d after",42));
EXPECT_EQ("before 42 after", fmt::sprintf("before %1$d after", 42)); EXPECT_EQ("before 42 after", test_sprintf("before %1$d after", 42));
EXPECT_EQ("answer = 42", fmt::sprintf("%1$s = %2$d", "answer", 42)); EXPECT_EQ("answer = 42", test_sprintf("%1$s = %2$d", "answer", 42));
EXPECT_EQ("42 is the answer", EXPECT_EQ("42 is the answer",
fmt::sprintf("%2$d is the %1$s", "answer", 42)); test_sprintf("%2$d is the %1$s", "answer", 42));
EXPECT_EQ("abracadabra", fmt::sprintf("%1$s%2$s%1$s", "abra", "cad")); EXPECT_EQ("abracadabra", test_sprintf("%1$s%2$s%1$s", "abra", "cad"));
} }
TEST(PrintfTest, AutomaticArgIndexing) { TEST(PrintfTest, AutomaticArgIndexing) {
EXPECT_EQ("abc", fmt::sprintf("%c%c%c", 'a', 'b', 'c')); EXPECT_EQ("abc", test_sprintf("%c%c%c", 'a', 'b', 'c'));
} }
TEST(PrintfTest, NumberIsTooBigInArgIndex) { TEST(PrintfTest, NumberIsTooBigInArgIndex) {
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$", BIG_NUM)), EXPECT_THROW_MSG(test_sprintf(format("%{}$", BIG_NUM)),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", BIG_NUM)), EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM)),
format_error, "number is too big"); format_error, "number is too big");
} }
TEST(PrintfTest, SwitchArgIndexing) { TEST(PrintfTest, SwitchArgIndexing) {
EXPECT_THROW_MSG(fmt::sprintf("%1$d%", 1, 2), EXPECT_THROW_MSG(test_sprintf("%1$d%", 1, 2),
format_error, "cannot switch from manual to automatic argument indexing"); format_error, "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::sprintf(format("%1$d%{}d", BIG_NUM), 1, 2), EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(fmt::sprintf("%1$d%d", 1, 2), EXPECT_THROW_MSG(test_sprintf("%1$d%d", 1, 2),
format_error, "cannot switch from manual to automatic argument indexing"); format_error, "cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(fmt::sprintf("%d%1$", 1, 2), EXPECT_THROW_MSG(test_sprintf("%d%1$", 1, 2),
format_error, "cannot switch from automatic to manual argument indexing"); format_error, "cannot switch from automatic to manual argument indexing");
EXPECT_THROW_MSG(fmt::sprintf(format("%d%{}$d", BIG_NUM), 1, 2), EXPECT_THROW_MSG(test_sprintf(format("%d%{}$d", BIG_NUM), 1, 2),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(fmt::sprintf("%d%1$d", 1, 2), EXPECT_THROW_MSG(test_sprintf("%d%1$d", 1, 2),
format_error, "cannot switch from automatic to manual argument indexing"); format_error, "cannot switch from automatic to manual argument indexing");
// Indexing errors override width errors. // Indexing errors override width errors.
EXPECT_THROW_MSG(fmt::sprintf(format("%d%1${}d", BIG_NUM), 1, 2), EXPECT_THROW_MSG(test_sprintf(format("%d%1${}d", BIG_NUM), 1, 2),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(fmt::sprintf(format("%1$d%{}d", BIG_NUM), 1, 2), EXPECT_THROW_MSG(test_sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
format_error, "number is too big"); format_error, "number is too big");
} }
TEST(PrintfTest, InvalidArgIndex) { TEST(PrintfTest, InvalidArgIndex) {
EXPECT_THROW_MSG(fmt::sprintf("%0$d", 42), format_error, EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error,
"argument index out of range"); "argument index out of range");
EXPECT_THROW_MSG(fmt::sprintf("%2$d", 42), format_error, EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error,
"argument index out of range"); "argument index out of range");
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", INT_MAX), 42), EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42),
format_error, "argument index out of range"); format_error, "argument index out of range");
EXPECT_THROW_MSG(fmt::sprintf("%2$", 42), EXPECT_THROW_MSG(test_sprintf("%2$", 42),
format_error, "argument index out of range"); format_error, "argument index out of range");
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", BIG_NUM), 42), EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42),
format_error, "number is too big"); format_error, "number is too big");
} }
@ -180,13 +191,8 @@ TEST(PrintfTest, HashFlag) {
safe_sprintf(buffer, "%#E", -42.0); safe_sprintf(buffer, "%#E", -42.0);
EXPECT_PRINTF(buffer, "%#E", -42.0); EXPECT_PRINTF(buffer, "%#E", -42.0);
if (fmt::internal::use_grisu()) {
EXPECT_PRINTF("-42.0", "%#g", -42.0);
EXPECT_PRINTF("-42.0", "%#G", -42.0);
} else {
EXPECT_PRINTF("-42.0000", "%#g", -42.0); EXPECT_PRINTF("-42.0000", "%#g", -42.0);
EXPECT_PRINTF("-42.0000", "%#G", -42.0); EXPECT_PRINTF("-42.0000", "%#G", -42.0);
}
safe_sprintf(buffer, "%#a", 16.0); safe_sprintf(buffer, "%#a", 16.0);
EXPECT_PRINTF(buffer, "%#a", 16.0); EXPECT_PRINTF(buffer, "%#a", 16.0);
@ -201,23 +207,23 @@ TEST(PrintfTest, Width) {
EXPECT_PRINTF(" abc", "%5s", "abc"); EXPECT_PRINTF(" abc", "%5s", "abc");
// Width cannot be specified twice. // Width cannot be specified twice.
EXPECT_THROW_MSG(fmt::sprintf("%5-5d", 42), format_error, EXPECT_THROW_MSG(test_sprintf("%5-5d", 42), format_error,
"invalid type specifier"); "invalid type specifier");
EXPECT_THROW_MSG(fmt::sprintf(format("%{}d", BIG_NUM), 42), EXPECT_THROW_MSG(test_sprintf(format("%{}d", BIG_NUM), 42),
format_error, "number is too big"); format_error, "number is too big");
EXPECT_THROW_MSG(fmt::sprintf(format("%1${}d", BIG_NUM), 42), EXPECT_THROW_MSG(test_sprintf(format("%1${}d", BIG_NUM), 42),
format_error, "number is too big"); format_error, "number is too big");
} }
TEST(PrintfTest, DynamicWidth) { TEST(PrintfTest, DynamicWidth) {
EXPECT_EQ(" 42", fmt::sprintf("%*d", 5, 42)); EXPECT_EQ(" 42", test_sprintf("%*d", 5, 42));
EXPECT_EQ("42 ", fmt::sprintf("%*d", -5, 42)); EXPECT_EQ("42 ", test_sprintf("%*d", -5, 42));
EXPECT_THROW_MSG(fmt::sprintf("%*d", 5.0, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%*d", 5.0, 42), format_error,
"width is not integer"); "width is not integer");
EXPECT_THROW_MSG(fmt::sprintf("%*d"), format_error, EXPECT_THROW_MSG(test_sprintf("%*d"), format_error,
"argument index out of range"); "argument index out of range");
EXPECT_THROW_MSG(fmt::sprintf("%*d", BIG_NUM, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%*d", BIG_NUM, 42), format_error,
"number is too big"); "number is too big");
} }
@ -258,17 +264,17 @@ TEST(PrintfTest, IgnorePrecisionForNonNumericArg) {
} }
TEST(PrintfTest, DynamicPrecision) { TEST(PrintfTest, DynamicPrecision) {
EXPECT_EQ("00042", fmt::sprintf("%.*d", 5, 42)); EXPECT_EQ("00042", test_sprintf("%.*d", 5, 42));
EXPECT_EQ("42", fmt::sprintf("%.*d", -5, 42)); EXPECT_EQ("42", test_sprintf("%.*d", -5, 42));
EXPECT_THROW_MSG(fmt::sprintf("%.*d", 5.0, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error,
"precision is not integer"); "precision is not integer");
EXPECT_THROW_MSG(fmt::sprintf("%.*d"), format_error, EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error,
"argument index out of range"); "argument index out of range");
EXPECT_THROW_MSG(fmt::sprintf("%.*d", BIG_NUM, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%.*d", BIG_NUM, 42), format_error,
"number is too big"); "number is too big");
if (sizeof(long long) != sizeof(int)) { if (sizeof(long long) != sizeof(int)) {
long long prec = static_cast<long long>(INT_MIN) - 1; long long prec = static_cast<long long>(INT_MIN) - 1;
EXPECT_THROW_MSG(fmt::sprintf("%.*d", prec, 42), format_error, EXPECT_THROW_MSG(test_sprintf("%.*d", prec, 42), format_error,
"number is too big"); "number is too big");
} }
} }
@ -431,11 +437,11 @@ TEST(PrintfTest, Char) {
TEST(PrintfTest, String) { TEST(PrintfTest, String) {
EXPECT_PRINTF("abc", "%s", "abc"); EXPECT_PRINTF("abc", "%s", "abc");
const char *null_str = nullptr; const char *null_str = FMT_NULL;
EXPECT_PRINTF("(null)", "%s", null_str); EXPECT_PRINTF("(null)", "%s", null_str);
EXPECT_PRINTF(" (null)", "%10s", null_str); EXPECT_PRINTF(" (null)", "%10s", null_str);
EXPECT_PRINTF(L"abc", L"%s", L"abc"); EXPECT_PRINTF(L"abc", L"%s", L"abc");
const wchar_t *null_wstr = nullptr; const wchar_t *null_wstr = FMT_NULL;
EXPECT_PRINTF(L"(null)", L"%s", null_wstr); EXPECT_PRINTF(L"(null)", L"%s", null_wstr);
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr); EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
} }
@ -444,22 +450,22 @@ TEST(PrintfTest, Pointer) {
int n; int n;
void *p = &n; void *p = &n;
EXPECT_PRINTF(fmt::format("{}", p), "%p", p); EXPECT_PRINTF(fmt::format("{}", p), "%p", p);
p = nullptr; p = FMT_NULL;
EXPECT_PRINTF("(nil)", "%p", p); EXPECT_PRINTF("(nil)", "%p", p);
EXPECT_PRINTF(" (nil)", "%10p", p); EXPECT_PRINTF(" (nil)", "%10p", p);
const char *s = "test"; const char *s = "test";
EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s); EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s);
const char *null_str = nullptr; const char *null_str = FMT_NULL;
EXPECT_PRINTF("(nil)", "%p", null_str); EXPECT_PRINTF("(nil)", "%p", null_str);
p = &n; p = &n;
EXPECT_PRINTF(fmt::format(L"{}", p), L"%p", p); EXPECT_PRINTF(fmt::format(L"{}", p), L"%p", p);
p = nullptr; p = FMT_NULL;
EXPECT_PRINTF(L"(nil)", L"%p", p); EXPECT_PRINTF(L"(nil)", L"%p", p);
EXPECT_PRINTF(L" (nil)", L"%10p", p); EXPECT_PRINTF(L" (nil)", L"%10p", p);
const wchar_t *w = L"test"; const wchar_t *w = L"test";
EXPECT_PRINTF(fmt::format(L"{:p}", w), L"%p", w); EXPECT_PRINTF(fmt::format(L"{:p}", w), L"%p", w);
const wchar_t *null_wstr = nullptr; const wchar_t *null_wstr = FMT_NULL;
EXPECT_PRINTF(L"(nil)", L"%p", null_wstr); EXPECT_PRINTF(L"(nil)", L"%p", null_wstr);
} }
@ -495,7 +501,7 @@ TEST(PrintfTest, WideString) {
} }
TEST(PrintfTest, PrintfCustom) { TEST(PrintfTest, PrintfCustom) {
EXPECT_EQ("abc", fmt::sprintf("%s", TestString("abc"))); EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
} }
TEST(PrintfTest, OStream) { TEST(PrintfTest, OStream) {
@ -504,3 +510,60 @@ TEST(PrintfTest, OStream) {
EXPECT_EQ("Don't panic!", os.str()); EXPECT_EQ("Don't panic!", os.str());
EXPECT_EQ(12, ret); EXPECT_EQ(12, ret);
} }
TEST(PrintfTest, VPrintf) {
fmt::format_arg_store<fmt::printf_context, int> as{42};
fmt::basic_format_args<fmt::printf_context> args(as);
EXPECT_EQ(fmt::vsprintf("%d", args), "42");
EXPECT_WRITE(stdout, fmt::vprintf("%d", args), "42");
EXPECT_WRITE(stdout, fmt::vfprintf(stdout, "%d", args), "42");
EXPECT_WRITE(stdout, fmt::vfprintf(std::cout, "%d", args), "42");
}
template<typename... Args>
void check_format_string_regression(fmt::string_view s, const Args&... args) {
fmt::sprintf(s, args...);
}
TEST(PrintfTest, CheckFormatStringRegression) {
check_format_string_regression("%c%s", 'x', "");
}
TEST(PrintfTest, VSPrintfMakeArgsExample) {
fmt::format_arg_store<fmt::printf_context, int, const char *> as{
42, "something"};
fmt::basic_format_args<fmt::printf_context> args(as);
EXPECT_EQ(
"[42] something happened", fmt::vsprintf("[%d] %s happened", args));
auto as2 = fmt::make_printf_args(42, "something");
fmt::basic_format_args<fmt::printf_context> args2(as2);
EXPECT_EQ(
"[42] something happened", fmt::vsprintf("[%d] %s happened", args2));
//the older gcc versions can't cast the return value
#if !defined(__GNUC__) || (__GNUC__ > 4)
EXPECT_EQ(
"[42] something happened",
fmt::vsprintf(
"[%d] %s happened", fmt::make_printf_args(42, "something")));
#endif
}
TEST(PrintfTest, VSPrintfMakeWArgsExample) {
fmt::format_arg_store<fmt::wprintf_context, int, const wchar_t *> as{
42, L"something"};
fmt::basic_format_args<fmt::wprintf_context> args(as);
EXPECT_EQ(
L"[42] something happened",
fmt::vsprintf(L"[%d] %s happened", args));
auto as2 = fmt::make_wprintf_args(42, L"something");
fmt::basic_format_args<fmt::wprintf_context> args2(as2);
EXPECT_EQ(
L"[42] something happened", fmt::vsprintf(L"[%d] %s happened", args2));
// the older gcc versions can't cast the return value
#if !defined(__GNUC__) || (__GNUC__ > 4)
EXPECT_EQ(
L"[42] something happened",
fmt::vsprintf(
L"[%d] %s happened", fmt::make_wprintf_args(42, L"something")));
#endif
}

View file

@ -9,12 +9,11 @@
// All Rights Reserved // All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface. // {fmt} support for ranges, containers and types tuple interface.
#include "fmt/ranges.h"
/// 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)
#include "fmt/ranges.h"
#include "gtest.h" #include "gtest.h"
#include <vector> #include <vector>

View file

@ -10,6 +10,7 @@
#endif #endif
#include "gmock.h" #include "gmock.h"
#include "fmt/locale.h"
#include "fmt/time.h" #include "fmt/time.h"
TEST(TimeTest, Format) { TEST(TimeTest, Format) {
@ -26,10 +27,18 @@ TEST(TimeTest, GrowBuffer) {
for (int i = 0; i < 30; ++i) for (int i = 0; i < 30; ++i)
s += "%c"; s += "%c";
s += "}\n"; s += "}\n";
std::time_t t = std::time(nullptr); std::time_t t = std::time(FMT_NULL);
fmt::format(s, *std::localtime(&t)); fmt::format(s, *std::localtime(&t));
} }
TEST(TimeTest, FormatToEmptyContainer) {
std::string s;
auto time = std::tm();
time.tm_sec = 42;
fmt::format_to(std::back_inserter(s), "{:%S}", time);
EXPECT_EQ(s, "42");
}
TEST(TimeTest, EmptyResult) { TEST(TimeTest, EmptyResult) {
EXPECT_EQ("", fmt::format("{}", std::tm())); EXPECT_EQ("", fmt::format("{}", std::tm()));
} }
@ -47,13 +56,13 @@ static bool EqualTime(const std::tm &lhs, const std::tm &rhs) {
} }
TEST(TimeTest, LocalTime) { TEST(TimeTest, LocalTime) {
std::time_t t = std::time(nullptr); std::time_t t = std::time(FMT_NULL);
std::tm tm = *std::localtime(&t); std::tm tm = *std::localtime(&t);
EXPECT_TRUE(EqualTime(tm, fmt::localtime(t))); EXPECT_TRUE(EqualTime(tm, fmt::localtime(t)));
} }
TEST(TimeTest, GMTime) { TEST(TimeTest, GMTime) {
std::time_t t = std::time(nullptr); std::time_t t = std::time(FMT_NULL);
std::tm tm = *std::gmtime(&t); std::tm tm = *std::gmtime(&t);
EXPECT_TRUE(EqualTime(tm, fmt::gmtime(t))); EXPECT_TRUE(EqualTime(tm, fmt::gmtime(t)));
} }

View file

@ -35,7 +35,7 @@ std::string get_system_error(int error_code);
extern const char *const FILE_CONTENT; extern const char *const FILE_CONTENT;
// Opens a buffered file for reading. // Opens a buffered file for reading.
fmt::buffered_file open_buffered_file(FILE **fp = nullptr); fmt::buffered_file open_buffered_file(FILE **fp = FMT_NULL);
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__)