externals/fmt: Update fmt to 5.3.0

Merge commit '0066ad2d3879f4604b3a2644128cf5d447e54eef' into HEAD
This commit is contained in:
MerryMage 2020-04-22 21:00:18 +01:00
commit 51fe05a443
41 changed files with 3817 additions and 2059 deletions

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
externals/fmt/include/fmt/chrono.h vendored 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;
}
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 OR 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 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);
} }
inline void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args) { FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
char escape_fd[] = "\x1b[38;2;000;000;000m"; // foreground color return text_style(/*is_foreground=*/false, background);
char escape_bg[] = "\x1b[48;2;000;000;000m"; // background color }
internal::to_esc(fd.r, escape_fd, 7);
internal::to_esc(fd.g, escape_fd, 11);
internal::to_esc(fd.b, escape_fd, 15);
internal::to_esc(bg.r, escape_bg, 7); FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
internal::to_esc(bg.g, escape_bg, 11); return text_style(lhs) | rhs;
internal::to_esc(bg.b, escape_bg, 15); }
std::fputs(escape_fd, stdout); namespace internal {
std::fputs(escape_bg, stdout);
vprint(format, args); template <typename Char>
std::fputs(internal::data::RESET_COLOR, stdout); 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;
} }
}
struct fill {
size_t n;
void operator()(char *buf) const {
buf[0] = '0';
buf[1] = '.';
std::uninitialized_fill_n(buf + 2, n, '0');
} }
precision = 0; };
break;
} // The number is given as v = f * pow(10, exp), where f has size digits.
case 'F': template <typename Handler>
upper = true; FMT_FUNC void grisu2_prettify(const gen_digits_params &params,
FMT_FALLTHROUGH int size, int exp, Handler &&handler) {
case 'f': { if (!params.fixed) {
int digits_to_remove = -dec_exp - precision; // Insert a decimal point after the first digit and add an exponent.
if (digits_to_remove > 0) { handler.insert(1, '.');
if (digits_to_remove >= static_cast<int>(size)) exp += size - 1;
digits_to_remove = static_cast<int>(size) - 1; if (size < params.num_digits)
round(buffer, size, dec_exp, digits_to_remove); handler.append(params.num_digits - size, '0');
} handler.append(params.upper ? 'E' : 'e');
break; write_exponent(exp, handler);
}
case 'e': case 'E':
format_exp_notation(buffer, size, dec_exp, precision, type == 'E');
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
externals/fmt/include/fmt/locale.h vendored 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
externals/fmt/support/cmake/fmt.pc.in vendored 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
externals/fmt/support/rst2md.py vendored 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)
add_fmt_test(format-impl-test) if (NOT (MSVC AND BUILD_SHARED_LIBS))
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
externals/fmt/test/chrono-test.cc vendored 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);
@ -754,7 +792,7 @@ TEST(FormatterTest, ArgErrors) {
template <int N> template <int N>
struct TestFormat { struct TestFormat {
template <typename... Args> template <typename... Args>
static std::string format(fmt::string_view format_str, const Args & ... args) { static std::string format(fmt::string_view format_str, const Args &... args) {
return TestFormat<N - 1>::format(format_str, N - 1, args...); return TestFormat<N - 1>::format(format_str, N - 1, args...);
} }
}; };
@ -762,7 +800,7 @@ struct TestFormat {
template <> template <>
struct TestFormat<0> { struct TestFormat<0> {
template <typename... Args> template <typename... Args>
static std::string format(fmt::string_view format_str, const Args & ... args) { static std::string format(fmt::string_view format_str, const Args &... args) {
return fmt::format(format_str, args...); return fmt::format(format_str, args...);
} }
}; };
@ -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
externals/fmt/test/locale-test.cc vendored 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__)