Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Attila Mark 2020-06-07 16:46:58 -07:00
commit 689bd354f0
61 changed files with 3063 additions and 2711 deletions

2
.gitignore vendored
View File

@ -1,4 +1,5 @@
.vscode/
.vs/
*.iml
.idea/
@ -14,6 +15,7 @@ bin/
/CMakeScripts
/doc/doxyxml
/doc/html
/doc/node_modules
virtualenv
/Testing
/install_manifest.txt

View File

@ -44,6 +44,11 @@ if (MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
endif ()
include(GNUInstallDirs)
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING
"Installation directory for include files, a relative path "
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
OFF)
@ -54,7 +59,7 @@ option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
option(FMT_FUZZ "Generate the fuzz target." OFF)
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
project(FMT CXX)
# Get version from core.h
@ -79,6 +84,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
include(cxx14)
include(CheckCXXCompilerFlag)
include(JoinPaths)
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index)
if (${index} GREATER -1)
@ -174,7 +180,11 @@ endfunction()
# Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h
locale.h os.h ostream.h posix.h printf.h ranges.h)
set(FMT_SOURCES src/format.cc src/os.cc)
if (FMT_OS)
set(FMT_SOURCES src/format.cc src/os.cc)
else()
set(FMT_SOURCES src/format.cc)
endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt)
@ -194,7 +204,7 @@ target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
@ -203,8 +213,9 @@ set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
# Set FMT_LIB_NAME for pkg-config fmt.pc.
get_target_property(FMT_LIB_NAME fmt OUTPUT_NAME)
# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target
# property because it's not set by default.
set(FMT_LIB_NAME fmt)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(FMT_LIB_NAME ${FMT_LIB_NAME}${FMT_DEBUG_POSTFIX})
endif ()
@ -229,42 +240,36 @@ target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt-header-only INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
# Install targets.
if (FMT_INSTALL)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
"Installation directory for cmake files, relative to "
"${CMAKE_INSTALL_PREFIX}.")
"Installation directory for cmake files, a relative path "
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
set(targets_export_name fmt-targets)
set (INSTALL_TARGETS fmt)
if (TARGET fmt-header-only)
set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only)
endif ()
set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
"Installation directory for libraries, relative to "
"${CMAKE_INSTALL_PREFIX}.")
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
"Installation directory for include files, relative to "
"${CMAKE_INSTALL_PREFIX}.")
"Installation directory for libraries, a relative path "
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
"Installation directory for pkgconfig (.pc) files, relative to "
"${CMAKE_INSTALL_PREFIX}.")
"Installation directory for pkgconfig (.pc) files, a relative path "
"that will be joined to ${CMAKE_INSTALL_PREFIX}, or an arbitrary absolute path.")
# Generate the version, config and target files into the build directory.
write_basic_package_version_file(
${version_config}
VERSION ${FMT_VERSION}
COMPATIBILITY AnyNewerVersion)
join_paths(libdir_for_pc_file "\${exec_prefix}" "${FMT_LIB_DIR}")
join_paths(includedir_for_pc_file "\${prefix}" "${FMT_INC_DIR}")
configure_file(
"${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in"
"${pkgconfig}"
@ -273,6 +278,8 @@ if (FMT_INSTALL)
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
${project_config}
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
set(INSTALL_TARGETS fmt fmt-header-only)
# Use a namespace because CMake provides better diagnostics for namespaced
# imported targets.
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
@ -293,7 +300,7 @@ if (FMT_INSTALL)
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}/fmt")
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif ()
@ -309,6 +316,7 @@ endif ()
# Control fuzzing independent of the unit tests.
if (FMT_FUZZ)
add_subdirectory(test/fuzzing)
target_compile_definitions(fmt PUBLIC FMT_FUZZ)
endif ()
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)

View File

@ -1,3 +1,226 @@
6.2.1 - 2020-05-09
------------------
* Fixed ostream support in ``sprintf``
(`#1631 <https://github.com/fmtlib/fmt/issues/1631>`_).
* Fixed type detection when using implicit conversion to ``string_view`` and
ostream ``operator<<`` inconsistently
(`#1662 <https://github.com/fmtlib/fmt/issues/1662>`_).
6.2.0 - 2020-04-05
------------------
* Improved error reporting when trying to format an object of a non-formattable
type:
.. code:: c++
fmt::format("{}", S());
now gives::
include/fmt/core.h:1015:5: error: static_assert failed due to requirement
'formattable' "Cannot format argument. To make type T formattable provide a
formatter<T> specialization:
https://fmt.dev/latest/api.html#formatting-user-defined-types"
static_assert(
^
...
note: in instantiation of function template specialization
'fmt::v6::format<char [3], S, char>' requested here
fmt::format("{}", S());
^
if ``S`` is not formattable.
* Reduced library size by ~10%.
* Always print decimal point if ``#`` is specified
(`#1476 <https://github.com/fmtlib/fmt/issues/1476>`_,
`#1498 <https://github.com/fmtlib/fmt/issues/1498>`_):
.. code:: c++
fmt::print("{:#.0f}", 42.0);
now prints ``42.``
* Implemented the ``'L'`` specifier for locale-specific numeric formatting to
improve compatibility with ``std::format``. The ``'n'`` specifier is now
deprecated and will be removed in the next major release.
* Moved OS-specific APIs such as ``windows_error`` from ``fmt/format.h`` to
``fmt/os.h``. You can define ``FMT_DEPRECATED_INCLUDE_OS`` to automatically
include ``fmt/os.h`` from ``fmt/format.h`` for compatibility but this will be
disabled in the next major release.
* Added precision overflow detection in floating-point formatting.
* Implemented detection of invalid use of ``fmt::arg``.
* Used ``type_identity`` to block unnecessary template argument deduction.
Thanks Tim Song.
* Improved UTF-8 handling
(`#1109 <https://github.com/fmtlib/fmt/issues/1109>`_):
.. code:: c++
fmt::print("┌{0:─^{2}}┐\n"
"│{1: ^{2}}│\n"
"└{0:─^{2}}┘\n", "", "Привет, мир!", 20);
now prints::
┌────────────────────┐
│ Привет, мир! │
└────────────────────┘
on systems that support Unicode.
* Added experimental dynamic argument storage
(`#1170 <https://github.com/fmtlib/fmt/issues/1170>`_,
`#1584 <https://github.com/fmtlib/fmt/pull/1584>`_):
.. code:: c++
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back("answer");
store.push_back(42);
fmt::vprint("The {} is {}.\n", store);
prints::
The answer is 42.
Thanks `@vsolontsov-ll (Vladimir Solontsov)
<https://github.com/vsolontsov-ll>`_.
* Made ``fmt::join`` accept ``initializer_list``
(`#1591 <https://github.com/fmtlib/fmt/pull/1591>`_).
Thanks `@Rapotkinnik (Nikolay Rapotkin) <https://github.com/Rapotkinnik>`_.
* Fixed handling of empty tuples
(`#1588 <https://github.com/fmtlib/fmt/issues/1588>`_).
* Fixed handling of output iterators in ``format_to_n``
(`#1506 <https://github.com/fmtlib/fmt/issues/1506>`_).
* Fixed formatting of ``std::chrono::duration`` types to wide output
(`#1533 <https://github.com/fmtlib/fmt/pull/1533>`_).
Thanks `@zeffy (pilao) <https://github.com/zeffy>`_.
* Added const ``begin`` and ``end`` overload to buffers
(`#1553 <https://github.com/fmtlib/fmt/pull/1553>`_).
Thanks `@dominicpoeschko <https://github.com/dominicpoeschko>`_.
* Added the ability to disable floating-point formatting via ``FMT_USE_FLOAT``,
``FMT_USE_DOUBLE`` and ``FMT_USE_LONG_DOUBLE`` macros for extremely
memory-constrained embedded system
(`#1590 <https://github.com/fmtlib/fmt/pull/1590>`_).
Thanks `@albaguirre (Alberto Aguirre) <https://github.com/albaguirre>`_.
* Made ``FMT_STRING`` work with ``constexpr`` ``string_view``
(`#1589 <https://github.com/fmtlib/fmt/pull/1589>`_).
Thanks `@scramsby (Scott Ramsby) <https://github.com/scramsby>`_.
* Implemented a minor optimization in the format string parser
(`#1560 <https://github.com/fmtlib/fmt/pull/1560>`_).
Thanks `@IkarusDeveloper <https://github.com/IkarusDeveloper>`_.
* Improved attribute detection
(`#1469 <https://github.com/fmtlib/fmt/pull/1469>`_,
`#1475 <https://github.com/fmtlib/fmt/pull/1475>`_,
`#1576 <https://github.com/fmtlib/fmt/pull/1576>`_).
Thanks `@federico-busato (Federico) <https://github.com/federico-busato>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@refnum <https://github.com/refnum>`_.
* Improved documentation
(`#1481 <https://github.com/fmtlib/fmt/pull/1481>`_,
`#1523 <https://github.com/fmtlib/fmt/pull/1523>`_).
Thanks `@JackBoosY (Jack·Boos·Yu) <https://github.com/JackBoosY>`_,
`@imba-tjd (谭九鼎) <https://github.com/imba-tjd>`_.
* Fixed symbol visibility on Linux when compiling with ``-fvisibility=hidden``
(`#1535 <https://github.com/fmtlib/fmt/pull/1535>`_).
Thanks `@milianw (Milian Wolff) <https://github.com/milianw>`_.
* Implemented various build configuration fixes and improvements
(`#1264 <https://github.com/fmtlib/fmt/issues/1264>`_,
`#1460 <https://github.com/fmtlib/fmt/issues/1460>`_,
`#1534 <https://github.com/fmtlib/fmt/pull/1534>`_,
`#1536 <https://github.com/fmtlib/fmt/issues/1536>`_,
`#1545 <https://github.com/fmtlib/fmt/issues/1545>`_,
`#1546 <https://github.com/fmtlib/fmt/pull/1546>`_,
`#1566 <https://github.com/fmtlib/fmt/issues/1566>`_,
`#1582 <https://github.com/fmtlib/fmt/pull/1582>`_,
`#1597 <https://github.com/fmtlib/fmt/issues/1597>`_,
`#1598 <https://github.com/fmtlib/fmt/pull/1598>`_).
Thanks `@ambitslix (Attila M. Szilagyi) <https://github.com/ambitslix>`_,
`@jwillikers (Jordan Williams) <https://github.com/jwillikers>`_,
`@stac47 (Laurent Stacul) <https://github.com/stac47>`_.
* Fixed various warnings and compilation issues
(`#1433 <https://github.com/fmtlib/fmt/pull/1433>`_,
`#1461 <https://github.com/fmtlib/fmt/issues/1461>`_,
`#1470 <https://github.com/fmtlib/fmt/pull/1470>`_,
`#1480 <https://github.com/fmtlib/fmt/pull/1480>`_,
`#1485 <https://github.com/fmtlib/fmt/pull/1485>`_,
`#1492 <https://github.com/fmtlib/fmt/pull/1492>`_,
`#1493 <https://github.com/fmtlib/fmt/issues/1493>`_,
`#1504 <https://github.com/fmtlib/fmt/issues/1504>`_,
`#1505 <https://github.com/fmtlib/fmt/pull/1505>`_,
`#1512 <https://github.com/fmtlib/fmt/pull/1512>`_,
`#1515 <https://github.com/fmtlib/fmt/issues/1515>`_,
`#1516 <https://github.com/fmtlib/fmt/pull/1516>`_,
`#1518 <https://github.com/fmtlib/fmt/pull/1518>`_,
`#1519 <https://github.com/fmtlib/fmt/pull/1519>`_,
`#1520 <https://github.com/fmtlib/fmt/pull/1520>`_,
`#1521 <https://github.com/fmtlib/fmt/pull/1521>`_,
`#1522 <https://github.com/fmtlib/fmt/pull/1522>`_,
`#1524 <https://github.com/fmtlib/fmt/issues/1524>`_,
`#1530 <https://github.com/fmtlib/fmt/pull/1530>`_,
`#1531 <https://github.com/fmtlib/fmt/issues/1531>`_,
`#1532 <https://github.com/fmtlib/fmt/pull/1532>`_,
`#1539 <https://github.com/fmtlib/fmt/issues/1539>`_,
`#1547 <https://github.com/fmtlib/fmt/issues/1547>`_,
`#1548 <https://github.com/fmtlib/fmt/issues/1548>`_,
`#1554 <https://github.com/fmtlib/fmt/pull/1554>`_,
`#1567 <https://github.com/fmtlib/fmt/issues/1567>`_,
`#1568 <https://github.com/fmtlib/fmt/pull/1568>`_,
`#1569 <https://github.com/fmtlib/fmt/pull/1569>`_,
`#1571 <https://github.com/fmtlib/fmt/pull/1571>`_,
`#1573 <https://github.com/fmtlib/fmt/pull/1573>`_,
`#1575 <https://github.com/fmtlib/fmt/pull/1575>`_,
`#1581 <https://github.com/fmtlib/fmt/pull/1581>`_,
`#1583 <https://github.com/fmtlib/fmt/issues/1583>`_,
`#1586 <https://github.com/fmtlib/fmt/issues/1586>`_,
`#1587 <https://github.com/fmtlib/fmt/issues/1587>`_,
`#1594 <https://github.com/fmtlib/fmt/issues/1594>`_,
`#1596 <https://github.com/fmtlib/fmt/pull/1596>`_,
`#1604 <https://github.com/fmtlib/fmt/issues/1604>`_,
`#1606 <https://github.com/fmtlib/fmt/pull/1606>`_,
`#1607 <https://github.com/fmtlib/fmt/issues/1607>`_,
`#1609 <https://github.com/fmtlib/fmt/issues/1609>`_).
Thanks `@marti4d (Chris Martin) <https://github.com/marti4d>`_,
`@iPherian <https://github.com/iPherian>`_,
`@parkertomatoes <https://github.com/parkertomatoes>`_,
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_,
`@torsten48 <https://github.com/torsten48>`_,
`@tohammer (Tobias Hammer) <https://github.com/tohammer>`_,
`@lefticus (Jason Turner) <https://github.com/lefticus>`_,
`@ryusakki (Haise) <https://github.com/ryusakki>`_,
`@adnsv (Alex Denisov) <https://github.com/adnsv>`_,
`@fghzxm <https://github.com/fghzxm>`_,
`@refnum <https://github.com/refnum>`_,
`@pramodk (Pramod Kumbhar) <https://github.com/pramodk>`_,
`@Spirrwell <https://github.com/Spirrwell>`_,
`@scramsby (Scott Ramsby) <https://github.com/scramsby>`_.
6.1.2 - 2019-12-11
------------------

View File

@ -264,8 +264,8 @@ or the bloat test::
Projects using this library
---------------------------
* `0 A.D. <https://play0ad.com/>`_: A free, open-source, cross-platform real-time
strategy game
* `0 A.D. <https://play0ad.com/>`_: A free, open-source, cross-platform
real-time strategy game
* `AMPL/MP <https://github.com/ampl/mp>`_:
An open-source library for mathematical programming
@ -282,6 +282,16 @@ Projects using this library
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
vehicle
* `Drake <https://drake.mit.edu/>`_: A planning, control, and analysis toolbox
for nonlinear dynamical systems (MIT)
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
(Lyft)
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
Player vs Player Gaming Network with tweaks
@ -291,27 +301,24 @@ Projects using this library
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): Home theater software
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game
* `Knuth <https://kth.cash/>`_: High-performance Bitcoin full-node
* `Drake <https://drake.mit.edu/>`_: A planning, control, and analysis toolbox
for nonlinear dynamical systems (MIT)
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
(Lyft)
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
* `Microsoft Verona <https://github.com/microsoft/verona>`_: Research programming language for concurrent ownership
* `MongoDB <https://mongodb.com/>`_: Distributed document database
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
generate randomized datasets
* `OpenSpace <https://openspaceproject.com/>`_: An open-source astrovisualization
framework
* `OpenSpace <https://openspaceproject.com/>`_: An open-source
astrovisualization framework
* `PenUltima Online (POL) <https://www.polserver.com/>`_:
An MMO server, compatible with most Ultima Online clients
* `PyTorch <https://github.com/pytorch/pytorch>`_: An open-source machine
learning library
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
associative database
@ -320,13 +327,14 @@ Projects using this library
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster
proxy
* `redpanda <https://vectorized.io/redpanda>`_: A 10x faster Kafka® replacement
for mission critical systems written in C++
* `rpclib <http://rpclib.net/>`_: A modern C++ msgpack-RPC server and client
library
* `Saddy <https://github.com/mamontov-cpp/saddy-graphics-engine-2d>`_:
Small crossplatform 2D graphic engine
* `Salesforce Analytics Cloud <https://www.salesforce.com/analytics-cloud/overview/>`_:
* `Salesforce Analytics Cloud
<https://www.salesforce.com/analytics-cloud/overview/>`_:
Business intelligence software
* `Scylla <https://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store
@ -344,6 +352,9 @@ Projects using this library
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
MMORPG framework
* `Windows Terminal <https://github.com/microsoft/terminal>`_: The new Windows
Terminal
`More... <https://github.com/search?q=fmtlib&type=Code>`_
If you are aware of other projects using this library, please let me know

View File

@ -9,4 +9,5 @@ add_custom_target(doc
SOURCES api.rst syntax.rst usage.rst build.py conf.py _templates/layout.html)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
DESTINATION share/doc/fmt OPTIONAL)
DESTINATION share/doc/fmt OPTIONAL
PATTERN ".doctrees" EXCLUDE)

View File

@ -6,14 +6,13 @@
<meta name="author" content="Victor Zverovich">
<link rel="stylesheet" href="_static/fmt.css">
{# Google Analytics #}
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-20116650-4"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();
a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;
a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-20116650-4', 'fmtlib.net');
ga('send', 'pageview');
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-20116650-4');
</script>
{% endblock %}

View File

@ -9,7 +9,8 @@ The {fmt} library API consists of the following parts:
* :ref:`fmt/core.h <core-api>`: the core API providing argument handling
facilities and a lightweight subset of formatting functions
* :ref:`fmt/format.h <format-api>`: the full format API providing compile-time
format string checks, output iterator and user-defined type support
format string checks, wide string, output iterator and user-defined type
support
* :ref:`fmt/ranges.h <ranges-api>`: additional formatting support for ranges
and tuples
* :ref:`fmt/chrono.h <chrono-api>`: date and time formatting
@ -43,7 +44,7 @@ participate in an overload resolution if the latter is not a string.
.. _format:
.. doxygenfunction:: format(const S&, Args&&...)
.. doxygenfunction:: vformat(const S&, basic_format_args<buffer_context<Char>>)
.. doxygenfunction:: vformat(const S&, basic_format_args<buffer_context<type_identity_t<Char>>>)
.. _print:
@ -68,6 +69,9 @@ Argument Lists
.. doxygenclass:: fmt::format_arg_store
:members:
.. doxygenclass:: fmt::dynamic_format_arg_store
:members:
.. doxygenclass:: fmt::basic_format_args
:members:
@ -96,7 +100,7 @@ locale::
#include <locale>
std::locale::global(std::locale("en_US.UTF-8"));
auto s = fmt::format("{:n}", 1000000); // s == "1,000,000"
auto s = fmt::format("{:L}", 1000000); // s == "1,000,000"
.. _format-api:
@ -104,7 +108,7 @@ Format API
==========
``fmt/format.h`` defines the full format API providing compile-time format
string checks, output iterator and user-defined type support.
string checks, wide string, output iterator and user-defined type support.
Compile-time Format String Checks
---------------------------------
@ -178,8 +182,7 @@ example::
enum class color {red, green, blue};
template <>
struct fmt::formatter<color>: formatter<string_view> {
template <> struct fmt::formatter<color>: formatter<string_view> {
// parse is inherited from formatter<string_view>.
template <typename FormatContext>
auto format(color c, FormatContext& ctx) {
@ -193,6 +196,15 @@ example::
}
};
Since ``parse`` is inherited from ``formatter<string_view>`` it will recognize
all string format specifications, for example
.. code-block:: c++
fmt::format("{:>10}", color::blue)
will return ``" blue"``.
You can also write a formatter for a hierarchy of classes::
#include <type_traits>
@ -238,9 +250,9 @@ Literal-based API
The following user-defined literals are defined in ``fmt/format.h``.
.. doxygenfunction:: operator""_format(const char *, std::size_t)
.. doxygenfunction:: operator""_format(const char *, size_t)
.. doxygenfunction:: operator""_a(const char *, std::size_t)
.. doxygenfunction:: operator""_a(const char *, size_t)
Utilities
---------
@ -261,6 +273,9 @@ Utilities
.. doxygenfunction:: fmt::join(It, It, string_view)
.. doxygenclass:: fmt::detail::buffer
:members:
.. doxygenclass:: fmt::basic_memory_buffer
:protected-members:
:members:
@ -399,7 +414,7 @@ formatting::
std::time_t t = std::time(nullptr);
// Prints "The date is 2016-04-29." (with the current date)
fmt::print("The date is {:%Y-%m-%d}.", *std::localtime(&t));
fmt::print("The date is {:%Y-%m-%d}.", fmt::localtime(t));
The format string syntax is described in the documentation of
`strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_.

View File

@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
from distutils.version import LooseVersion
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2']
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', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1']
def pip_install(package, commit=None, **kwargs):
"Install package using pip."

View File

@ -79,10 +79,10 @@ The general form of a *standard format specifier* is:
fill: <a character other than '{' or '}'>
align: "<" | ">" | "^"
sign: "+" | "-" | " "
width: `integer` | "{" `arg_id` "}"
precision: `integer` | "{" `arg_id` "}"
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "p" | "s"
int_type: "b" | "B" | "d" | "n" | "o" | "x" | "X"
width: `integer` | "{" [`arg_id`] "}"
precision: `integer` | "{" [`arg_id`] "}"
type: `int_type` | "a" | "A" | "c" | "e" | "E" | "f" | "F" | "g" | "G" | "L" | "p" | "s"
int_type: "b" | "B" | "d" | "o" | "x" | "X"
The *fill* character can be any Unicode code point other than ``'{'`` or
``'}'``. The presence of a fill character is signaled by the character following
@ -143,7 +143,7 @@ conversions, trailing zeros are not removed from the result.
.. ifconfig:: False
The ``','`` option signals the use of a comma for a thousands separator.
For a locale aware separator, use the ``'n'`` integer presentation type
For a locale aware separator, use the ``'L'`` integer presentation type
instead.
*width* is a decimal integer defining the minimum field width. If not
@ -214,9 +214,9 @@ The available integer presentation types are:
| | ``'#'`` option with this type adds the prefix ``"0X"`` |
| | to the output value. |
+---------+----------------------------------------------------------+
| ``'n'`` | Number. This is the same as ``'d'``, except that it uses |
| | the current locale setting to insert the appropriate |
| | number separator characters. |
| ``'L'`` | Locale-specific format. This is the same as ``'d'``, |
| | except that it uses the current locale setting to insert |
| | the appropriate number separator characters. |
+---------+----------------------------------------------------------+
| none | The same as ``'d'``. |
+---------+----------------------------------------------------------+
@ -261,9 +261,9 @@ The available presentation types for floating-point values are:
| | ``'E'`` if the number gets too large. The |
| | representations of infinity and NaN are uppercased, too. |
+---------+----------------------------------------------------------+
| ``'n'`` | Number. This is the same as ``'g'``, except that it uses |
| | the current locale setting to insert the appropriate |
| | number separator characters. |
| ``'L'`` | Locale-specific format. This is the same as ``'g'``, |
| | except that it uses the current locale setting to insert |
| | the appropriate number separator characters. |
+---------+----------------------------------------------------------+
| none | Similar to ``'g'``, except that fixed-point notation, |
| | when used, has at least one digit past the decimal |
@ -403,7 +403,7 @@ Using the comma as a thousands separator::
#include <fmt/locale.h>
auto s = fmt::format(std::locale("en_US.UTF-8"), "{:n}", 1234567890);
auto s = fmt::format(std::locale("en_US.UTF-8"), "{:L}", 1234567890);
// s == "1,234,567,890"
.. ifconfig:: False

View File

@ -48,7 +48,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
if (from < (T::min)() || from > (T::max)()) {
// outside range.
ec = 1;
return {};
@ -74,7 +74,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (fmt::internal::is_negative(from)) {
if (fmt::detail::is_negative(from)) {
ec = 1;
return {};
}
@ -84,7 +84,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
if (from > static_cast<From>((T::max)())) {
ec = 1;
return {};
}
@ -97,7 +97,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
if (from > static_cast<From>((T::max)())) {
// outside range.
ec = 1;
return {};
@ -141,7 +141,7 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
// catch the only happy case
if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) {
if (from >= T::lowest() && from <= (T::max)()) {
return static_cast<To>(from);
}
// not within range.
@ -195,12 +195,13 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
const auto min1 =
(std::numeric_limits<IntermediateRep>::min)() / Factor::num;
if (count < min1) {
ec = 1;
return {};
@ -269,7 +270,7 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = internal::max_value<IntermediateRep>() /
constexpr auto max1 = detail::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
@ -306,12 +307,12 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// Usage: f FMT_NOMACRO()
#define FMT_NOMACRO
namespace internal {
namespace detail {
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); }
} // namespace internal
} // namespace detail
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
@ -322,22 +323,22 @@ inline std::tm localtime(std::time_t time) {
dispatcher(std::time_t t) : time_(t) {}
bool run() {
using namespace fmt::internal;
using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) {
using namespace fmt::internal;
bool handle(detail::null<>) {
using namespace fmt::detail;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
using namespace fmt::internal;
bool fallback(detail::null<>) {
using namespace fmt::detail;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
@ -359,21 +360,21 @@ inline std::tm gmtime(std::time_t time) {
dispatcher(std::time_t t) : time_(t) {}
bool run() {
using namespace fmt::internal;
using namespace fmt::detail;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) {
using namespace fmt::internal;
bool handle(detail::null<>) {
using namespace fmt::detail;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
bool fallback(detail::null<>) {
std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
@ -386,17 +387,17 @@ inline std::tm gmtime(std::time_t time) {
return gt.tm_;
}
namespace internal {
inline std::size_t strftime(char* str, std::size_t count, const char* format,
namespace detail {
inline size_t strftime(char* str, size_t count, const char* format,
const std::tm* time) {
return std::strftime(str, count, format, time);
}
inline std::size_t strftime(wchar_t* str, std::size_t count,
const wchar_t* format, const std::tm* time) {
inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
const std::tm* time) {
return std::wcsftime(str, count, format, time);
}
} // namespace internal
} // namespace detail
template <typename Char> struct formatter<std::tm, Char> {
template <typename ParseContext>
@ -405,7 +406,7 @@ template <typename Char> struct formatter<std::tm, Char> {
if (it != ctx.end() && *it == ':') ++it;
auto end = it;
while (end != ctx.end() && *end != '}') ++end;
tm_format.reserve(internal::to_unsigned(end - it + 1));
tm_format.reserve(detail::to_unsigned(end - it + 1));
tm_format.append(it, end);
tm_format.push_back('\0');
return end;
@ -414,11 +415,10 @@ template <typename Char> struct formatter<std::tm, Char> {
template <typename FormatContext>
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buf;
std::size_t start = buf.size();
size_t start = buf.size();
for (;;) {
std::size_t size = buf.capacity() - start;
std::size_t count =
internal::strftime(&buf[start], size, &tm_format[0], &tm);
size_t size = buf.capacity() - start;
size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) {
buf.resize(start + count);
break;
@ -430,7 +430,7 @@ template <typename Char> struct formatter<std::tm, Char> {
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
const size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
return std::copy(buf.begin(), buf.end(), ctx.out());
@ -439,7 +439,7 @@ template <typename Char> struct formatter<std::tm, Char> {
basic_memory_buffer<Char> tm_format;
};
namespace internal {
namespace detail {
template <typename Period> FMT_CONSTEXPR const char* get_units() {
return nullptr;
}
@ -768,17 +768,23 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
val);
}
template <typename Char, typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
return std::copy(unit.begin(), unit.end(), out);
}
template <typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
// This works when wchar_t is UTF-32 because units only contain characters
// that have the same representation in UTF-16 and UTF-32.
utf8_to_utf16 u(unit);
return std::copy(u.c_str(), u.c_str() + u.size(), out);
}
template <typename Char, typename Period, typename OutputIt>
OutputIt format_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>()) {
string_view s(unit);
if (const_check(std::is_same<Char, wchar_t>())) {
utf8_to_utf16 u(s);
return std::copy(u.c_str(), u.c_str() + u.size(), out);
}
return std::copy(s.begin(), s.end(), out);
}
if (const char* unit = get_units<Period>())
return copy_unit(string_view(unit), out, Char());
const Char num_f[] = {'[', '{', '}', ']', 's', 0};
if (Period::den == 1) return format_to(out, num_f, Period::num);
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0};
@ -874,7 +880,7 @@ struct chrono_formatter {
if (isnan(value)) return write_nan();
uint32_or_64_or_128_t<int> n =
to_unsigned(to_nonnegative_int(value, max_value<int>()));
int num_digits = internal::count_digits(n);
int num_digits = detail::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);
}
@ -1004,14 +1010,14 @@ struct chrono_formatter {
out = format_duration_unit<char_type, Period>(out);
}
};
} // namespace internal
} // namespace detail
template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> {
private:
basic_format_specs<Char> specs;
int precision;
using arg_ref_type = internal::arg_ref<Char>;
using arg_ref_type = detail::arg_ref<Char>;
arg_ref_type width_ref;
arg_ref_type precision_ref;
mutable basic_string_view<Char> format_str;
@ -1032,7 +1038,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
return arg_ref_type(context.next_arg_id());
}
@ -1062,17 +1068,17 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx, format_str};
begin = internal::parse_align(begin, end, handler);
begin = detail::parse_align(begin, end, handler);
if (begin == end) return {begin, begin};
begin = internal::parse_width(begin, end, handler);
begin = detail::parse_width(begin, end, handler);
if (begin == end) return {begin, begin};
if (*begin == '.') {
if (std::is_floating_point<Rep>::value)
begin = internal::parse_precision(begin, end, handler);
begin = detail::parse_precision(begin, end, handler);
else
handler.on_error("precision not allowed for this argument type");
}
end = parse_chrono_format(begin, end, internal::chrono_format_checker());
end = parse_chrono_format(begin, end, detail::chrono_format_checker());
return {begin, end};
}
@ -1083,7 +1089,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
-> decltype(ctx.begin()) {
auto range = do_parse(ctx);
format_str = basic_string_view<Char>(
&*range.begin, internal::to_unsigned(range.end - range.begin));
&*range.begin, detail::to_unsigned(range.end - range.begin));
return range.end;
}
@ -1094,23 +1100,21 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
// is not specified.
basic_memory_buffer<Char> buf;
auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>;
internal::basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>(specs.width,
width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx);
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref,
ctx);
detail::handle_dynamic_spec<detail::precision_checker>(precision,
precision_ref, ctx);
if (begin == end || *begin == '}') {
out = internal::format_duration_value<Char>(out, d.count(), precision);
internal::format_duration_unit<Char, Period>(out);
out = detail::format_duration_value<Char>(out, d.count(), precision);
detail::format_duration_unit<Char, Period>(out);
} else {
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d);
f.precision = precision;
parse_chrono_format(begin, end, f);
}
w.write(buf.data(), buf.size(), specs);
return w.out();
return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
};

View File

@ -198,7 +198,7 @@ struct rgb {
uint8_t b;
};
namespace internal {
namespace detail {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
@ -221,7 +221,7 @@ struct color_type {
uint32_t rgb_color;
} value;
};
} // namespace internal
} // namespace detail
// Experimental text formatting support.
class text_style {
@ -298,11 +298,11 @@ class text_style {
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 {
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
@ -313,7 +313,7 @@ class text_style {
private:
FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT
detail::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems() {
@ -326,23 +326,23 @@ class text_style {
}
}
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
FMT_NOEXCEPT;
internal::color_type foreground_color;
internal::color_type background_color;
detail::color_type foreground_color;
detail::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 {
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/true, foreground);
}
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/false, background);
}
@ -350,21 +350,21 @@ FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs;
}
namespace internal {
namespace detail {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
FMT_CONSTEXPR ansi_color_escape(detail::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;
bool is_background = esc == detail::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;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
@ -398,7 +398,7 @@ template <typename Char> struct ansi_color_escape {
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9;
std::size_t index = 0;
size_t index = 0;
for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
@ -429,14 +429,14 @@ template <typename Char> struct ansi_color_escape {
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);
detail::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, detail::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);
detail::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, detail::data::background_color);
}
template <typename Char>
@ -455,11 +455,11 @@ inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
}
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::reset_color, stream);
fputs(detail::data::reset_color, stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::wreset_color, stream);
fputs(detail::data::wreset_color, stream);
}
template <typename Char>
@ -476,33 +476,31 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
auto foreground =
internal::make_foreground_color<Char>(ts.get_foreground());
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
auto background =
internal::make_background_color<Char>(ts.get_background());
auto background = detail::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
internal::vformat_to(buf, format_str, args);
if (has_style) internal::reset_color<Char>(buf);
detail::vformat_to(buf, format_str, args);
if (has_style) detail::reset_color<Char>(buf);
}
} // namespace internal
} // namespace detail
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format), args);
detail::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
internal::fputs(buf.data(), f);
detail::fputs(buf.data(), f);
}
/**
@ -513,10 +511,10 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
internal::check_format_string<Args...>(format_str);
detail::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S>>;
format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as));
@ -530,7 +528,7 @@ void print(std::FILE* f, const text_style& ts, const S& format_str,
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
}
@ -540,7 +538,7 @@ inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args);
detail::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
}
@ -560,7 +558,7 @@ template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return vformat(ts, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
detail::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@ -13,7 +13,7 @@
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
// Part of a compiled format string. It can be either literal text or a
// replacement field.
@ -62,13 +62,15 @@ template <typename Char> struct part_counter {
if (begin != end) ++num_parts;
}
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
return ++num_parts, 0;
}
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned brace_counter = 0;
@ -116,25 +118,28 @@ class format_string_compiler : public error_handler {
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
FMT_CONSTEXPR void on_arg_id() {
FMT_CONSTEXPR int on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id());
return 0;
}
FMT_CONSTEXPR void on_arg_id(int id) {
FMT_CONSTEXPR int on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
return 0;
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
return 0;
}
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
@ -160,23 +165,24 @@ FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
template <typename Range, typename Context, typename Id>
template <typename OutputIt, typename Context, typename Id>
void format_arg(
basic_format_parse_context<typename Range::value_type>& parse_ctx,
basic_format_parse_context<typename Context::char_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
ctx.advance_to(visit_format_arg(
arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx),
ctx.arg(arg_id)));
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace cf {
template <typename Context, typename Range, typename CompiledFormat>
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
-> typename Context::iterator {
template <typename Context, typename OutputIt, typename CompiledFormat>
auto vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) -> typename Context::iterator {
using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out.begin(), args);
Context ctx(out, args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
@ -197,12 +203,12 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.str);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
@ -226,7 +232,9 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
ctx, nullptr, &specs),
arg));
break;
}
}
@ -240,7 +248,7 @@ struct basic_compiled_format {};
template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>;
using parts_container = std::vector<internal::format_part<char_type>>;
using parts_container = std::vector<detail::format_part<char_type>>;
parts_container compiled_parts;
@ -305,7 +313,7 @@ struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>(
internal::to_string_view(S()));
detail::to_string_view(S()));
return compiled_parts.data;
}
};
@ -318,8 +326,8 @@ class compiled_format : private compiled_format_base<S> {
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename Range, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
template <typename Context, typename OutputIt, typename CompiledFormat>
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
@ -383,9 +391,7 @@ OutputIt format_default(OutputIt out, T value) {
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) {
writer w(out);
w.write(value);
return w.out();
return detail::write(out, value);
}
template <typename Char, typename OutputIt>
@ -495,7 +501,7 @@ constexpr auto compile_format_string(S format_str) {
}
}
#endif // __cpp_if_constexpr
} // namespace internal
} // namespace detail
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
@ -504,14 +510,14 @@ template <typename... Args, typename S,
constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str;
if constexpr (str.size() == 0) {
return internal::make_text(str, 0, 0);
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
internal::unknown_format>()) {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
detail::unknown_format>()) {
return detail::compiled_format<S, Args...>(to_string_view(format_str));
} else {
return result;
}
@ -520,7 +526,7 @@ constexpr auto compile(S format_str) {
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
cf.format(std::back_inserter(buffer), args...);
@ -528,7 +534,7 @@ std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
@ -536,8 +542,8 @@ OutputIt format_to(OutputIt out, const CompiledFormat& cf,
# else
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
return detail::compiled_format<S, Args...>(to_string_view(format_str));
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
@ -545,49 +551,50 @@ constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
// Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N])
-> internal::compiled_format<const Char*, Args...> {
return internal::compiled_format<const Char*, Args...>(
-> detail::compiled_format<const Char*, Args...> {
return detail::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
internal::cf::vformat_to<context>(range(buffer), cf,
detail::buffer<Char>& base = buffer;
detail::cf::vformat_to<context>(std::back_inserter(base), cf,
make_format_args<context>(args...));
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>;
return internal::cf::vformat_to<context>(range(out), cf,
return detail::cf::vformat_to<context>(out, cf,
make_format_args<context>(args...));
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
template <
typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& std::is_base_of<
detail::basic_compiled_format, CompiledFormat>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const CompiledFormat& cf,
const Args&... args) {
auto it =
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()};
}
template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(internal::counting_iterator(), cf, args...).count();
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(detail::counting_iterator(), cf, args...).count();
}
FMT_END_NAMESPACE

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@
#include <cstdarg>
#include <cstring> // for std::memmove
#include <cwchar>
#include <exception>
#include "format.h"
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
@ -22,8 +23,14 @@
#endif
#ifdef _WIN32
# include <io.h>
# if defined(NOMINMAX)
# include <windows.h>
# else
# define NOMINMAX
# include <windows.h>
# undef NOMINMAX
# endif
# include <io.h>
#endif
#ifdef _MSC_VER
@ -33,15 +40,17 @@
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
inline fmt::internal::null<> strerror_r(int, char*, ...) { return {}; }
inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) { return {}; }
inline fmt::detail::null<> strerror_r(int, char*, ...) { return {}; }
inline fmt::detail::null<> strerror_s(char*, size_t, ...) { return {}; }
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
print(stderr, "{}:{}: assertion failed: {}", file, line, message);
std::abort();
// Chosen instead of std::abort to satisfy Clang in CUDA mode during device
// code pass.
std::terminate();
}
#ifndef _MSC_VER
@ -67,14 +76,14 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
// other - failure
// Buffer should be at least of size 1.
FMT_FUNC int safe_strerror(int error_code, char*& buffer,
std::size_t buffer_size) FMT_NOEXCEPT {
size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer");
class dispatcher {
private:
int error_code_;
char*& buffer_;
std::size_t buffer_size_;
size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings.
void operator=(const dispatcher&) {}
@ -97,7 +106,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
// Handle the case when strerror_r is not available.
FMT_MAYBE_UNUSED
int handle(internal::null<>) {
int handle(detail::null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
@ -111,7 +120,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
#if !FMT_MSC_VER
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::null<>) {
int fallback(detail::null<>) {
errno = 0;
buffer_ = strerror(error_code_);
return errno;
@ -119,7 +128,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
#endif
public:
dispatcher(int err_code, char*& buf, std::size_t buf_size)
dispatcher(int err_code, char*& buf, size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); }
@ -127,7 +136,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
return dispatcher(error_code, buffer, buffer_size).run();
}
FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT {
// Report error code making sure that the output fits into
// inline_buffer_size to avoid dynamic memory allocation and potential
@ -136,20 +145,17 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
if (internal::is_negative(error_code)) {
if (detail::is_negative(error_code)) {
abs_value = 0 - abs_value;
++error_code_size;
}
error_code_size += internal::to_unsigned(internal::count_digits(abs_value));
internal::writer w(out);
if (message.size() <= inline_buffer_size - error_code_size) {
w.write(message);
w.write(SEP);
}
w.write(ERROR_STR);
w.write(error_code);
error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
auto it = std::back_inserter(out);
if (message.size() <= inline_buffer_size - error_code_size)
format_to(it, "{}{}", message, SEP);
format_to(it, "{}{}", ERROR_STR, error_code);
assert(out.size() <= inline_buffer_size);
}
@ -168,10 +174,10 @@ FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
size_t written = std::fwrite(ptr, size, count, stream);
if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
}
} // namespace internal
} // namespace detail
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
namespace internal {
namespace detail {
template <typename Locale>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
@ -194,18 +200,16 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
.decimal_point();
}
} // namespace internal
} // namespace detail
#else
template <typename Char>
FMT_FUNC std::string internal::grouping_impl(locale_ref) {
FMT_FUNC std::string detail::grouping_impl(locale_ref) {
return "\03";
}
template <typename Char>
FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
template <typename Char> FMT_FUNC Char detail::thousands_sep_impl(locale_ref) {
return FMT_STATIC_THOUSANDS_SEPARATOR;
}
template <typename Char>
FMT_FUNC Char internal::decimal_point_impl(locale_ref) {
template <typename Char> FMT_FUNC Char detail::decimal_point_impl(locale_ref) {
return '.';
}
#endif
@ -222,9 +226,9 @@ FMT_FUNC void system_error::init(int err_code, string_view format_str,
base = std::runtime_error(to_string(buffer));
}
namespace internal {
namespace detail {
template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) {
template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) {
// fallback_uintptr is always stored in little endian.
int i = static_cast<int>(sizeof(void*)) - 1;
while (i > 0 && n.value[i] == 0) --i;
@ -317,6 +321,10 @@ 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 wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
template <typename T>
const char basic_data<T>::left_padding_shifts[] = {31, 31, 0, 1, 0};
template <typename T>
const char basic_data<T>::right_padding_shifts[] = {0, 31, 0, 1, 0};
template <typename T> struct bits {
static FMT_CONSTEXPR_DECL const int value =
@ -507,20 +515,23 @@ class bigint {
basic_memory_buffer<bigit, bigits_capacity> bigits_;
int exp_;
bigit operator[](int index) const { return bigits_[to_unsigned(index)]; }
bigit& operator[](int index) { return bigits_[to_unsigned(index)]; }
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
friend struct formatter<bigint>;
void subtract_bigits(int index, bigit other, bigit& borrow) {
auto result = static_cast<double_bigit>(bigits_[index]) - other - borrow;
bigits_[index] = static_cast<bigit>(result);
auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
(*this)[index] = static_cast<bigit>(result);
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
}
void remove_leading_zeros() {
int num_bigits = static_cast<int>(bigits_.size()) - 1;
while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits;
bigits_.resize(num_bigits + 1);
while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
bigits_.resize(to_unsigned(num_bigits + 1));
}
// Computes *this -= other assuming aligned bigints and *this >= other.
@ -573,9 +584,10 @@ class bigint {
void operator=(const bigint&) = delete;
void assign(const bigint& other) {
bigits_.resize(other.bigits_.size());
auto size = other.bigits_.size();
bigits_.resize(size);
auto data = other.bigits_.data();
std::copy(data, data + other.bigits_.size(), bigits_.data());
std::copy(data, data + size, make_checked(bigits_.data(), size));
exp_ = other.exp_;
}
@ -591,7 +603,7 @@ class bigint {
int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
bigint& operator<<=(int shift) {
FMT_NOINLINE bigint& operator<<=(int shift) {
assert(shift >= 0);
exp_ += shift / bigit_bits;
shift %= bigit_bits;
@ -621,7 +633,7 @@ class bigint {
int end = i - j;
if (end < 0) end = 0;
for (; i >= end; --i, --j) {
bigit lhs_bigit = lhs.bigits_[i], rhs_bigit = rhs.bigits_[j];
bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
}
if (i != j) return i > j ? 1 : -1;
@ -636,7 +648,7 @@ class bigint {
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
if (max_lhs_bigits > num_rhs_bigits) return 1;
auto get_bigit = [](const bigint& n, int i) -> bigit {
return i >= n.exp_ && i < n.num_bigits() ? n.bigits_[i - n.exp_] : 0;
return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
};
double_bigit borrow = 0;
int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
@ -676,7 +688,7 @@ class bigint {
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
int num_bigits = static_cast<int>(bigits_.size());
int num_result_bigits = 2 * num_bigits;
bigits_.resize(num_result_bigits);
bigits_.resize(to_unsigned(num_result_bigits));
using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
auto sum = accumulator_t();
for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
@ -686,7 +698,7 @@ class bigint {
// Most terms are multiplied twice which can be optimized in the future.
sum += static_cast<double_bigit>(n[i]) * n[j];
}
bigits_[bigit_index] = static_cast<bigit>(sum);
(*this)[bigit_index] = static_cast<bigit>(sum);
sum >>= bits<bigit>::value; // Compute the carry.
}
// Do the same for the top half.
@ -694,7 +706,7 @@ class bigint {
++bigit_index) {
for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
sum += static_cast<double_bigit>(n[i++]) * n[j--];
bigits_[bigit_index] = static_cast<bigit>(sum);
(*this)[bigit_index] = static_cast<bigit>(sum);
sum >>= bits<bigit>::value;
}
--num_result_bigits;
@ -708,11 +720,11 @@ class bigint {
FMT_ASSERT(this != &divisor, "");
if (compare(*this, divisor) < 0) return 0;
int num_bigits = static_cast<int>(bigits_.size());
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1] != 0, "");
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
int exp_difference = exp_ - divisor.exp_;
if (exp_difference > 0) {
// Align bigints by adding trailing zeros to simplify subtraction.
bigits_.resize(num_bigits + exp_difference);
bigits_.resize(to_unsigned(num_bigits + exp_difference));
for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
bigits_[j] = bigits_[i];
std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
@ -1023,7 +1035,7 @@ void fallback_format(Double d, buffer<char>& buf, int& exp10) {
if (result > 0 || (result == 0 && (digit % 2) != 0))
++data[num_digits - 1];
}
buf.resize(num_digits);
buf.resize(to_unsigned(num_digits));
exp10 -= num_digits - 1;
return;
}
@ -1142,7 +1154,7 @@ int snprintf_float(T value, int precision, float_specs specs,
for (;;) {
auto begin = buf.data() + offset;
auto capacity = buf.capacity() - offset;
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
#ifdef FMT_FUZZ
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
@ -1157,7 +1169,7 @@ int snprintf_float(T value, int precision, float_specs specs,
buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially.
continue;
}
unsigned size = to_unsigned(result);
auto size = to_unsigned(result);
// Size equal to capacity means that the last character was truncated.
if (size >= capacity) {
buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'.
@ -1175,7 +1187,7 @@ int snprintf_float(T value, int precision, float_specs specs,
--p;
} while (is_digit(*p));
int fraction_size = static_cast<int>(end - p - 1);
std::memmove(p, p + 1, fraction_size);
std::memmove(p, p + 1, to_unsigned(fraction_size));
buf.resize(size - 1);
return -fraction_size;
}
@ -1204,9 +1216,9 @@ int snprintf_float(T value, int precision, float_specs specs,
while (*fraction_end == '0') --fraction_end;
// Move the fractional part left to get rid of the decimal point.
fraction_size = static_cast<int>(fraction_end - begin - 1);
std::memmove(begin + 1, begin + 2, fraction_size);
std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size));
}
buf.resize(fraction_size + offset + 1);
buf.resize(to_unsigned(fraction_size) + offset + 1);
return exp - fraction_size;
}
}
@ -1265,19 +1277,19 @@ FMT_FUNC const char* utf8_decode(const char* buf, uint32_t* c, int* e) {
return next;
}
} // namespace internal
} // namespace detail
template <> struct formatter<internal::bigint> {
template <> struct formatter<detail::bigint> {
format_parse_context::iterator parse(format_parse_context& ctx) {
return ctx.begin();
}
format_context::iterator format(const internal::bigint& n,
format_context::iterator format(const detail::bigint& n,
format_context& ctx) {
auto out = ctx.out();
bool first = true;
for (auto i = n.bigits_.size(); i > 0; --i) {
auto value = n.bigits_[i - 1];
auto value = n.bigits_[i - 1u];
if (first) {
out = format_to(out, "{:x}", value);
first = false;
@ -1286,12 +1298,12 @@ template <> struct formatter<internal::bigint> {
out = format_to(out, "{:08x}", value);
}
if (n.exp_ > 0)
out = format_to(out, "p{}", n.exp_ * internal::bigint::bigit_bits);
out = format_to(out, "p{}", n.exp_ * detail::bigint::bigit_bits);
return out;
}
};
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
auto transcode = [this](const char* p) {
auto cp = uint32_t();
auto error = 0;
@ -1313,7 +1325,7 @@ FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
}
if (auto num_chars_left = s.data() + s.size() - p) {
char buf[2 * block_size - 1] = {};
memcpy(buf, p, num_chars_left);
memcpy(buf, p, to_unsigned(num_chars_left));
p = buf;
do {
p = transcode(p);
@ -1322,7 +1334,7 @@ FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
buffer_.push_back(0);
}
FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT {
FMT_TRY {
memory_buffer buf;
@ -1330,12 +1342,9 @@ FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
for (;;) {
char* system_message = &buf[0];
int result =
internal::safe_strerror(error_code, system_message, buf.size());
detail::safe_strerror(error_code, system_message, buf.size());
if (result == 0) {
internal::writer w(out);
w.write(message);
w.write(": ");
w.write(system_message);
format_to(std::back_inserter(out), "{}: {}", message, system_message);
return;
}
if (result != ERANGE)
@ -1347,7 +1356,7 @@ FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
format_error_code(out, error_code, message);
}
FMT_FUNC void internal::error_handler::on_error(const char* message) {
FMT_FUNC void detail::error_handler::on_error(const char* message) {
FMT_THROW(format_error(message));
}
@ -1356,32 +1365,38 @@ FMT_FUNC void report_system_error(int error_code,
report_error(format_system_error, error_code, message);
}
FMT_FUNC std::string detail::vformat(string_view format_str, format_args args) {
memory_buffer buffer;
detail::vformat_to(buffer, format_str, args);
return to_string(buffer);
}
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
memory_buffer buffer;
internal::vformat_to(buffer, format_str,
detail::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args));
#ifdef _WIN32
auto fd = _fileno(f);
if (_isatty(fd)) {
internal::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
detail::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
auto written = DWORD();
if (!WriteConsoleW(reinterpret_cast<HANDLE>(_get_osfhandle(fd)),
u16.c_str(), static_cast<DWORD>(u16.size()), &written,
nullptr)) {
throw format_error("failed to write to console");
FMT_THROW(format_error("failed to write to console"));
}
return;
}
#endif
internal::fwrite_fully(buffer.data(), 1, buffer.size(), f);
detail::fwrite_fully(buffer.data(), 1, buffer.size(), f);
}
#ifdef _WIN32
// Print assuming legacy (non-Unicode) encoding.
FMT_FUNC void internal::vprint_mojibake(std::FILE* f, string_view format_str,
FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str,
format_args args) {
memory_buffer buffer;
internal::vformat_to(buffer, format_str,
detail::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args));
fwrite_fully(buffer.data(), 1, buffer.size(), f);
}

File diff suppressed because it is too large Load Diff

View File

@ -14,15 +14,15 @@
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
template <typename Char>
typename buffer_context<Char>::iterator vformat_to(
const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
internal::locale_ref(loc));
using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
return vformat_to<af>(std::back_inserter(buf), to_string_view(format_str),
args, detail::locale_ref(loc));
}
template <typename Char>
@ -30,43 +30,43 @@ std::basic_string<Char> vformat(
const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args);
detail::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer);
}
} // namespace internal
} // namespace detail
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat(loc, to_string_view(format_str), args);
return detail::vformat(loc, to_string_view(format_str), args);
}
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return internal::vformat(
return detail::vformat(
loc, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
detail::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t<
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
detail::is_output_iterator<OutputIt>::value, char_t<S>>>
inline OutputIt vformat_to(
OutputIt out, const std::locale& loc, const S& format_str,
format_args_t<type_identity_t<OutputIt>, Char> args) {
using range = internal::output_range<OutputIt, Char>;
return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
using af = arg_formatter<OutputIt, Char>;
return vformat_to<af>(out, to_string_view(format_str), args,
detail::locale_ref(loc));
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&&
detail::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) {
internal::check_format_string<Args...>(format_str);
detail::check_format_string<Args...>(format_str);
using context = format_context_t<OutputIt, char_t<S>>;
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),

View File

@ -15,11 +15,10 @@
#include <cerrno>
#include <clocale> // for locale_t
#include <cstddef>
#include <cstdio>
#include <cstdlib> // for strtod_l
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
@ -51,7 +50,7 @@
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) call
# define FMT_SYSTEM(call) ::call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
@ -134,7 +133,7 @@ class error_code {
};
#ifdef _WIN32
namespace internal {
namespace detail {
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
@ -157,7 +156,7 @@ class utf16_to_utf8 {
FMT_API void format_windows_error(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
} // namespace internal
} // namespace detail
/** A Windows error. */
class windows_error : public system_error {
@ -314,10 +313,10 @@ class file {
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void* buffer, std::size_t count);
FMT_API size_t read(void* buffer, size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void* buffer, std::size_t count);
FMT_API size_t write(const void* buffer, size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
@ -346,21 +345,15 @@ long getpagesize();
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale {
class locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
static void freelocale(locale_t loc) { _free_locale(loc); }
static locale_t newlocale(int category_mask, const char* locale, locale_t) {
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale) { _free_locale(locale); }
static double strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return _strtod_l(nptr, endptr, locale);
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
return _strtod_l(nptr, endptr, loc);
}
# endif
@ -368,13 +361,18 @@ class Locale {
public:
using type = locale_t;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
locale(const locale&) = delete;
void operator=(const locale&) = delete;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
locale() {
# ifndef _WIN32
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
# else
locale_ = _create_locale(LC_NUMERIC, "C");
# endif
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~Locale() { freelocale(locale_); }
~locale() { freelocale(locale_); }
type get() const { return locale_; }
@ -387,6 +385,7 @@ class Locale {
return result;
}
};
using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE
FMT_END_NAMESPACE

View File

@ -9,10 +9,15 @@
#define FMT_OSTREAM_H_
#include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename Char> class basic_printf_parse_context;
template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail {
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private:
@ -93,34 +98,53 @@ void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
#endif
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.resize(buf.size());
}
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: formatter<basic_string_view<Char>, Char> {
template <typename Context>
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
: private formatter<basic_string_view<Char>, Char> {
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return formatter<basic_string_view<Char>, Char>::parse(ctx);
}
template <typename ParseCtx,
FMT_ENABLE_IF(std::is_same<
ParseCtx, basic_printf_parse_context<Char>>::value)>
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
}
};
} // namespace internal
} // namespace detail
template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer);
detail::vformat_to(buffer, format_str, args);
detail::write(os, buffer);
}
/**
@ -133,10 +157,10 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
detail::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@ -14,7 +14,7 @@
#include "ostream.h"
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
@ -90,11 +90,11 @@ template <typename T, typename Context> class arg_converter {
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value)));
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value)));
}
} else {
@ -102,9 +102,9 @@ template <typename T, typename Context> class arg_converter {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
} else {
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value));
}
}
@ -133,7 +133,7 @@ template <typename Context> class char_converter {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<typename Context::char_type>(value));
}
@ -141,6 +141,13 @@ template <typename Context> class char_converter {
void operator()(T) {} // No conversion needed for non-integral types.
};
// An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise.
template <typename Char> struct get_cstring {
template <typename T> const Char* operator()(T) { return nullptr; }
const Char* operator()(const Char* s) { return s; }
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
template <typename Char> class printf_width_handler {
@ -155,7 +162,7 @@ template <typename Char> class printf_width_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (internal::is_negative(value)) {
if (detail::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
@ -172,23 +179,25 @@ template <typename Char> class printf_width_handler {
};
template <typename Char, typename Context>
void printf(buffer<Char>& buf, basic_string_view<Char> format,
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format();
}
} // namespace detail
template <typename OutputIt, typename Char, typename Context>
internal::truncating_iterator<OutputIt> printf(
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
// For printing into memory_buffer.
template <typename Char, typename Context>
FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
basic_string_view<Char> format,
basic_format_args<Context> args) {
return Context(it, format, args).format();
return detail::vprintf(buf, format, args);
}
} // namespace internal
using internal::printf; // For printing into memory_buffer.
template <typename Range> class printf_arg_formatter;
using detail::vprintf;
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context;
/**
@ -196,15 +205,15 @@ template <typename OutputIt, typename Char> class basic_printf_context;
The ``printf`` argument formatter.
\endrst
*/
template <typename Range>
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
template <typename OutputIt, typename Char>
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
public:
using iterator = typename Range::iterator;
using iterator = OutputIt;
private:
using char_type = typename Range::value_type;
using base = internal::arg_formatter_base<Range>;
using context_type = basic_printf_context<iterator, char_type>;
using char_type = Char;
using base = detail::arg_formatter_base<OutputIt, Char>;
using context_type = basic_printf_context<OutputIt, Char>;
context_type& context_;
@ -229,9 +238,9 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
\endrst
*/
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
: base(iter, &specs, detail::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
@ -246,6 +255,10 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
return base::operator()(value);
} else {
@ -312,18 +325,21 @@ template <typename T> struct printf_formatter {
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
internal::format_value(internal::get_container(ctx.out()), value);
detail::format_value(detail::get_container(ctx.out()), value);
return ctx.out();
}
};
/** This template formats data and writes the output to a writer. */
/**
This template formats data and writes the output through an output iterator.
*/
template <typename OutputIt, typename Char> class basic_printf_context {
public:
/** The character type for the output. */
using char_type = Char;
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
private:
@ -331,7 +347,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out_;
basic_format_args<basic_printf_context> args_;
basic_format_parse_context<Char> parse_ctx_;
parse_context_type parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
@ -346,9 +362,8 @@ template <typename OutputIt, typename Char> class basic_printf_context {
public:
/**
\rst
Constructs a ``printf_context`` object. References to the arguments and
the writer are stored in the context object so make sure they have
appropriate lifetimes.
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
@ -358,18 +373,18 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
internal::locale_ref locale() { return {}; }
detail::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
parse_context_type& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
OutputIt format();
};
@ -389,7 +404,9 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
specs.fill[0] = '0';
break;
case ' ':
if (specs.sign != sign::plus) {
specs.sign = sign::space;
}
break;
case '#':
specs.alt = true;
@ -407,7 +424,7 @@ basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
return internal::get_arg(*this, arg_index);
return detail::get_arg(*this, arg_index);
}
template <typename OutputIt, typename Char>
@ -419,7 +436,7 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
internal::error_handler eh;
detail::error_handler eh;
int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index
++it;
@ -438,12 +455,12 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
internal::error_handler eh;
detail::error_handler eh;
specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(visit_format_arg(
internal::printf_width_handler<char_type>(specs), get_arg()));
detail::printf_width_handler<char_type>(specs), get_arg()));
}
}
return arg_index;
@ -471,38 +488,52 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument index out of range");
if (arg_index == 0) on_error("argument not found");
// Parse precision.
if (it != end && *it == '.') {
++it;
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
internal::error_handler eh;
detail::error_handler eh;
specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') {
++it;
specs.precision = static_cast<int>(
visit_format_arg(internal::printf_precision_handler(), get_arg()));
visit_format_arg(detail::printf_precision_handler(), get_arg()));
} else {
specs.precision = 0;
}
}
format_arg arg = get_arg(arg_index);
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral())
specs.fill[0] =
' '; // Ignore '0' flag for non-numeric types or if '-' present.
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>(
str,
detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
}
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
specs.alt = false;
if (specs.fill[0] == '0') {
if (arg.is_arithmetic())
if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric;
else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types.
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
}
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0;
using internal::convert_arg;
using detail::convert_arg;
switch (c) {
case 'h':
if (t == 'h') {
@ -526,7 +557,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
convert_arg<intmax_t>(arg, t);
break;
case 'z':
convert_arg<std::size_t>(arg, t);
convert_arg<size_t>(arg, t);
break;
case 't':
convert_arg<std::ptrdiff_t>(arg, t);
@ -551,7 +582,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
specs.type = 'd';
break;
case 'c':
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
visit_format_arg(detail::char_converter<basic_printf_context>(arg),
arg);
break;
}
@ -560,15 +591,14 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
start = it;
// Format argument.
visit_format_arg(ArgFormatter(out, specs, *this), arg);
out = visit_format_arg(ArgFormatter(out, specs, *this), arg);
}
return std::copy(start, it, out);
}
template <typename Char>
using basic_printf_context_t =
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
Char>;
basic_printf_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
@ -605,7 +635,7 @@ inline std::basic_string<Char> vsprintf(
const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
vprintf(buffer, to_string_view(format), args);
return to_string(buffer);
}
@ -619,7 +649,7 @@ inline std::basic_string<Char> vsprintf(
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), make_format_args<context>(args...));
@ -630,8 +660,8 @@ inline int vfprintf(
std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
std::size_t size = buffer.size();
vprintf(buffer, to_string_view(format), args);
size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
@ -647,7 +677,7 @@ inline int vfprintf(
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format),
@ -671,7 +701,7 @@ inline int vprintf(
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_string<S>::value)>
inline int printf(const S& format_str, const Args&... args) {
using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str),
@ -683,8 +713,8 @@ inline int vfprintf(
std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
internal::write(os, buffer);
vprintf(buffer, to_string_view(format), args);
detail::write(os, buffer);
return static_cast<int>(buffer.size());
}
@ -693,7 +723,7 @@ template <typename ArgFormatter, typename Char,
typename Context =
basic_printf_context<typename ArgFormatter::iterator, Char>>
typename ArgFormatter::iterator vprintf(
internal::buffer<Char>& out, basic_string_view<Char> format_str,
detail::buffer<Char>& out, basic_string_view<Char> format_str,
basic_format_args<type_identity_t<Context>> args) {
typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>();

View File

@ -33,7 +33,7 @@ template <typename Char> struct formatting_base {
template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
static FMT_CONSTEXPR_DECL const size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix;
@ -54,7 +54,7 @@ struct formatting_tuple : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
namespace internal {
namespace detail {
template <typename RangeT, typename OutputIterator>
OutputIterator copy(const RangeT& range, OutputIterator out) {
@ -118,26 +118,24 @@ template <typename T> class is_tuple_like_ {
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
template <std::size_t N>
using make_index_sequence = std::make_index_sequence<N>;
template <size_t... N> using index_sequence = std::index_sequence<N...>;
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
};
template <std::size_t... N>
using index_sequence = integer_sequence<std::size_t, N...>;
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
template <typename T, std::size_t N, T... Ns>
template <typename T, size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
#endif
template <class Tuple, class F, size_t... Is>
@ -185,11 +183,11 @@ FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'";
}
} // namespace internal
} // namespace detail
template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
template <typename TupleT, typename Char>
@ -202,17 +200,17 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
out = internal::copy(formatting.delimiter, out);
out = detail::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i;
}
formatting_tuple<Char>& formatting;
std::size_t& i;
size_t& i;
typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
};
@ -228,14 +226,14 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formatting.prefix, out);
size_t i = 0;
detail::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
detail::copy(formatting.postfix, out);
return ctx.out();
}
@ -243,10 +241,9 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value &&
!internal::is_like_std_string<T>::value &&
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<internal::std_string_view<Char>, T>::value;
!std::is_constructible<detail::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
@ -262,15 +259,17 @@ struct formatter<RangeT, Char,
template <typename FormatContext>
typename FormatContext::iterator format(const RangeT& values,
FormatContext& ctx) {
auto out = internal::copy(formatting.prefix, ctx.out());
std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0;
auto it = values.begin();
auto end = values.end();
for (; it != end; ++it) {
if (i > 0) {
if (formatting.add_prepostfix_space) *out++ = ' ';
out = internal::copy(formatting.delimiter, out);
out = detail::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
if (++i > formatting.range_length_limit) {
@ -279,11 +278,11 @@ struct formatter<RangeT, Char,
}
}
if (formatting.add_prepostfix_space) *out++ = ' ';
return internal::copy(formatting.postfix, out);
return detail::copy(formatting.postfix, out);
}
};
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
template <typename Char, typename... T> struct tuple_arg_join : detail::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
@ -301,14 +300,14 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
template <typename FormatContext>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
}
private:
template <typename FormatContext, size_t... N>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
internal::index_sequence<N...>) {
detail::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...);
}
@ -371,14 +370,14 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
\endrst
*/
template <typename T>
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join(
std::initializer_list<T> list, string_view sep) {
arg_join<const T*, const T*, char> join(std::initializer_list<T> list,
string_view sep) {
return join(std::begin(list), std::end(list), sep);
}
template <typename T>
arg_join<internal::iterator_t<const std::initializer_list<T>>, wchar_t> join(
std::initializer_list<T> list, wstring_view sep) {
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
wstring_view sep) {
return join(std::begin(list), std::end(list), sep);
}

View File

@ -8,12 +8,12 @@
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
#ifdef FMT_FUZZ
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
@ -23,154 +23,46 @@ int format_float(char* buf, std::size_t size, const char* format, int precision,
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
struct sprintf_specs {
int precision;
char type;
bool alt : 1;
} // namespace detail
template <typename Char>
constexpr sprintf_specs(basic_format_specs<Char> specs)
: precision(specs.precision), type(specs.type), alt(specs.alt) {}
constexpr bool has_precision() const { return precision >= 0; }
};
// This is deprecated and is kept only to preserve ABI compatibility.
template <typename Double>
char* sprintf_format(Double value, internal::buffer<char>& buf,
sprintf_specs specs) {
// 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 (specs.alt || !specs.type) *format_ptr++ = '#';
if (specs.precision >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
}
if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
char type = specs.type;
if (type == '%')
type = 'f';
else if (type == 0 || type == 'n')
type = 'g';
#if FMT_MSC_VER
if (type == 'F') {
// MSVC's printf doesn't support 'F'.
type = 'f';
}
#endif
*format_ptr++ = type;
*format_ptr = '\0';
// Format using snprintf.
char* start = nullptr;
char* decimal_point_pos = nullptr;
for (;;) {
std::size_t buffer_size = buf.capacity();
start = &buf[0];
int result =
format_float(start, buffer_size, format, specs.precision, value);
if (result >= 0) {
unsigned n = internal::to_unsigned(result);
if (n < buf.capacity()) {
// Find the decimal point.
auto p = buf.data(), end = p + n;
if (*p == '+' || *p == '-') ++p;
if (specs.type != 'a' && specs.type != 'A') {
while (p < end && *p >= '0' && *p <= '9') ++p;
if (p < end && *p != 'e' && *p != 'E') {
decimal_point_pos = p;
if (!specs.type) {
// Keep only one trailing zero after the decimal point.
++p;
if (*p == '0') ++p;
while (p != end && *p >= '1' && *p <= '9') ++p;
char* where = p;
while (p != end && *p == '0') ++p;
if (p == end || *p < '0' || *p > '9') {
if (p != end) std::memmove(where, p, to_unsigned(end - p));
n -= static_cast<unsigned>(p - where);
}
}
}
}
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);
}
}
return decimal_point_pos;
}
} // namespace internal
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
sprintf_specs);
template FMT_API char* internal::sprintf_format(long double,
internal::buffer<char>&,
sprintf_specs);
template struct FMT_INSTANTIATION_DEF_API internal::basic_data<void>;
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, internal::float_specs,
internal::buffer<char>&) =
internal::format_float;
int (*instantiate_format_float)(double, int, detail::float_specs,
detail::buffer<char>&) = detail::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
#endif
// Explicit instantiations for char.
template FMT_API std::string internal::grouping_impl<char>(locale_ref);
template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
template FMT_API std::string detail::grouping_impl<char>(locale_ref);
template FMT_API char detail::thousands_sep_impl(locale_ref);
template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<char>::append(const char*, const char*);
template FMT_API void detail::buffer<char>::append(const char*, const char*);
template FMT_API void internal::arg_map<format_context>::init(
const basic_format_args<format_context>& args);
template FMT_API format_context::iterator detail::vformat_to(
detail::buffer<char>&, string_view, basic_format_args<format_context>);
template FMT_API std::string internal::vformat<char>(
string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to(
internal::buffer<char>&, string_view, basic_format_args<format_context>);
template FMT_API int internal::snprintf_float(double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::snprintf_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(double, int, internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::snprintf_float(long double, int,
detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(long double, int, detail::float_specs,
detail::buffer<char>&);
// Explicit instantiations for wchar_t.
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
FMT_END_NAMESPACE

View File

@ -73,14 +73,14 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
FMT_BEGIN_NAMESPACE
#ifdef _WIN32
internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
detail::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
if (int error_code = convert(s)) {
FMT_THROW(windows_error(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
int internal::utf16_to_utf8::convert(wstring_view s) {
int detail::utf16_to_utf8::convert(wstring_view s) {
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
if (s_size == 0) {
@ -105,12 +105,12 @@ void windows_error::init(int err_code, string_view format_str,
format_args args) {
error_code_ = err_code;
memory_buffer buffer;
internal::format_windows_error(buffer, err_code, vformat(format_str, args));
detail::format_windows_error(buffer, err_code, vformat(format_str, args));
std::runtime_error& base = *this;
base = std::runtime_error(to_string(buffer));
}
void internal::format_windows_error(internal::buffer<char>& out, int error_code,
void detail::format_windows_error(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT {
FMT_TRY {
wmemory_buffer buf;
@ -124,10 +124,7 @@ void internal::format_windows_error(internal::buffer<char>& out, int error_code,
if (result != 0) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
internal::writer w(out);
w.write(message);
w.write(": ");
w.write(utf8_message);
format_to(std::back_inserter(out), "{}: {}", message, utf8_message);
return;
}
break;
@ -143,7 +140,7 @@ void internal::format_windows_error(internal::buffer<char>& out, int error_code,
void report_windows_error(int error_code,
fmt::string_view message) FMT_NOEXCEPT {
report_error(internal::format_windows_error, error_code, message);
report_error(detail::format_windows_error, error_code, message);
}
#endif // _WIN32
@ -234,14 +231,14 @@ std::size_t file::read(void* buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
return internal::to_unsigned(result);
return detail::to_unsigned(result);
}
std::size_t file::write(const void* buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
return internal::to_unsigned(result);
return detail::to_unsigned(result);
}
file file::dup(int fd) {
@ -292,7 +289,11 @@ void file::pipe(file& read_end, file& write_end) {
buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR.
#if defined(__MINGW32__) && defined(_POSIX_)
FILE* f = ::fdopen(fd_, mode);
#else
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
#endif
if (!f)
FMT_THROW(
system_error(errno, "cannot associate stream with file descriptor"));

1
support/Vagrantfile vendored
View File

@ -4,6 +4,7 @@
# A vagrant config for testing against gcc-4.8.
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/xenial64"
config.disksize.size = '15GB'
config.vm.provider "virtualbox" do |vb|
vb.memory = "4096"

View File

@ -0,0 +1,26 @@
# This module provides function for joining paths
# known from from most languages
#
# Original license:
# SPDX-License-Identifier: (MIT OR CC0-1.0)
# Explicit permission given to distribute this module under
# the terms of the project as described in /LICENSE.rst.
# Copyright 2020 Jan Tojnar
# https://github.com/jtojnar/cmake-snips
#
# Modelled after Python’s os.path.join
# https://docs.python.org/3.7/library/os.path.html#os.path.join
# Windows not supported
function(join_paths joined_path first_path_segment)
set(temp_path "${first_path_segment}")
foreach(current_segment IN LISTS ARGN)
if(NOT ("${current_segment}" STREQUAL ""))
if(IS_ABSOLUTE "${current_segment}")
set(temp_path "${current_segment}")
else()
set(temp_path "${temp_path}/${current_segment}")
endif()
endif()
endforeach()
set(${joined_path} "${temp_path}" PARENT_SCOPE)
endfunction()

View File

@ -1,7 +1,7 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
libdir=@libdir_for_pc_file@
includedir=@includedir_for_pc_file@
Name: fmt
Description: A modern formatting library

View File

@ -144,9 +144,15 @@ def update_site(env):
b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data)
b.data = b.data.replace('std::FILE*', 'std::FILE *')
b.data = b.data.replace('unsigned int', 'unsigned')
b.data = b.data.replace('operator""_', 'operator"" _')
#b.data = b.data.replace('operator""_', 'operator"" _')
b.data = b.data.replace(', size_t', ', std::size_t')
b.data = b.data.replace('aa long', 'a long')
if version.startswith('6.2.'):
b.data = b.data.replace(
'vformat(const S&, basic_format_args<' +
'buffer_context<Char>>)',
'vformat(const S&, basic_format_args<' +
'buffer_context<type_identity_t<Char>>>)')
# Fix a broken link in index.rst.
index = os.path.join(target_doc_dir, 'index.rst')
with rewrite(index) as b:

View File

@ -10,10 +10,11 @@
#endif
#include "fmt/chrono.h"
#include "gtest-extra.h"
#include <iomanip>
#include "gtest-extra.h"
std::tm make_tm() {
auto time = std::tm();
time.tm_mday = 1;

View File

@ -6,6 +6,7 @@
// For the license information refer to format.h.
#include "fmt/color.h"
#include "gtest-extra.h"
TEST(ColorsTest, ColorsPrint) {

View File

@ -6,6 +6,7 @@
// For the license information refer to format.h.
#include <stdint.h>
#include <cctype>
#include <cfloat>
#include <climits>
@ -39,8 +40,8 @@ using testing::StrictMock;
#if FMT_USE_CONSTEXPR
template <unsigned EXPECTED_PARTS_COUNT, typename Format>
void check_prepared_parts_type(Format format) {
typedef fmt::internal::compiled_format_base<decltype(format)> provider;
typedef fmt::internal::format_part<char>
typedef fmt::detail::compiled_format_base<decltype(format)> provider;
typedef fmt::detail::format_part<char>
expected_parts_type[EXPECTED_PARTS_COUNT];
static_assert(std::is_same<typename provider::parts_container,
expected_parts_type>::value,
@ -84,11 +85,11 @@ TEST(CompileTest, PassCompileString) {
TEST(CompileTest, FormatToArrayOfChars) {
char buffer[32] = {0};
const auto prepared = fmt::compile<int>("4{}");
fmt::format_to(fmt::internal::make_checked(buffer, 32), prepared, 2);
fmt::format_to(fmt::detail::make_checked(buffer, 32), prepared, 2);
EXPECT_EQ(std::string("42"), buffer);
wchar_t wbuffer[32] = {0};
const auto wprepared = fmt::compile<int>(L"4{}");
fmt::format_to(fmt::internal::make_checked(wbuffer, 32), wprepared, 2);
fmt::format_to(fmt::detail::make_checked(wbuffer, 32), wprepared, 2);
EXPECT_EQ(std::wstring(L"42"), wbuffer);
}

View File

@ -30,8 +30,8 @@
using fmt::basic_format_arg;
using fmt::string_view;
using fmt::internal::buffer;
using fmt::internal::value;
using fmt::detail::buffer;
using fmt::detail::value;
using testing::_;
using testing::StrictMock;
@ -42,7 +42,7 @@ struct test_struct {};
template <typename Context, typename T>
basic_format_arg<Context> make_arg(const T& value) {
return fmt::internal::make_arg<Context>(value);
return fmt::detail::make_arg<Context>(value);
}
} // namespace
@ -83,20 +83,20 @@ TEST(BufferTest, Nonmoveable) {
// A test buffer with a dummy grow method.
template <typename T> struct test_buffer : buffer<T> {
void grow(std::size_t capacity) { this->set(nullptr, capacity); }
void grow(size_t capacity) { this->set(nullptr, capacity); }
};
template <typename T> struct mock_buffer : buffer<T> {
MOCK_METHOD1(do_grow, void(std::size_t capacity));
MOCK_METHOD1(do_grow, void(size_t capacity));
void grow(std::size_t capacity) {
void grow(size_t capacity) {
this->set(this->data(), capacity);
do_grow(capacity);
}
mock_buffer() {}
mock_buffer(T* data) { this->set(data, 0); }
mock_buffer(T* data, std::size_t capacity) { this->set(data, capacity); }
mock_buffer(T* data, size_t capacity) { this->set(data, capacity); }
};
TEST(BufferTest, Ctor) {
@ -115,7 +115,7 @@ TEST(BufferTest, Ctor) {
}
{
int dummy;
std::size_t capacity = std::numeric_limits<std::size_t>::max();
size_t capacity = std::numeric_limits<size_t>::max();
mock_buffer<int> buffer(&dummy, capacity);
EXPECT_EQ(&dummy, &buffer[0]);
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
@ -148,7 +148,7 @@ TEST(BufferTest, Access) {
EXPECT_EQ(11, buffer[0]);
buffer[3] = 42;
EXPECT_EQ(42, *(&buffer[0] + 3));
const fmt::internal::buffer<char>& const_buffer = buffer;
const fmt::detail::buffer<char>& const_buffer = buffer;
EXPECT_EQ(42, const_buffer[3]);
}
@ -210,7 +210,8 @@ TEST(ArgTest, FormatArgs) {
}
struct custom_context {
typedef char char_type;
using char_type = char;
using parse_context_type = fmt::format_parse_context;
template <typename T> struct formatter_type {
template <typename ParseContext>
@ -233,20 +234,20 @@ struct custom_context {
TEST(ArgTest, MakeValueWithCustomContext) {
test_struct t;
fmt::internal::value<custom_context> arg(
fmt::internal::arg_mapper<custom_context>().map(t));
fmt::detail::value<custom_context> arg(
fmt::detail::arg_mapper<custom_context>().map(t));
custom_context ctx = {false, fmt::format_parse_context("")};
arg.custom.format(&t, ctx.parse_context(), ctx);
EXPECT_TRUE(ctx.called);
}
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
template <typename Char>
bool operator==(custom_value<Char> lhs, custom_value<Char> rhs) {
return lhs.value == rhs.value;
}
} // namespace internal
} // namespace detail
FMT_END_NAMESPACE
// Use a unique result type to make sure that there are no undesirable
@ -371,12 +372,12 @@ TEST(ArgTest, PointerArg) {
struct check_custom {
test_result operator()(
fmt::basic_format_arg<fmt::format_context>::handle h) const {
struct test_buffer : fmt::internal::buffer<char> {
struct test_buffer : fmt::detail::buffer<char> {
char data[10];
test_buffer() : fmt::internal::buffer<char>(data, 0, 10) {}
void grow(std::size_t) {}
test_buffer() : fmt::detail::buffer<char>(data, 0, 10) {}
void grow(size_t) {}
} buffer;
fmt::internal::buffer<char>& base = buffer;
fmt::detail::buffer<char>& base = buffer;
fmt::format_parse_context parse_ctx("");
fmt::format_context ctx(std::back_inserter(base), fmt::format_args());
h.format(parse_ctx, ctx);
@ -435,8 +436,7 @@ template <> struct formatter<custom_type> {
}
template <typename FormatContext>
auto format(const custom_type& p, FormatContext& ctx) -> decltype(format_to(
ctx.out(), std::declval<typename FormatContext::char_type const*>())) {
auto format(const custom_type& p, FormatContext& ctx) -> decltype(ctx.out()) {
return format_to(ctx.out(), "cust={}", p.i);
}
};
@ -456,6 +456,25 @@ TEST(FormatDynArgsTest, CustomFormat) {
EXPECT_EQ("cust=0 and cust=1 and cust=3", result);
}
TEST(FormatDynArgsTest, NamedInt) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(fmt::arg("a1", 42));
std::string result = fmt::vformat("{a1}", store);
EXPECT_EQ("42", result);
}
TEST(FormatDynArgsTest, NamedStrings) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
char str[]{"1234567890"};
store.push_back(fmt::arg("a1", str));
store.push_back(fmt::arg("a2", std::cref(str)));
str[0] = 'X';
std::string result = fmt::vformat("{a1} and {a2}", store);
EXPECT_EQ("1234567890 and X234567890", result);
}
TEST(FormatDynArgsTest, NamedArgByRef) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
@ -470,12 +489,79 @@ TEST(FormatDynArgsTest, NamedArgByRef) {
// storages.
int a1_val{42};
auto a1 = fmt::arg("a1_", a1_val);
store.push_back("abc");
store.push_back(1.5f);
store.push_back(std::cref(a1));
std::string result = fmt::vformat("{a1_}", // and {} and {}",
store);
std::string result = fmt::vformat("{a1_} and {} and {} and {}", store);
EXPECT_EQ("42 and abc and 1.5 and 42", result);
}
TEST(FormatDynArgsTest, NamedCustomFormat) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
custom_type c{};
store.push_back(fmt::arg("c1", c));
++c.i;
store.push_back(fmt::arg("c2", c));
++c.i;
store.push_back(fmt::arg("c_ref", std::cref(c)));
++c.i;
std::string result = fmt::vformat("{c1} and {c2} and {c_ref}", store);
EXPECT_EQ("cust=0 and cust=1 and cust=3", result);
}
TEST(FormatDynArgsTest, Clear) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
std::string result = fmt::vformat("{}", store);
EXPECT_EQ("42", result);
store.push_back(43);
result = fmt::vformat("{} and {}", store);
EXPECT_EQ("42 and 43", result);
store.clear();
store.push_back(44);
result = fmt::vformat("{}", store);
EXPECT_EQ("44", result);
}
TEST(FormatDynArgsTest, Reserve) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.reserve(2, 1);
store.push_back(1.5f);
store.push_back(fmt::arg("a1", 42));
std::string result = fmt::vformat("{a1} and {}", store);
EXPECT_EQ("42 and 1.5", result);
}
struct copy_throwable {
copy_throwable() {}
copy_throwable(const copy_throwable&) { throw "deal with it"; }
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<copy_throwable> {
auto parse(format_parse_context& ctx) const -> decltype(ctx.begin()) {
return ctx.begin();
}
auto format(copy_throwable, format_context& ctx) -> decltype(ctx.out()) {
return ctx.out();
}
};
FMT_END_NAMESPACE
TEST(FormatDynArgsTest, ThrowOnCopy) {
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(std::string("foo"));
try {
store.push_back(copy_throwable());
} catch (...) {
}
EXPECT_EQ(fmt::vformat("{}", store), "foo");
}
TEST(StringViewTest, ValueType) {
@ -492,9 +578,9 @@ TEST(StringViewTest, Length) {
// Check string_view's comparison operator.
template <template <typename> class Op> void check_op() {
const char* inputs[] = {"foo", "fop", "fo"};
std::size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
for (std::size_t i = 0; i < num_inputs; ++i) {
for (std::size_t j = 0; j < num_inputs; ++j) {
size_t num_inputs = sizeof(inputs) / sizeof(*inputs);
for (size_t i = 0; i < num_inputs; ++i) {
for (size_t j = 0; j < num_inputs; ++j) {
string_view lhs(inputs[i]), rhs(inputs[j]);
EXPECT_EQ(Op<int>()(lhs.compare(rhs), 0), Op<string_view>()(lhs, rhs));
}
@ -580,7 +666,7 @@ 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(); }
size_t length() const FMT_NOEXCEPT { return s_.size(); }
operator const Char*() const { return s_.c_str(); }
private:
@ -596,23 +682,6 @@ inline fmt::basic_string_view<Char> to_string_view(const my_string<Char>& s)
struct non_string {};
} // namespace my_ns
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()); }
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())};
}
} // namespace FakeQt
template <typename T> class IsStringTest : public testing::Test {};
typedef ::testing::Types<char, wchar_t, char16_t, char32_t> StringCharTypes;
@ -624,21 +693,19 @@ struct derived_from_string_view : fmt::basic_string_view<Char> {};
} // namespace
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::detail::is_string<TypeParam*>::value);
EXPECT_TRUE(fmt::detail::is_string<const TypeParam*>::value);
EXPECT_TRUE(fmt::detail::is_string<TypeParam[2]>::value);
EXPECT_TRUE(fmt::detail::is_string<const TypeParam[2]>::value);
EXPECT_TRUE(fmt::detail::is_string<std::basic_string<TypeParam>>::value);
EXPECT_TRUE(fmt::detail::is_string<fmt::basic_string_view<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);
using string_view = fmt::internal::std_string_view<TypeParam>;
fmt::detail::is_string<derived_from_string_view<TypeParam>>::value);
using string_view = fmt::detail::std_string_view<TypeParam>;
EXPECT_TRUE(std::is_empty<string_view>::value !=
fmt::internal::is_string<string_view>::value);
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);
fmt::detail::is_string<string_view>::value);
EXPECT_TRUE(fmt::detail::is_string<my_ns::my_string<TypeParam>>::value);
EXPECT_FALSE(fmt::detail::is_string<my_ns::non_string>::value);
}
TEST(CoreTest, Format) {
@ -661,33 +728,16 @@ TEST(CoreTest, FormatTo) {
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::mapped_type_constant<my_string<char>,
fmt::detail::type type =
fmt::detail::mapped_type_constant<my_string<char>,
fmt::format_context>::value;
EXPECT_EQ(type, fmt::internal::type::string_type);
type = fmt::internal::mapped_type_constant<my_string<wchar_t>,
fmt::wformat_context>::value;
EXPECT_EQ(type, fmt::internal::type::string_type);
type =
fmt::internal::mapped_type_constant<QString, fmt::wformat_context>::value;
EXPECT_EQ(type, fmt::internal::type::string_type);
// Does not compile: only wide format contexts are compatible with QString!
// type = fmt::internal::mapped_type_constant<QString,
// fmt::format_context>::value;
EXPECT_EQ(type, fmt::detail::type::string_type);
}
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 {
@ -722,15 +772,6 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
fmt::format("{}", explicitly_convertible_to_std_string_view()));
}
# endif
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()));
}
#endif
struct disabled_rvalue_conversion {

View File

@ -18,14 +18,14 @@
// A custom argument formatter that doesn't print `-` for floating-point values
// rounded to 0.
class custom_arg_formatter
: public fmt::arg_formatter<fmt::buffer_range<char>> {
: public fmt::arg_formatter<fmt::format_context::iterator, char> {
public:
using range = fmt::buffer_range<char>;
typedef fmt::arg_formatter<range> base;
using base = fmt::arg_formatter<fmt::format_context::iterator, char>;
custom_arg_formatter(fmt::format_context& ctx,
fmt::format_parse_context* parse_ctx,
fmt::format_specs* s = nullptr)
fmt::format_specs* s = nullptr,
const char* = nullptr)
: base(ctx, parse_ctx, s) {}
using base::operator();
@ -39,8 +39,10 @@ class custom_arg_formatter
std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) {
fmt::memory_buffer buffer;
fmt::internal::buffer<char>& base = buffer;
// Pass custom argument formatter as a template arg to vwrite.
fmt::vformat_to<custom_arg_formatter>(buffer, format_str, args);
fmt::vformat_to<custom_arg_formatter>(std::back_inserter(base), format_str,
args);
return std::string(buffer.data(), buffer.size());
}

View File

@ -488,22 +488,21 @@ template<class... Args>
namespace std {
namespace detail {
template <typename Range>
template <typename OutputIt, typename Char>
class arg_formatter
: public fmt::internal::arg_formatter_base<Range, error_handler> {
: public fmt::internal::arg_formatter_base<OutputIt, Char, error_handler> {
private:
using char_type = typename Range::value_type;
using base = fmt::internal::arg_formatter_base<Range, error_handler>;
using format_context = std::basic_format_context<typename base::iterator, char_type>;
using parse_context = basic_format_parse_context<char_type>;
using char_type = Char;
using base = fmt::internal::arg_formatter_base<OutputIt, Char, error_handler>;
using format_context = std::basic_format_context<OutputIt, Char>;
using parse_context = basic_format_parse_context<Char>;
parse_context* parse_ctx_;
format_context& ctx_;
public:
typedef Range range;
typedef typename base::iterator iterator;
typedef typename base::format_specs format_specs;
using iterator = OutputIt;
using format_specs = typename base::format_specs;
/**
\rst
@ -513,7 +512,7 @@ class arg_formatter
\endrst
*/
arg_formatter(format_context& ctx, parse_context* parse_ctx = nullptr, fmt::format_specs* spec = nullptr)
: base(Range(ctx.out()), spec, {}), parse_ctx_(parse_ctx), ctx_(ctx) {}
: base(ctx.out(), spec, {}), parse_ctx_(parse_ctx), ctx_(ctx) {}
using base::operator();
@ -582,12 +581,12 @@ class custom_formatter {
template <typename ArgFormatter, typename Char, typename Context>
struct format_handler : detail::error_handler {
typedef typename ArgFormatter::range range;
using iterator = typename ArgFormatter::iterator;
format_handler(range r, basic_string_view<Char> str,
format_handler(iterator out, basic_string_view<Char> str,
basic_format_args<Context> format_args,
fmt::internal::locale_ref loc)
: parse_ctx(str), context(r.begin(), format_args, loc) {}
: parse_ctx(str), context(out, format_args, loc) {}
void on_text(const Char* begin, const Char* end) {
auto size = fmt::internal::to_unsigned(end - begin);
@ -597,23 +596,20 @@ struct format_handler : detail::error_handler {
context.advance_to(out);
}
void on_arg_id() {
arg = context.arg(parse_ctx.next_arg_id());
}
void on_arg_id(unsigned id) {
parse_ctx.check_arg_id(id);
arg = context.arg(id);
}
void on_arg_id(fmt::basic_string_view<Char>) {}
int on_arg_id() { return parse_ctx.next_arg_id(); }
int on_arg_id(unsigned id) { return parse_ctx.check_arg_id(id), id; }
int on_arg_id(fmt::basic_string_view<Char>) { return 0; }
void on_replacement_field(const Char* p) {
void on_replacement_field(int id, const Char* p) {
auto arg = context.arg(id);
parse_ctx.advance_to(parse_ctx.begin() + (p - &*parse_ctx.begin()));
custom_formatter<Context> f(parse_ctx, context);
if (!visit_format_arg(f, arg))
context.advance_to(visit_format_arg(ArgFormatter(context, &parse_ctx), arg));
}
const Char* on_format_specs(const Char* begin, const Char* end) {
const Char* on_format_specs(int id, const Char* begin, const Char* end) {
auto arg = context.arg(id);
parse_ctx.advance_to(parse_ctx.begin() + (begin - &*parse_ctx.begin()));
custom_formatter<Context> f(parse_ctx, context);
if (visit_format_arg(f, arg)) return &*parse_ctx.begin();
@ -631,7 +627,6 @@ struct format_handler : detail::error_handler {
basic_format_parse_context<Char> parse_ctx;
Context context;
basic_format_arg<Context> arg;
};
template <typename T, typename Char>
@ -650,7 +645,6 @@ struct formatter {
auto eh = ctx.error_handler();
switch (type) {
case internal::type::none_type:
case internal::type::named_arg_type:
FMT_ASSERT(false, "invalid argument type");
break;
case internal::type::int_type:
@ -693,9 +687,9 @@ struct formatter {
specs_.width, specs_.width_ref, ctx);
fmt::internal::handle_dynamic_spec<fmt::internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx);
using range_type = fmt::internal::output_range<typename FormatContext::iterator,
using af = arg_formatter<typename FormatContext::iterator,
typename FormatContext::char_type>;
return visit_format_arg(arg_formatter<range_type>(ctx, nullptr, &specs_),
return visit_format_arg(af(ctx, nullptr, &specs_),
basic_format_arg<FormatContext>(val));
}
@ -718,9 +712,9 @@ template<class... Args>
string vformat(string_view fmt, format_args args) {
fmt::memory_buffer mbuf;
fmt::internal::buffer<char>& buf = mbuf;
using range = fmt::buffer_range<char>;
detail::format_handler<detail::arg_formatter<range>, char, format_context>
h(range(std::back_inserter(buf)), fmt, args, {});
using af = detail::arg_formatter<fmt::format_context::iterator, char>;
detail::format_handler<af, char, format_context>
h(std::back_inserter(buf), fmt, args, {});
fmt::internal::parse_format_string<false>(fmt::to_string_view(fmt), h);
return to_string(mbuf);
}
@ -741,9 +735,9 @@ template<class Out, class... Args>
template<class Out>
Out vformat_to(Out out, string_view fmt, format_args_t<fmt::type_identity_t<Out>, char> args) {
using range = fmt::internal::output_range<Out, char>;
detail::format_handler<detail::arg_formatter<range>, char, basic_format_context<Out, char>>
h(range(out), fmt, args, {});
using af = detail::arg_formatter<Out, char>;
detail::format_handler<af, char, basic_format_context<Out, char>>
h(out, fmt, args, {});
fmt::internal::parse_format_string<false>(fmt::to_string_view(fmt), h);
return h.context.out();
}

View File

@ -21,9 +21,9 @@
#undef max
using fmt::internal::bigint;
using fmt::internal::fp;
using fmt::internal::max_value;
using fmt::detail::bigint;
using fmt::detail::fp;
using fmt::detail::max_value;
static_assert(!std::is_copy_constructible<bigint>::value, "");
static_assert(!std::is_copy_assignable<bigint>::value, "");
@ -102,7 +102,7 @@ TEST(BigIntTest, Multiply) {
}
TEST(BigIntTest, Accumulator) {
fmt::internal::accumulator acc;
fmt::detail::accumulator acc;
EXPECT_EQ(acc.lower, 0);
EXPECT_EQ(acc.upper, 0);
acc.upper = 12;
@ -110,7 +110,7 @@ TEST(BigIntTest, Accumulator) {
EXPECT_EQ(static_cast<uint32_t>(acc), 34);
acc += 56;
EXPECT_EQ(acc.lower, 90);
acc += fmt::internal::max_value<uint64_t>();
acc += fmt::detail::max_value<uint64_t>();
EXPECT_EQ(acc.upper, 13);
EXPECT_EQ(acc.lower, 89);
acc >>= 32;
@ -262,7 +262,7 @@ TEST(FPTest, GetCachedPower) {
typedef std::numeric_limits<double> limits;
for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) {
int dec_exp = 0;
auto fp = fmt::internal::get_cached_power(exp, dec_exp);
auto fp = fmt::detail::get_cached_power(exp, dec_exp);
EXPECT_LE(exp, fp.e);
int dec_exp_step = 8;
EXPECT_LE(fp.e, exp + dec_exp_step * log2(10));
@ -271,8 +271,8 @@ TEST(FPTest, GetCachedPower) {
}
TEST(FPTest, GetRoundDirection) {
using fmt::internal::get_round_direction;
using fmt::internal::round_direction;
using fmt::detail::get_round_direction;
using fmt::detail::round_direction;
EXPECT_EQ(round_direction::down, get_round_direction(100, 50, 0));
EXPECT_EQ(round_direction::up, get_round_direction(100, 51, 0));
EXPECT_EQ(round_direction::down, get_round_direction(100, 40, 10));
@ -295,9 +295,9 @@ TEST(FPTest, GetRoundDirection) {
}
TEST(FPTest, FixedHandler) {
struct handler : fmt::internal::fixed_handler {
struct handler : fmt::detail::fixed_handler {
char buffer[10];
handler(int prec = 0) : fmt::internal::fixed_handler() {
handler(int prec = 0) : fmt::detail::fixed_handler() {
buf = buffer;
precision = prec;
}
@ -306,7 +306,7 @@ TEST(FPTest, FixedHandler) {
handler().on_digit('0', 100, 99, 0, exp, false);
EXPECT_THROW(handler().on_digit('0', 100, 100, 0, exp, false),
assertion_failure);
namespace digits = fmt::internal::digits;
namespace digits = fmt::detail::digits;
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 10, exp, false), digits::done);
// Check that divisor - error doesn't overflow.
EXPECT_EQ(handler(1).on_digit('0', 100, 10, 101, exp, false), digits::error);
@ -318,7 +318,7 @@ TEST(FPTest, FixedHandler) {
TEST(FPTest, GrisuFormatCompilesWithNonIEEEDouble) {
fmt::memory_buffer buf;
format_float(0.42, -1, fmt::internal::float_specs(), buf);
format_float(0.42, -1, fmt::detail::float_specs(), buf);
}
template <typename T> struct value_extractor {
@ -330,11 +330,11 @@ template <typename T> struct value_extractor {
#if FMT_USE_INT128
// Apple Clang does not define typeid for __int128_t and __uint128_t.
FMT_NORETURN T operator()(fmt::internal::int128_t) {
FMT_NORETURN T operator()(fmt::detail::int128_t) {
throw std::runtime_error("invalid type __int128_t");
}
FMT_NORETURN T operator()(fmt::internal::uint128_t) {
FMT_NORETURN T operator()(fmt::detail::uint128_t) {
throw std::runtime_error("invalid type __uint128_t");
}
#endif
@ -342,9 +342,9 @@ template <typename T> struct value_extractor {
TEST(FormatTest, ArgConverter) {
long long value = max_value<long long>();
auto arg = fmt::internal::make_arg<fmt::format_context>(value);
auto arg = fmt::detail::make_arg<fmt::format_context>(value);
fmt::visit_format_arg(
fmt::internal::arg_converter<long long, fmt::format_context>(arg, 'd'),
fmt::detail::arg_converter<long long, fmt::format_context>(arg, 'd'),
arg);
EXPECT_EQ(value, fmt::visit_format_arg(value_extractor<long long>(), arg));
}
@ -360,9 +360,9 @@ TEST(FormatTest, FormatNegativeNaN) {
TEST(FormatTest, StrError) {
char* message = nullptr;
char buffer[BUFFER_SIZE];
EXPECT_ASSERT(fmt::internal::safe_strerror(EDOM, message = nullptr, 0),
EXPECT_ASSERT(fmt::detail::safe_strerror(EDOM, message = nullptr, 0),
"invalid buffer");
EXPECT_ASSERT(fmt::internal::safe_strerror(EDOM, message = buffer, 0),
EXPECT_ASSERT(fmt::detail::safe_strerror(EDOM, message = buffer, 0),
"invalid buffer");
buffer[0] = 'x';
#if defined(_GNU_SOURCE) && !defined(__COVERITY__)
@ -374,18 +374,18 @@ TEST(FormatTest, StrError) {
#endif
int result =
fmt::internal::safe_strerror(error_code, message = buffer, BUFFER_SIZE);
fmt::detail::safe_strerror(error_code, message = buffer, BUFFER_SIZE);
EXPECT_EQ(result, 0);
std::size_t message_size = std::strlen(message);
size_t message_size = std::strlen(message);
EXPECT_GE(BUFFER_SIZE - 1u, message_size);
EXPECT_EQ(get_system_error(error_code), message);
// safe_strerror never uses buffer on MinGW.
#if !defined(__MINGW32__) && !defined(__sun)
result =
fmt::internal::safe_strerror(error_code, message = buffer, message_size);
fmt::detail::safe_strerror(error_code, message = buffer, message_size);
EXPECT_EQ(ERANGE, result);
result = fmt::internal::safe_strerror(error_code, message = buffer, 1);
result = fmt::detail::safe_strerror(error_code, message = buffer, 1);
EXPECT_EQ(buffer, message); // Message should point to buffer.
EXPECT_EQ(ERANGE, result);
EXPECT_STREQ("", message);
@ -397,51 +397,48 @@ TEST(FormatTest, FormatErrorCode) {
{
fmt::memory_buffer buffer;
format_to(buffer, "garbage");
fmt::internal::format_error_code(buffer, 42, "test");
fmt::detail::format_error_code(buffer, 42, "test");
EXPECT_EQ("test: " + msg, to_string(buffer));
}
{
fmt::memory_buffer buffer;
std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size() + 1,
'x');
fmt::internal::format_error_code(buffer, 42, prefix);
fmt::detail::format_error_code(buffer, 42, prefix);
EXPECT_EQ(msg, to_string(buffer));
}
int codes[] = {42, -1};
for (std::size_t i = 0, n = sizeof(codes) / sizeof(*codes); i < n; ++i) {
for (size_t i = 0, n = sizeof(codes) / sizeof(*codes); i < n; ++i) {
// Test maximum buffer size.
msg = fmt::format("error {}", codes[i]);
fmt::memory_buffer buffer;
std::string prefix(fmt::inline_buffer_size - msg.size() - sep.size(), 'x');
fmt::internal::format_error_code(buffer, codes[i], prefix);
fmt::detail::format_error_code(buffer, codes[i], prefix);
EXPECT_EQ(prefix + sep + msg, to_string(buffer));
std::size_t size = fmt::inline_buffer_size;
size_t size = fmt::inline_buffer_size;
EXPECT_EQ(size, buffer.size());
buffer.resize(0);
// Test with a message that doesn't fit into the buffer.
prefix += 'x';
fmt::internal::format_error_code(buffer, codes[i], prefix);
fmt::detail::format_error_code(buffer, codes[i], prefix);
EXPECT_EQ(msg, to_string(buffer));
}
}
TEST(FormatTest, CountCodePoints) {
#ifndef __cpp_char8_t
using fmt::char8_t;
#endif
EXPECT_EQ(
4, fmt::internal::count_code_points(
fmt::basic_string_view<fmt::internal::char8_type>(
reinterpret_cast<const fmt::internal::char8_type*>("ёжик"))));
EXPECT_EQ(4,
fmt::detail::count_code_points(
fmt::basic_string_view<fmt::detail::char8_type>(
reinterpret_cast<const fmt::detail::char8_type*>("ёжик"))));
}
// Tests fmt::internal::count_digits for integer type Int.
// Tests fmt::detail::count_digits for integer type Int.
template <typename Int> void test_count_digits() {
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::internal::count_digits(i));
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::detail::count_digits(i));
for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) {
n *= 10;
EXPECT_EQ(i, fmt::internal::count_digits(n - 1));
EXPECT_EQ(i + 1, fmt::internal::count_digits(n));
EXPECT_EQ(i, fmt::detail::count_digits(n - 1));
EXPECT_EQ(i + 1, fmt::detail::count_digits(n));
}
}
@ -450,11 +447,10 @@ TEST(UtilTest, CountDigits) {
test_count_digits<uint64_t>();
}
TEST(UtilTest, WriteUIntPtr) {
fmt::memory_buffer buf;
fmt::internal::writer writer(buf);
writer.write_pointer(
fmt::internal::fallback_uintptr(reinterpret_cast<void*>(0xface)),
nullptr);
EXPECT_EQ("0xface", to_string(buf));
TEST(UtilTest, WriteFallbackUIntPtr) {
std::string s;
fmt::detail::write_ptr<char>(
std::back_inserter(s),
fmt::detail::fallback_uintptr(reinterpret_cast<void*>(0xface)), nullptr);
EXPECT_EQ(s, "0xface");
}

View File

@ -35,10 +35,6 @@
#include "util.h"
#undef ERROR
#undef min
#undef max
using std::size_t;
using fmt::basic_memory_buffer;
using fmt::format;
@ -46,8 +42,8 @@ using fmt::format_error;
using fmt::memory_buffer;
using fmt::string_view;
using fmt::wmemory_buffer;
using fmt::internal::basic_writer;
using fmt::internal::max_value;
using fmt::wstring_view;
using fmt::detail::max_value;
using testing::Return;
using testing::StrictMock;
@ -101,47 +97,6 @@ void std_format(long double value, std::wstring& result) {
result = buffer;
}
#endif
// Checks if writing value to BasicWriter<Char> produces the same result
// as writing it to std::basic_ostringstream<Char>.
template <typename Char, typename T>
::testing::AssertionResult check_write(const T& value, const char* type) {
fmt::basic_memory_buffer<Char> buffer;
using range = fmt::buffer_range<Char>;
basic_writer<range> writer(buffer);
writer.write(value);
std::basic_string<Char> actual = to_string(buffer);
std::basic_string<Char> expected;
std_format(value, expected);
if (expected == actual) return ::testing::AssertionSuccess();
return ::testing::AssertionFailure()
<< "Value of: (Writer<" << type << ">() << value).str()\n"
<< " Actual: " << actual << "\n"
<< "Expected: " << expected << "\n";
}
struct AnyWriteChecker {
template <typename T>
::testing::AssertionResult operator()(const char*, const T& value) const {
::testing::AssertionResult result = check_write<char>(value, "char");
return result ? check_write<wchar_t>(value, "wchar_t") : result;
}
};
template <typename Char> struct WriteChecker {
template <typename T>
::testing::AssertionResult operator()(const char*, const T& value) const {
return check_write<Char>(value, "char");
}
};
// Checks if writing value to BasicWriter produces the same result
// as writing it to std::ostringstream both for char and wchar_t.
#define CHECK_WRITE(value) EXPECT_PRED_FORMAT1(AnyWriteChecker(), value)
#define CHECK_WRITE_CHAR(value) EXPECT_PRED_FORMAT1(WriteChecker<char>(), value)
#define CHECK_WRITE_WCHAR(value) \
EXPECT_PRED_FORMAT1(WriteChecker<wchar_t>(), value)
} // namespace
struct uint32_pair {
@ -149,10 +104,10 @@ struct uint32_pair {
};
TEST(UtilTest, BitCast) {
auto s = fmt::internal::bit_cast<uint32_pair>(uint64_t{42});
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), 42ull);
s = fmt::internal::bit_cast<uint32_pair>(uint64_t(~0ull));
EXPECT_EQ(fmt::internal::bit_cast<uint64_t>(s), ~0ull);
auto s = fmt::detail::bit_cast<uint32_pair>(uint64_t{42});
EXPECT_EQ(fmt::detail::bit_cast<uint64_t>(s), 42ull);
s = fmt::detail::bit_cast<uint32_pair>(uint64_t(~0ull));
EXPECT_EQ(fmt::detail::bit_cast<uint64_t>(s), ~0ull);
}
TEST(UtilTest, Increment) {
@ -177,18 +132,18 @@ TEST(UtilTest, ParseNonnegativeInt) {
fmt::string_view s = "10000000000";
auto begin = s.begin(), end = s.end();
EXPECT_THROW_MSG(
parse_nonnegative_int(begin, end, fmt::internal::error_handler()),
parse_nonnegative_int(begin, end, fmt::detail::error_handler()),
fmt::format_error, "number is too big");
s = "2147483649";
begin = s.begin();
end = s.end();
EXPECT_THROW_MSG(
parse_nonnegative_int(begin, end, fmt::internal::error_handler()),
parse_nonnegative_int(begin, end, fmt::detail::error_handler()),
fmt::format_error, "number is too big");
}
TEST(IteratorTest, CountingIterator) {
fmt::internal::counting_iterator it;
fmt::detail::counting_iterator it;
auto prev = it++;
EXPECT_EQ(prev.count(), 0);
EXPECT_EQ(it.count(), 1);
@ -196,7 +151,7 @@ TEST(IteratorTest, CountingIterator) {
TEST(IteratorTest, TruncatingIterator) {
char* p = nullptr;
fmt::internal::truncating_iterator<char*> it(p, 3);
fmt::detail::truncating_iterator<char*> it(p, 3);
auto prev = it++;
EXPECT_EQ(prev.base(), p);
EXPECT_EQ(it.base(), p + 1);
@ -205,7 +160,7 @@ TEST(IteratorTest, TruncatingIterator) {
TEST(IteratorTest, TruncatingBackInserter) {
std::string buffer;
auto bi = std::back_inserter(buffer);
fmt::internal::truncating_iterator<decltype(bi)> it(bi, 2);
fmt::detail::truncating_iterator<decltype(bi)> it(bi, 2);
*it++ = '4';
*it++ = '2';
*it++ = '1';
@ -214,20 +169,20 @@ TEST(IteratorTest, TruncatingBackInserter) {
}
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<
EXPECT_TRUE(fmt::detail::is_output_iterator<char*>::value);
EXPECT_FALSE(fmt::detail::is_output_iterator<const char*>::value);
EXPECT_FALSE(fmt::detail::is_output_iterator<std::string>::value);
EXPECT_TRUE(fmt::detail::is_output_iterator<
std::back_insert_iterator<std::string>>::value);
EXPECT_TRUE(fmt::internal::is_output_iterator<std::string::iterator>::value);
EXPECT_TRUE(fmt::detail::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);
fmt::detail::is_output_iterator<std::string::const_iterator>::value);
EXPECT_FALSE(fmt::detail::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);
fmt::detail::is_output_iterator<std::list<char>::iterator>::value);
EXPECT_FALSE(
fmt::detail::is_output_iterator<std::list<char>::const_iterator>::value);
EXPECT_FALSE(fmt::detail::is_output_iterator<uint32_pair>::value);
}
TEST(MemoryBufferTest, Ctor) {
@ -342,10 +297,10 @@ TEST(MemoryBufferTest, Grow) {
mock_allocator<int> alloc;
struct TestMemoryBuffer : Base {
TestMemoryBuffer(Allocator alloc) : Base(alloc) {}
void grow(std::size_t size) { Base::grow(size); }
void grow(size_t size) { Base::grow(size); }
} buffer((Allocator(&alloc)));
buffer.resize(7);
using fmt::internal::to_unsigned;
using fmt::detail::to_unsigned;
for (int i = 0; i < 7; ++i) buffer[to_unsigned(i)] = i * i;
EXPECT_EQ(10u, buffer.capacity());
int mem[20];
@ -370,7 +325,7 @@ TEST(MemoryBufferTest, Allocator) {
basic_memory_buffer<char, 10, TestAllocator> buffer2(
(TestAllocator(&alloc)));
EXPECT_EQ(&alloc, buffer2.get_allocator().get());
std::size_t size = 2 * fmt::inline_buffer_size;
size_t size = 2 * fmt::inline_buffer_size;
EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem));
buffer2.reserve(size);
EXPECT_CALL(alloc, deallocate(&mem, size));
@ -381,7 +336,7 @@ TEST(MemoryBufferTest, ExceptionInDeallocate) {
typedef allocator_ref<mock_allocator<char>> TestAllocator;
StrictMock<mock_allocator<char>> alloc;
basic_memory_buffer<char, 10, TestAllocator> buffer((TestAllocator(&alloc)));
std::size_t size = 2 * fmt::inline_buffer_size;
size_t size = 2 * fmt::inline_buffer_size;
std::vector<char> mem(size);
{
EXPECT_CALL(alloc, allocate(size)).WillOnce(Return(&mem[0]));
@ -396,27 +351,27 @@ TEST(MemoryBufferTest, ExceptionInDeallocate) {
EXPECT_THROW(buffer.reserve(2 * size), std::exception);
EXPECT_EQ(&mem2[0], &buffer[0]);
// Check that the data has been copied.
for (std::size_t i = 0; i < size; ++i) EXPECT_EQ('x', buffer[i]);
for (size_t i = 0; i < size; ++i) EXPECT_EQ('x', buffer[i]);
}
EXPECT_CALL(alloc, deallocate(&mem2[0], 2 * size));
}
TEST(UtilTest, UTF8ToUTF16) {
fmt::internal::utf8_to_utf16 u("лошадка");
fmt::detail::utf8_to_utf16 u("лошадка");
EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str());
EXPECT_EQ(7, u.size());
// U+10437 { DESERET SMALL LETTER YEE }
EXPECT_EQ(L"\xD801\xDC37", fmt::internal::utf8_to_utf16("𐐷").str());
EXPECT_THROW_MSG(fmt::internal::utf8_to_utf16("\xc3\x28"), std::runtime_error,
EXPECT_EQ(L"\xD801\xDC37", fmt::detail::utf8_to_utf16("𐐷").str());
EXPECT_THROW_MSG(fmt::detail::utf8_to_utf16("\xc3\x28"), std::runtime_error,
"invalid utf8");
EXPECT_THROW_MSG(fmt::internal::utf8_to_utf16(fmt::string_view("л", 1)),
EXPECT_THROW_MSG(fmt::detail::utf8_to_utf16(fmt::string_view("л", 1)),
std::runtime_error, "invalid utf8");
EXPECT_EQ(L"123456", fmt::internal::utf8_to_utf16("123456").str());
EXPECT_EQ(L"123456", fmt::detail::utf8_to_utf16("123456").str());
}
TEST(UtilTest, UTF8ToUTF16EmptyString) {
std::string s = "";
fmt::internal::utf8_to_utf16 u(s.c_str());
fmt::detail::utf8_to_utf16 u(s.c_str());
EXPECT_EQ(L"", u.str());
EXPECT_EQ(s.size(), u.size());
}
@ -478,101 +433,6 @@ TEST(StringViewTest, Ctor) {
EXPECT_EQ(4u, string_view(std::string("defg")).size());
}
TEST(WriterTest, Data) {
memory_buffer buf;
fmt::internal::writer w(buf);
w.write(42);
EXPECT_EQ("42", to_string(buf));
}
TEST(WriterTest, WriteInt) {
CHECK_WRITE(42);
CHECK_WRITE(-42);
CHECK_WRITE(static_cast<short>(12));
CHECK_WRITE(34u);
CHECK_WRITE(std::numeric_limits<int>::min());
CHECK_WRITE(max_value<int>());
CHECK_WRITE(max_value<unsigned>());
}
TEST(WriterTest, WriteLong) {
CHECK_WRITE(56l);
CHECK_WRITE(78ul);
CHECK_WRITE(std::numeric_limits<long>::min());
CHECK_WRITE(max_value<long>());
CHECK_WRITE(max_value<unsigned long>());
}
TEST(WriterTest, WriteLongLong) {
CHECK_WRITE(56ll);
CHECK_WRITE(78ull);
CHECK_WRITE(std::numeric_limits<long long>::min());
CHECK_WRITE(max_value<long long>());
CHECK_WRITE(max_value<unsigned long long>());
}
TEST(WriterTest, WriteDouble) {
CHECK_WRITE(4.2);
CHECK_WRITE(-4.2);
auto min = std::numeric_limits<double>::min();
auto max = max_value<double>();
if (fmt::internal::use_grisu<double>()) {
EXPECT_EQ("2.2250738585072014e-308", fmt::format("{}", min));
EXPECT_EQ("1.7976931348623157e+308", fmt::format("{}", max));
} else {
CHECK_WRITE(min);
CHECK_WRITE(max);
}
}
TEST(WriterTest, WriteLongDouble) {
CHECK_WRITE(4.2l);
CHECK_WRITE_CHAR(-4.2l);
std::wstring str;
std_format(4.2l, str);
if (str[0] != '-')
CHECK_WRITE_WCHAR(-4.2l);
else
fmt::print("warning: long double formatting with std::swprintf is broken");
auto min = std::numeric_limits<long double>::min();
auto max = max_value<long double>();
if (fmt::internal::use_grisu<long double>()) {
EXPECT_EQ("2.2250738585072014e-308", fmt::format("{}", min));
EXPECT_EQ("1.7976931348623157e+308", fmt::format("{}", max));
} else {
CHECK_WRITE(min);
CHECK_WRITE(max);
}
}
TEST(WriterTest, WriteDoubleAtBufferBoundary) {
memory_buffer buf;
fmt::internal::writer writer(buf);
for (int i = 0; i < 100; ++i) writer.write(1.23456789);
}
TEST(WriterTest, WriteDoubleWithFilledBuffer) {
memory_buffer buf;
fmt::internal::writer writer(buf);
// Fill the buffer.
for (int i = 0; i < fmt::inline_buffer_size; ++i) writer.write(' ');
writer.write(1.2);
fmt::string_view sv(buf.data(), buf.size());
sv.remove_prefix(fmt::inline_buffer_size);
EXPECT_EQ("1.2", sv);
}
TEST(WriterTest, WriteChar) { CHECK_WRITE('a'); }
TEST(WriterTest, WriteWideChar) { CHECK_WRITE_WCHAR(L'a'); }
TEST(WriterTest, WriteString) {
CHECK_WRITE_CHAR("abc");
CHECK_WRITE_WCHAR("abc");
}
TEST(WriterTest, WriteWideString) { CHECK_WRITE_WCHAR(L"abc"); }
TEST(FormatToTest, FormatWithoutArgs) {
std::string s;
fmt::format_to(std::back_inserter(s), "test");
@ -639,15 +499,14 @@ 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, "argument index out of range");
EXPECT_THROW_MSG(format("{0}"), format_error, "argument not found");
EXPECT_THROW_MSG(format("{00}", 42), format_error, "invalid format string");
char format_str[BUFFER_SIZE];
safe_sprintf(format_str, "{%u", INT_MAX);
EXPECT_THROW_MSG(format(format_str), format_error, "invalid format string");
safe_sprintf(format_str, "{%u}", INT_MAX);
EXPECT_THROW_MSG(format(format_str), format_error,
"argument index out of range");
EXPECT_THROW_MSG(format(format_str), format_error, "argument not found");
safe_sprintf(format_str, "{%u", INT_MAX + 1u);
EXPECT_THROW_MSG(format(format_str), format_error, "number is too big");
@ -672,13 +531,13 @@ template <> struct TestFormat<0> {
TEST(FormatterTest, ManyArgs) {
EXPECT_EQ("19", TestFormat<20>::format("{19}"));
EXPECT_THROW_MSG(TestFormat<20>::format("{20}"), format_error,
"argument index out of range");
"argument not found");
EXPECT_THROW_MSG(TestFormat<21>::format("{21}"), format_error,
"argument index out of range");
enum { max_packed_args = fmt::internal::max_packed_args };
"argument not found");
enum { max_packed_args = fmt::detail::max_packed_args };
std::string format_str = fmt::format("{{{}}}", max_packed_args + 1);
EXPECT_THROW_MSG(TestFormat<max_packed_args>::format(format_str),
format_error, "argument index out of range");
format_error, "argument not found");
}
TEST(FormatterTest, NamedArg) {
@ -707,7 +566,7 @@ TEST(FormatterTest, AutoArgIndex) {
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{:.{0}}", 1.2345, 2), format_error,
"cannot switch from automatic to manual argument indexing");
EXPECT_THROW_MSG(format("{}"), format_error, "argument index out of range");
EXPECT_THROW_MSG(format("{}"), format_error, "argument not found");
}
TEST(FormatterTest, EmptySpecs) { EXPECT_EQ("42", format("{0:}", 42)); }
@ -747,37 +606,7 @@ TEST(FormatterTest, RightAlign) {
}
#if FMT_NUMERIC_ALIGN
TEST(FormatterTest, NumericAlign) {
EXPECT_EQ(" 42", format("{0:=4}", 42));
EXPECT_EQ("+ 42", format("{0:=+4}", 42));
EXPECT_EQ(" 42", format("{0:=4o}", 042));
EXPECT_EQ("+ 42", format("{0:=+4o}", 042));
EXPECT_EQ(" 42", format("{0:=4x}", 0x42));
EXPECT_EQ("+ 42", format("{0:=+4x}", 0x42));
EXPECT_EQ("- 42", format("{0:=5}", -42));
EXPECT_EQ(" 42", format("{0:=5}", 42u));
EXPECT_EQ("- 42", format("{0:=5}", -42l));
EXPECT_EQ(" 42", format("{0:=5}", 42ul));
EXPECT_EQ("- 42", format("{0:=5}", -42ll));
EXPECT_EQ(" 42", format("{0:=5}", 42ull));
EXPECT_EQ("- 42.0", format("{0:=7}", -42.0));
EXPECT_EQ("- 42.0", format("{0:=7}", -42.0l));
EXPECT_THROW_MSG(format("{0:=5", 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG(format("{0:=5}", 'c'), format_error,
"invalid format specifier for char");
EXPECT_THROW_MSG(format("{0:=5}", "abc"), format_error,
"format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:=8}", reinterpret_cast<void*>(0xface)),
format_error, "format specifier requires numeric argument");
EXPECT_EQ(" 1.0", fmt::format("{:= }", 1.0));
}
TEST(FormatToTest, FormatToNonbackInsertIteratorWithSignAndNumericAlignment) {
char buffer[16] = {};
fmt::format_to(fmt::internal::make_checked(buffer, 16), "{: =+}", 42.0);
EXPECT_STREQ("+42.0", buffer);
}
TEST(FormatterTest, NumericAlign) { EXPECT_EQ("0042", format("{0:=4}", 42)); }
#endif
TEST(FormatterTest, CenterAlign) {
@ -968,7 +797,7 @@ TEST(FormatterTest, Width) {
safe_sprintf(format_str, "{0:%u", UINT_MAX);
increment(format_str + 3);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
std::size_t size = std::strlen(format_str);
size_t size = std::strlen(format_str);
format_str[size] = '}';
format_str[size + 1] = 0;
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
@ -998,7 +827,7 @@ TEST(FormatterTest, RuntimeWidth) {
safe_sprintf(format_str, "{0:{%u", UINT_MAX);
increment(format_str + 4);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
std::size_t size = std::strlen(format_str);
size_t size = std::strlen(format_str);
format_str[size] = '}';
format_str[size + 1] = 0;
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
@ -1010,8 +839,7 @@ TEST(FormatterTest, RuntimeWidth) {
EXPECT_THROW_MSG(format("{0:{}", 0), format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG(format("{0:{?}}", 0), format_error, "invalid format string");
EXPECT_THROW_MSG(format("{0:{1}}", 0), format_error,
"argument index out of range");
EXPECT_THROW_MSG(format("{0:{1}}", 0), format_error, "argument not found");
EXPECT_THROW_MSG(format("{0:{0:}}", 0), format_error,
"invalid format string");
@ -1052,7 +880,7 @@ TEST(FormatterTest, Precision) {
safe_sprintf(format_str, "{0:.%u", UINT_MAX);
increment(format_str + 4);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
std::size_t size = std::strlen(format_str);
size_t size = std::strlen(format_str);
format_str[size] = '}';
format_str[size + 1] = 0;
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
@ -1125,6 +953,7 @@ TEST(FormatterTest, Precision) {
format("{:.838A}", -2.14001164E+38));
EXPECT_EQ("123.", format("{:#.0f}", 123.0));
EXPECT_EQ("1.23", format("{:.02f}", 1.234));
EXPECT_EQ("0.001", format("{:.1g}", 0.001));
EXPECT_THROW_MSG(format("{0:.2}", reinterpret_cast<void*>(0xcafe)),
format_error,
@ -1132,7 +961,7 @@ TEST(FormatterTest, Precision) {
EXPECT_THROW_MSG(format("{0:.2f}", reinterpret_cast<void*>(0xcafe)),
format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{:.{}e}", 42.0, fmt::internal::max_value<int>()),
EXPECT_THROW_MSG(format("{:.{}e}", 42.0, fmt::detail::max_value<int>()),
format_error, "number is too big");
EXPECT_EQ("st", format("{0:.2}", "str"));
@ -1143,7 +972,7 @@ TEST(FormatterTest, RuntimePrecision) {
safe_sprintf(format_str, "{0:.{%u", UINT_MAX);
increment(format_str + 5);
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
std::size_t size = std::strlen(format_str);
size_t size = std::strlen(format_str);
format_str[size] = '}';
format_str[size + 1] = 0;
EXPECT_THROW_MSG(format(format_str, 0), format_error, "number is too big");
@ -1158,8 +987,7 @@ TEST(FormatterTest, RuntimePrecision) {
"invalid format string");
EXPECT_THROW_MSG(format("{0:.{1}", 0, 0), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.{1}}", 0), format_error,
"argument index out of range");
EXPECT_THROW_MSG(format("{0:.{1}}", 0), format_error, "argument not found");
EXPECT_THROW_MSG(format("{0:.{0:}}", 0), format_error,
"invalid format string");
@ -1254,7 +1082,8 @@ TEST(FormatterTest, FormatShort) {
TEST(FormatterTest, FormatInt) {
EXPECT_THROW_MSG(format("{0:v", 42), format_error,
"missing '}' in format string");
check_unknown_types(42, "bBdoxXn", "integer");
check_unknown_types(42, "bBdoxXnLc", "integer");
EXPECT_EQ("x", format("{:c}", static_cast<int>('x')));
}
TEST(FormatterTest, FormatBin) {
@ -1395,6 +1224,7 @@ TEST(FormatterTest, FormatOct) {
TEST(FormatterTest, FormatIntLocale) {
EXPECT_EQ("1234", format("{:n}", 1234));
EXPECT_EQ("1234", format("{:L}", 1234));
}
struct ConvertibleToLongLong {
@ -1410,7 +1240,7 @@ TEST(FormatterTest, FormatFloat) {
}
TEST(FormatterTest, FormatDouble) {
check_unknown_types(1.2, "eEfFgGaAn%", "double");
check_unknown_types(1.2, "eEfFgGaAnL%", "double");
EXPECT_EQ("0.0", format("{:}", 0.0));
EXPECT_EQ("0.000000", format("{:f}", 0.0));
EXPECT_EQ("0", format("{:g}", 0.0));
@ -1419,6 +1249,7 @@ TEST(FormatterTest, FormatDouble) {
EXPECT_EQ("392.65", format("{:G}", 392.65));
EXPECT_EQ("392.650000", format("{:f}", 392.65));
EXPECT_EQ("392.650000", format("{:F}", 392.65));
EXPECT_EQ("42", format("{:L}", 42.0));
char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "%e", 392.65);
EXPECT_EQ(buffer, format("{0:e}", 392.65));
@ -1489,7 +1320,7 @@ TEST(FormatterTest, FormatLongDouble) {
}
TEST(FormatterTest, FormatChar) {
const char types[] = "cbBdoxXn";
const char types[] = "cbBdoxXnL";
check_unknown_types('a', types, "char");
EXPECT_EQ("a", format("{0}", 'a'));
EXPECT_EQ("z", format("{0:c}", 'z'));
@ -1605,6 +1436,40 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
}
#endif
// std::is_constructible is broken in MSVC until version 2015.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1900
struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; }
};
TEST(FormatTest, FormatExplicitlyConvertibleToWStringView) {
EXPECT_EQ(L"foo",
fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
}
#endif
namespace fake_qt {
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()); }
private:
std::shared_ptr<std::wstring> s_;
};
fmt::basic_string_view<wchar_t> to_string_view(const QString& s) FMT_NOEXCEPT {
return {s.utf16(), static_cast<size_t>(s.size())};
}
} // namespace fake_qt
TEST(FormatTest, FormatForeignStrings) {
using fake_qt::QString;
EXPECT_EQ(fmt::format(QString(L"{}"), 42), L"42");
EXPECT_EQ(fmt::format(QString(L"{}"), QString(L"42")), L"42");
}
FMT_BEGIN_NAMESPACE
template <> struct formatter<Date> {
template <typename ParseContext>
@ -1646,7 +1511,7 @@ TEST(FormatterTest, CustomFormat) {
TEST(FormatterTest, CustomFormatTo) {
char buf[10] = {};
auto end =
&*fmt::format_to(fmt::internal::make_checked(buf, 10), "{}", Answer());
&*fmt::format_to(fmt::detail::make_checked(buf, 10), "{}", Answer());
EXPECT_EQ(end, buf + 2);
EXPECT_STREQ(buf, "42");
}
@ -1755,6 +1620,8 @@ TEST(FormatTest, Print) {
EXPECT_WRITE(stderr, fmt::print(stderr, "Don't {}!", "panic"),
"Don't panic!");
#endif
// Check that the wide print overload compiles.
if (fmt::detail::const_check(false)) fmt::print(L"test");
}
TEST(FormatTest, Variadic) {
@ -1765,9 +1632,9 @@ TEST(FormatTest, Variadic) {
TEST(FormatTest, Dynamic) {
typedef fmt::format_context ctx;
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>("abc1"));
args.emplace_back(fmt::internal::make_arg<ctx>(1.5f));
args.emplace_back(fmt::detail::make_arg<ctx>(42));
args.emplace_back(fmt::detail::make_arg<ctx>("abc1"));
args.emplace_back(fmt::detail::make_arg<ctx>(1.5f));
std::string result = fmt::vformat(
"{} and {} and {}",
@ -1853,10 +1720,23 @@ TEST(FormatTest, UnpackedArgs) {
struct string_like {};
fmt::string_view to_string_view(string_like) { return "foo"; }
constexpr char with_null[3] = {'{', '}', '\0'};
constexpr char no_null[2] = {'{', '}'};
TEST(FormatTest, CompileTimeString) {
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
EXPECT_EQ(L"42", fmt::format(FMT_STRING(L"{}"), 42));
EXPECT_EQ("foo", fmt::format(FMT_STRING("{}"), string_like()));
(void)with_null;
(void)no_null;
#if __cplusplus >= 201703L
EXPECT_EQ("42", fmt::format(FMT_STRING(with_null), 42));
EXPECT_EQ("42", fmt::format(FMT_STRING(no_null), 42));
#endif
#if defined(FMT_USE_STRING_VIEW) && __cplusplus >= 201703L
EXPECT_EQ("42", fmt::format(FMT_STRING(std::string_view("{}")), 42));
EXPECT_EQ(L"42", fmt::format(FMT_STRING(std::wstring_view(L"{}")), 42));
#endif
}
TEST(FormatTest, CustomFormatCompileTimeString) {
@ -1926,10 +1806,10 @@ TEST(FormatTest, StrongEnum) {
}
#endif
using buffer_range = fmt::buffer_range<char>;
using buffer_iterator = fmt::format_context::iterator;
class mock_arg_formatter
: public fmt::internal::arg_formatter_base<buffer_range> {
: public fmt::detail::arg_formatter_base<buffer_iterator, char> {
private:
#if FMT_USE_INT128
MOCK_METHOD1(call, void(__int128_t value));
@ -1938,24 +1818,23 @@ class mock_arg_formatter
#endif
public:
typedef fmt::internal::arg_formatter_base<buffer_range> base;
typedef buffer_range range;
using base = fmt::detail::arg_formatter_base<buffer_iterator, char>;
mock_arg_formatter(fmt::format_context& ctx, fmt::format_parse_context*,
fmt::format_specs* s = nullptr)
: base(fmt::internal::get_container(ctx.out()), s, ctx.locale()) {
fmt::format_specs* s = nullptr, const char* = nullptr)
: base(ctx.out(), s, ctx.locale()) {
EXPECT_CALL(*this, call(42));
}
template <typename T>
typename std::enable_if<fmt::internal::is_integral<T>::value, iterator>::type
typename std::enable_if<fmt::detail::is_integral<T>::value, iterator>::type
operator()(T value) {
call(value);
return base::operator()(value);
}
template <typename T>
typename std::enable_if<!fmt::internal::is_integral<T>::value, iterator>::type
typename std::enable_if<!fmt::detail::is_integral<T>::value, iterator>::type
operator()(T value) {
return base::operator()(value);
}
@ -1967,7 +1846,9 @@ class mock_arg_formatter
static void custom_vformat(fmt::string_view format_str, fmt::format_args args) {
fmt::memory_buffer buffer;
fmt::vformat_to<mock_arg_formatter>(buffer, format_str, args);
fmt::internal::buffer<char>& base = buffer;
fmt::vformat_to<mock_arg_formatter>(std::back_inserter(base), format_str,
args);
}
template <typename... Args>
@ -2141,7 +2022,7 @@ struct test_arg_id_handler {
template <size_t N>
FMT_CONSTEXPR test_arg_id_handler parse_arg_id(const char (&s)[N]) {
test_arg_id_handler h;
fmt::internal::parse_arg_id(s, s + N, h);
fmt::detail::parse_arg_id(s, s + N, h);
return h;
}
@ -2162,9 +2043,9 @@ struct test_format_specs_handler {
fmt::align_t align = fmt::align::none;
char fill = 0;
int width = 0;
fmt::internal::arg_ref<char> width_ref;
fmt::detail::arg_ref<char> width_ref;
int precision = 0;
fmt::internal::arg_ref<char> precision_ref;
fmt::detail::arg_ref<char> precision_ref;
char type = 0;
// Workaround for MSVC2017 bug that results in "expression did not evaluate
@ -2190,12 +2071,12 @@ struct test_format_specs_handler {
FMT_CONSTEXPR void on_zero() { res = ZERO; }
FMT_CONSTEXPR void on_width(int w) { width = w; }
FMT_CONSTEXPR void on_dynamic_width(fmt::internal::auto_id) {}
FMT_CONSTEXPR void on_dynamic_width(fmt::detail::auto_id) {}
FMT_CONSTEXPR void on_dynamic_width(int index) { width_ref = index; }
FMT_CONSTEXPR void on_dynamic_width(string_view) {}
FMT_CONSTEXPR void on_precision(int p) { precision = p; }
FMT_CONSTEXPR void on_dynamic_precision(fmt::internal::auto_id) {}
FMT_CONSTEXPR void on_dynamic_precision(fmt::detail::auto_id) {}
FMT_CONSTEXPR void on_dynamic_precision(int index) { precision_ref = index; }
FMT_CONSTEXPR void on_dynamic_precision(string_view) {}
@ -2207,7 +2088,7 @@ struct test_format_specs_handler {
template <size_t N>
FMT_CONSTEXPR test_format_specs_handler parse_test_specs(const char (&s)[N]) {
test_format_specs_handler h;
fmt::internal::parse_format_specs(s, s + N, h);
fmt::detail::parse_format_specs(s, s + N, h);
return h;
}
@ -2241,8 +2122,9 @@ struct test_parse_context {
};
struct test_context {
typedef char char_type;
typedef fmt::basic_format_arg<test_context> format_arg;
using char_type = char;
using format_arg = fmt::basic_format_arg<test_context>;
using parse_context_type = fmt::format_parse_context;
template <typename T> struct formatter_type {
typedef fmt::formatter<T, char_type> type;
@ -2250,7 +2132,7 @@ struct test_context {
template <typename Id>
FMT_CONSTEXPR fmt::basic_format_arg<test_context> arg(Id id) {
return fmt::internal::make_arg<test_context>(id);
return fmt::detail::make_arg<test_context>(id);
}
void on_error(const char*) {}
@ -2263,7 +2145,7 @@ FMT_CONSTEXPR fmt::format_specs parse_specs(const char (&s)[N]) {
auto specs = fmt::format_specs();
auto parse_ctx = test_parse_context();
auto ctx = test_context();
fmt::internal::specs_handler<test_parse_context, test_context> h(
fmt::detail::specs_handler<test_parse_context, test_context> h(
specs, parse_ctx, ctx);
parse_format_specs(s, s + N, h);
return specs;
@ -2287,11 +2169,11 @@ TEST(FormatTest, ConstexprSpecsHandler) {
}
template <size_t N>
FMT_CONSTEXPR fmt::internal::dynamic_format_specs<char> parse_dynamic_specs(
FMT_CONSTEXPR fmt::detail::dynamic_format_specs<char> parse_dynamic_specs(
const char (&s)[N]) {
fmt::internal::dynamic_format_specs<char> specs;
fmt::detail::dynamic_format_specs<char> specs;
test_parse_context ctx{};
fmt::internal::dynamic_specs_handler<test_parse_context> h(specs, ctx);
fmt::detail::dynamic_specs_handler<test_parse_context> h(specs, ctx);
parse_format_specs(s, s + N, h);
return specs;
}
@ -2315,8 +2197,8 @@ TEST(FormatTest, ConstexprDynamicSpecsHandler) {
template <size_t N>
FMT_CONSTEXPR test_format_specs_handler check_specs(const char (&s)[N]) {
fmt::internal::specs_checker<test_format_specs_handler> checker(
test_format_specs_handler(), fmt::internal::type::double_type);
fmt::detail::specs_checker<test_format_specs_handler> checker(
test_format_specs_handler(), fmt::detail::type::double_type);
parse_format_specs(s, s + N, checker);
return checker;
}
@ -2341,13 +2223,14 @@ TEST(FormatTest, ConstexprSpecsChecker) {
struct test_format_string_handler {
FMT_CONSTEXPR void on_text(const char*, const char*) {}
FMT_CONSTEXPR void on_arg_id() {}
FMT_CONSTEXPR int on_arg_id() { return 0; }
template <typename T> FMT_CONSTEXPR void on_arg_id(T) {}
template <typename T> FMT_CONSTEXPR int on_arg_id(T) { return 0; }
FMT_CONSTEXPR void on_replacement_field(const char*) {}
FMT_CONSTEXPR void on_replacement_field(int, const char*) {}
FMT_CONSTEXPR const char* on_format_specs(const char* begin, const char*) {
FMT_CONSTEXPR const char* on_format_specs(int, const char* begin,
const char*) {
return begin;
}
@ -2358,7 +2241,7 @@ struct test_format_string_handler {
template <size_t N> FMT_CONSTEXPR bool parse_string(const char (&s)[N]) {
test_format_string_handler h;
fmt::internal::parse_format_string<true>(fmt::string_view(s, N - 1), h);
fmt::detail::parse_format_string<true>(fmt::string_view(s, N - 1), h);
return !h.error;
}
@ -2402,8 +2285,10 @@ FMT_CONSTEXPR bool equal(const char* s1, const char* s2) {
template <typename... Args>
FMT_CONSTEXPR bool test_error(const char* fmt, const char* expected_error) {
const char* actual_error = nullptr;
fmt::internal::do_check_format_string<char, test_error_handler, Args...>(
string_view(fmt, len(fmt)), test_error_handler(actual_error));
string_view s(fmt, len(fmt));
fmt::detail::format_string_checker<char, test_error_handler, Args...> checker(
s, test_error_handler(actual_error));
fmt::detail::parse_format_string<true>(s, checker);
return equal(actual_error, expected_error);
}
@ -2416,12 +2301,12 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR_NOARGS("foo", nullptr);
EXPECT_ERROR_NOARGS("}", "unmatched '}' in format string");
EXPECT_ERROR("{0:s", "unknown format specifier", Date);
# if FMT_MSC_VER >= 1916
// This causes an internal compiler error in MSVC2017.
# if !FMT_MSC_VER || FMT_MSC_VER >= 1916
// This causes an detail compiler error in MSVC2017.
EXPECT_ERROR("{:{<}", "invalid fill character '{'", int);
EXPECT_ERROR("{:10000000000}", "number is too big", int);
EXPECT_ERROR("{:.10000000000}", "number is too big", int);
EXPECT_ERROR_NOARGS("{:x}", "argument index out of range");
EXPECT_ERROR_NOARGS("{:x}", "argument not found");
# if FMT_NUMERIC_ALIGN
EXPECT_ERROR("{0:=5", "unknown format specifier", int);
EXPECT_ERROR("{:=}", "format specifier requires numeric argument",
@ -2440,6 +2325,8 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("{:+}", "format specifier requires signed argument", unsigned);
EXPECT_ERROR("{:-}", "format specifier requires signed argument", unsigned);
EXPECT_ERROR("{: }", "format specifier requires signed argument", unsigned);
EXPECT_ERROR("{:{}}", "argument not found", int);
EXPECT_ERROR("{:.{}}", "argument not found", double);
EXPECT_ERROR("{:.2}", "precision not allowed for this argument type", int);
EXPECT_ERROR("{:s}", "invalid type specifier", int);
EXPECT_ERROR("{:s}", "invalid type specifier", bool);
@ -2462,8 +2349,8 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("{:.{0x}}", "invalid format string", int);
EXPECT_ERROR("{:.{-}}", "invalid format string", int);
EXPECT_ERROR("{:.x}", "missing precision specifier", int);
EXPECT_ERROR_NOARGS("{}", "argument index out of range");
EXPECT_ERROR("{1}", "argument index out of range", int);
EXPECT_ERROR_NOARGS("{}", "argument not found");
EXPECT_ERROR("{1}", "argument not found", int);
EXPECT_ERROR("{1}{}",
"cannot switch from manual to automatic argument indexing", int,
int);
@ -2474,7 +2361,7 @@ TEST(FormatTest, FormatStringErrors) {
TEST(FormatTest, VFormatTo) {
typedef fmt::format_context context;
fmt::basic_format_arg<context> arg = fmt::internal::make_arg<context>(42);
fmt::basic_format_arg<context> arg = fmt::detail::make_arg<context>(42);
fmt::basic_format_args<context> args(&arg, 1);
std::string s;
fmt::vformat_to(std::back_inserter(s), "{}", args);
@ -2484,7 +2371,7 @@ TEST(FormatTest, VFormatTo) {
EXPECT_EQ("42", s);
typedef fmt::wformat_context wcontext;
fmt::basic_format_arg<wcontext> warg = fmt::internal::make_arg<wcontext>(42);
fmt::basic_format_arg<wcontext> warg = fmt::detail::make_arg<wcontext>(42);
fmt::basic_format_args<wcontext> wargs(&warg, 1);
std::wstring w;
fmt::vformat_to(std::back_inserter(w), L"{}", wargs);
@ -2512,7 +2399,7 @@ TEST(FormatTest, EmphasisNonHeaderOnly) {
}
TEST(FormatTest, CharTraitsIsNotAmbiguous) {
// Test that we don't inject internal names into the std namespace.
// Test that we don't inject detail names into the std namespace.
using namespace std;
char_traits<char>::char_type c;
(void)c;
@ -2550,13 +2437,12 @@ template <typename S> std::string from_u8str(const S& str) {
}
TEST(FormatTest, FormatUTF8Precision) {
using str_type = std::basic_string<fmt::internal::char8_type>;
str_type format(
reinterpret_cast<const fmt::internal::char8_type*>(u8"{:.4}"));
str_type str(reinterpret_cast<const fmt::internal::char8_type*>(
using str_type = std::basic_string<fmt::detail::char8_type>;
str_type format(reinterpret_cast<const fmt::detail::char8_type*>(u8"{:.4}"));
str_type str(reinterpret_cast<const fmt::detail::char8_type*>(
u8"caf\u00e9s")); // cafés
auto result = fmt::format(format, str);
EXPECT_EQ(fmt::internal::count_code_points(result), 4);
EXPECT_EQ(fmt::detail::count_code_points(result), 4);
EXPECT_EQ(result.size(), 5);
EXPECT_EQ(from_u8str(result), from_u8str(str.substr(0, 5)));
}

View File

@ -7,14 +7,19 @@ in fmt. It is a part of the continous fuzzing at
The source code is modified to make the fuzzing possible without locking up on
resource exhaustion:
```cpp
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
#ifdef FMT_FUZZ
if(spec.precision>100000) {
throw std::runtime_error("fuzz mode - avoiding large precision");
}
#endif
```
This macro is the defacto standard for making fuzzing practically possible, see
[the libFuzzer documentation](https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode).
This macro `FMT_FUZZ` is enabled on OSS-Fuzz builds and makes fuzzing
practically possible. It is used in fmt code to prevent resource exhaustion in
fuzzing mode.
The macro `FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION` is the
defacto standard for making fuzzing practically possible to disable certain
fuzzing-unfriendly features (for example, randomness), see [the libFuzzer
documentation](https://llvm.org/docs/LibFuzzer.html#fuzzer-friendly-build-mode).
## Running the fuzzers locally

View File

@ -25,7 +25,7 @@ void invoke_inner(fmt::string_view formatstring, const Item item) {
// Item is the underlying type for duration (int, long etc)
template <typename Item>
void invoke_outer(const uint8_t* Data, std::size_t Size, const int scaling) {
void invoke_outer(const uint8_t* Data, size_t Size, const int scaling) {
// always use a fixed location of the data
using fmt_fuzzer::Nfixed;
@ -97,7 +97,7 @@ void invoke_outer(const uint8_t* Data, std::size_t Size, const int scaling) {
// doit_impl<Item,std::yotta>(buf.data(),item);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 4) {
return 0;
}

View File

@ -4,7 +4,7 @@
#include <vector>
#include "fuzzer_common.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size);
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size);
int main(int argc, char* argv[]) {
for (int i = 1; i < argc; ++i) {
std::ifstream in(argv[i]);
@ -13,7 +13,7 @@ int main(int argc, char* argv[]) {
const auto pos = in.tellg();
assert(pos >= 0);
in.seekg(0, std::ios_base::beg);
std::vector<char> buf(static_cast<std::size_t>(pos));
std::vector<char> buf(static_cast<size_t>(pos));
in.read(buf.data(), static_cast<long>(buf.size()));
assert(in.gcount() == pos);
LLVMFuzzerTestOneInput(fmt_fuzzer::as_bytes(buf.data()), buf.size());

View File

@ -10,7 +10,7 @@
#include "fuzzer_common.h"
template <typename Item1>
void invoke_fmt(const uint8_t* Data, std::size_t Size, unsigned int argsize) {
void invoke_fmt(const uint8_t* Data, size_t Size, unsigned int argsize) {
constexpr auto N1 = sizeof(Item1);
static_assert(N1 <= fmt_fuzzer::Nfixed, "Nfixed too small");
if (Size <= fmt_fuzzer::Nfixed) {
@ -29,11 +29,11 @@ void invoke_fmt(const uint8_t* Data, std::size_t Size, unsigned int argsize) {
// allocating buffers separately is slower, but increases chances
// of detecting memory errors
#if FMT_FUZZ_SEPARATE_ALLOCATION
std::vector<char> argnamebuffer(argsize);
std::vector<char> argnamebuffer(argsize + 1);
std::memcpy(argnamebuffer.data(), Data, argsize);
auto argname = fmt::string_view(argnamebuffer.data(), argsize);
auto argname = argnamebuffer.data();
#else
auto argname = fmt::string_view(fmt_fuzzer::as_chars(Data), argsize);
auto argname = fmt_fuzzer::as_chars(Data);
#endif
Data += argsize;
Size -= argsize;
@ -105,7 +105,7 @@ template <typename Callback> void invoke(int index, Callback callback) {
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 3) {
return 0;
}

View File

@ -13,7 +13,7 @@
using fmt_fuzzer::Nfixed;
template <typename Item>
void invoke_fmt(const uint8_t* Data, std::size_t Size) {
void invoke_fmt(const uint8_t* Data, size_t Size) {
constexpr auto N = sizeof(Item);
static_assert(N <= Nfixed, "Nfixed is too small");
if (Size <= Nfixed) {
@ -40,7 +40,7 @@ void invoke_fmt(const uint8_t* Data, std::size_t Size) {
#endif
}
void invoke_fmt_time(const uint8_t* Data, std::size_t Size) {
void invoke_fmt_time(const uint8_t* Data, size_t Size) {
using Item = std::time_t;
constexpr auto N = sizeof(Item);
static_assert(N <= Nfixed, "Nfixed too small");
@ -69,7 +69,7 @@ void invoke_fmt_time(const uint8_t* Data, std::size_t Size) {
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 3) {
return 0;
}

View File

@ -10,7 +10,7 @@
using fmt_fuzzer::Nfixed;
template <typename Item1, typename Item2>
void invoke_fmt(const uint8_t* Data, std::size_t Size) {
void invoke_fmt(const uint8_t* Data, size_t Size) {
constexpr auto N1 = sizeof(Item1);
constexpr auto N2 = sizeof(Item2);
static_assert(N1 <= Nfixed, "size1 exceeded");
@ -90,7 +90,7 @@ template <typename Callback> void invoke(int index, Callback callback) {
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 3) {
return 0;
}

View File

@ -10,7 +10,7 @@
constexpr auto Nfixed = fmt_fuzzer::Nfixed;
template <typename Item1, typename Item2>
void invoke_fmt(const uint8_t* Data, std::size_t Size) {
void invoke_fmt(const uint8_t* Data, size_t Size) {
constexpr auto N1 = sizeof(Item1);
constexpr auto N2 = sizeof(Item2);
static_assert(N1 <= Nfixed, "size1 exceeded");
@ -86,7 +86,7 @@ template <typename Callback> void invoke(int index, Callback callback) {
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
if (Size <= 3) {
return 0;
}

View File

@ -344,6 +344,7 @@ class GTEST_API_ SingleFailureChecker {
# include <sys/mman.h> // NOLINT
# include <sys/time.h> // NOLINT
# include <unistd.h> // NOLINT
# include <string>
#elif GTEST_OS_SYMBIAN
@ -7728,6 +7729,7 @@ InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() {
# include <sys/syslimits.h>
#else
# include <limits.h>
# include <climits> // Some Linux distributions define PATH_MAX here.
#endif // GTEST_OS_WINDOWS_MOBILE
@ -8899,6 +8901,7 @@ const char* StringFromGTestEnv(const char* flag, const char* default_value) {
#include <ctype.h>
#include <stdio.h>
#include <ostream> // NOLINT
#include <string>
@ -9494,6 +9497,7 @@ const char* TypedTestCasePState::VerifyRegisteredTestNames(
// This file implements cardinalities.
#include <limits.h>
#include <ostream> // NOLINT
#include <sstream>
#include <string>
@ -9646,6 +9650,7 @@ GTEST_API_ Cardinality Exactly(int n) { return Between(n, n); }
// USE THEM IN USER CODE.
#include <ctype.h>
#include <ostream> // NOLINT
#include <string>
@ -9808,6 +9813,7 @@ GTEST_API_ void Log(LogSeverity severity, const string& message,
// utilities for defining matchers.
#include <string.h>
#include <sstream>
#include <string>
@ -10291,6 +10297,7 @@ bool UnorderedElementsAreMatcherImplBase::
// EXPECT_CALL).
#include <stdlib.h>
#include <iostream> // NOLINT
#include <map>
#include <set>

View File

@ -8,6 +8,7 @@
#include "gtest-extra.h"
#include <gtest/gtest-spi.h>
#include <algorithm>
#include <cstring>
#include <memory>
@ -167,7 +168,8 @@ TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) {
// Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, WriteTests) {
// successful EXPECT_WRITE
EXPECT_WRITE(stdout,
EXPECT_WRITE(
stdout,
{ // NOLINT
a_++;
std::printf("test");
@ -177,7 +179,8 @@ TEST_F(SingleEvaluationTest, WriteTests) {
EXPECT_EQ(1, b_);
// failed EXPECT_WRITE
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout,
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(
stdout,
{ // NOLINT
a_++;
std::printf("test");
@ -417,7 +420,8 @@ TEST(OutputRedirectTest, ErrorInDtor) {
std::unique_ptr<OutputRedirect> redir(new OutputRedirect(f.get()));
// Put a character in a file buffer.
EXPECT_EQ('x', fputc('x', f.get()));
EXPECT_WRITE(stderr,
EXPECT_WRITE(
stderr,
{
// The close function must be called inside EXPECT_WRITE,
// otherwise the system may recycle closed file descriptor when

View File

@ -57,7 +57,7 @@ std::string OutputRedirect::restore_and_read() {
if (read_end_.descriptor() == -1) return content; // Already read.
enum { BUFFER_SIZE = 4096 };
char buffer[BUFFER_SIZE];
std::size_t count = 0;
size_t count = 0;
do {
count = read_end_.read(buffer, BUFFER_SIZE);
content.append(buffer, count);
@ -66,9 +66,9 @@ std::string OutputRedirect::restore_and_read() {
return content;
}
std::string read(file& f, std::size_t count) {
std::string read(file& f, size_t count) {
std::string buffer(count, '\0');
std::size_t n = 0, offset = 0;
size_t n = 0, offset = 0;
do {
n = f.read(&buffer[offset], count - offset);
// We can't read more than size_t bytes since count has type size_t.

View File

@ -9,8 +9,9 @@
#define FMT_GTEST_EXTRA_H_
#include <string>
#include "gmock.h"
#include "fmt/os.h"
#include "gmock.h"
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
@ -137,7 +138,7 @@ class SuppressAssert {
EXPECT_SYSTEM_ERROR(SUPPRESS_ASSERT(statement), error_code, message)
// Attempts to read count characters from a file.
std::string read(fmt::file& f, std::size_t count);
std::string read(fmt::file& f, size_t count);
# define EXPECT_READ(file, expected_content) \
EXPECT_EQ(expected_content, read(file, std::strlen(expected_content)))

View File

@ -6,9 +6,10 @@
// For the license information refer to format.h.
#include "fmt/locale.h"
#include "gmock.h"
using fmt::internal::max_value;
using fmt::detail::max_value;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Char> struct numpunct : std::numpunct<Char> {

View File

@ -16,8 +16,8 @@ template <typename T> class mock_allocator {
mock_allocator() {}
mock_allocator(const mock_allocator&) {}
typedef T value_type;
MOCK_METHOD1_T(allocate, T*(std::size_t n));
MOCK_METHOD2_T(deallocate, void(T* p, std::size_t n));
MOCK_METHOD1_T(allocate, T*(size_t n));
MOCK_METHOD2_T(deallocate, void(T* p, size_t n));
};
template <typename Allocator> class allocator_ref {
@ -51,10 +51,10 @@ template <typename Allocator> class allocator_ref {
public:
Allocator* get() const { return alloc_; }
value_type* allocate(std::size_t n) {
value_type* allocate(size_t n) {
return std::allocator_traits<Allocator>::allocate(*alloc_, n);
}
void deallocate(value_type* p, std::size_t n) { alloc_->deallocate(p, n); }
void deallocate(value_type* p, size_t n) { alloc_->deallocate(p, n); }
};
#endif // FMT_MOCK_ALLOCATOR_H_

View File

@ -5,11 +5,12 @@
//
// For the license information refer to format.h.
#include "fmt/os.h"
#include <cstdlib> // std::exit
#include <cstring>
#include <memory>
#include "fmt/os.h"
#include "gtest-extra.h"
#include "util.h"
@ -26,14 +27,14 @@ using fmt::error_code;
TEST(UtilTest, UTF16ToUTF8) {
std::string s = "ёжик";
fmt::internal::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A");
fmt::detail::utf16_to_utf8 u(L"\x0451\x0436\x0438\x043A");
EXPECT_EQ(s, u.str());
EXPECT_EQ(s.size(), u.size());
}
TEST(UtilTest, UTF16ToUTF8EmptyString) {
std::string s = "";
fmt::internal::utf16_to_utf8 u(L"");
fmt::detail::utf16_to_utf8 u(L"");
EXPECT_EQ(s, u.str());
EXPECT_EQ(s.size(), u.size());
}
@ -43,7 +44,7 @@ void check_utf_conversion_error(
const char* message,
fmt::basic_string_view<Char> str = fmt::basic_string_view<Char>(0, 1)) {
fmt::memory_buffer out;
fmt::internal::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
fmt::detail::format_windows_error(out, ERROR_INVALID_PARAMETER, message);
fmt::system_error error(0, "");
try {
(Converter)(str);
@ -55,12 +56,12 @@ void check_utf_conversion_error(
}
TEST(UtilTest, UTF16ToUTF8Error) {
check_utf_conversion_error<fmt::internal::utf16_to_utf8, wchar_t>(
check_utf_conversion_error<fmt::detail::utf16_to_utf8, wchar_t>(
"cannot convert string from UTF-16 to UTF-8");
}
TEST(UtilTest, UTF16ToUTF8Convert) {
fmt::internal::utf16_to_utf8 u;
fmt::detail::utf16_to_utf8 u;
EXPECT_EQ(ERROR_INVALID_PARAMETER, u.convert(fmt::wstring_view(0, 1)));
EXPECT_EQ(ERROR_INVALID_PARAMETER,
u.convert(fmt::wstring_view(L"foo", INT_MAX + 1u)));
@ -73,16 +74,15 @@ TEST(UtilTest, FormatWindowsError) {
0, ERROR_FILE_EXISTS,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<LPWSTR>(&message), 0, 0);
fmt::internal::utf16_to_utf8 utf8_message(message);
fmt::detail::utf16_to_utf8 utf8_message(message);
LocalFree(message);
fmt::memory_buffer actual_message;
fmt::internal::format_windows_error(actual_message, ERROR_FILE_EXISTS,
"test");
fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS, "test");
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
fmt::to_string(actual_message));
actual_message.resize(0);
auto max_size = fmt::internal::max_value<size_t>();
fmt::internal::format_windows_error(actual_message, ERROR_FILE_EXISTS,
auto max_size = fmt::detail::max_value<size_t>();
fmt::detail::format_windows_error(actual_message, ERROR_FILE_EXISTS,
fmt::string_view(0, max_size));
EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS),
fmt::to_string(actual_message));
@ -103,10 +103,10 @@ TEST(UtilTest, FormatLongWindowsError) {
reinterpret_cast<LPWSTR>(&message), 0, 0) == 0) {
return;
}
fmt::internal::utf16_to_utf8 utf8_message(message);
fmt::detail::utf16_to_utf8 utf8_message(message);
LocalFree(message);
fmt::memory_buffer actual_message;
fmt::internal::format_windows_error(actual_message, provisioning_not_allowed,
fmt::detail::format_windows_error(actual_message, provisioning_not_allowed,
"test");
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
fmt::to_string(actual_message));
@ -120,14 +120,14 @@ TEST(UtilTest, WindowsError) {
error = e;
}
fmt::memory_buffer message;
fmt::internal::format_windows_error(message, ERROR_FILE_EXISTS, "test error");
fmt::detail::format_windows_error(message, ERROR_FILE_EXISTS, "test error");
EXPECT_EQ(to_string(message), error.what());
EXPECT_EQ(ERROR_FILE_EXISTS, error.error_code());
}
TEST(UtilTest, ReportWindowsError) {
fmt::memory_buffer out;
fmt::internal::format_windows_error(out, ERROR_FILE_EXISTS, "test error");
fmt::detail::format_windows_error(out, ERROR_FILE_EXISTS, "test error");
out.push_back('\n');
EXPECT_WRITE(stderr,
fmt::report_windows_error(ERROR_FILE_EXISTS, "test error"),
@ -164,10 +164,10 @@ static file open_file() {
// Attempts to write a string to a file.
static void write(file& f, fmt::string_view s) {
std::size_t num_chars_left = s.size();
size_t num_chars_left = s.size();
const char* ptr = s.data();
do {
std::size_t count = f.write(ptr, num_chars_left);
size_t count = f.write(ptr, num_chars_left);
ptr += count;
// We can't write more than size_t bytes since num_chars_left
// has type size_t.
@ -238,7 +238,8 @@ TEST(BufferedFileTest, CloseFileInDtor) {
TEST(BufferedFileTest, CloseErrorInDtor) {
std::unique_ptr<buffered_file> f(new buffered_file(open_buffered_file()));
EXPECT_WRITE(stderr,
EXPECT_WRITE(
stderr,
{
// The close function must be called inside EXPECT_WRITE,
// otherwise the system may recycle closed file descriptor when
@ -369,7 +370,8 @@ TEST(FileTest, CloseFileInDtor) {
TEST(FileTest, CloseErrorInDtor) {
std::unique_ptr<file> f(new file(open_file()));
EXPECT_WRITE(stderr,
EXPECT_WRITE(
stderr,
{
// The close function must be called inside EXPECT_WRITE,
// otherwise the system may recycle closed file descriptor when
@ -489,9 +491,9 @@ TEST(FileTest, Fdopen) {
# ifdef FMT_LOCALE
TEST(LocaleTest, Strtod) {
fmt::Locale locale;
fmt::locale loc;
const char *start = "4.2", *ptr = start;
EXPECT_EQ(4.2, locale.strtod(ptr));
EXPECT_EQ(4.2, loc.strtod(ptr));
EXPECT_EQ(start + 3, ptr);
}
# endif

View File

@ -21,9 +21,9 @@ template <> struct formatter<test> : formatter<int> {
};
} // namespace fmt
#include "fmt/ostream.h"
#include <sstream>
#include "fmt/ostream.h"
#include "gmock.h"
#include "gtest-extra.h"
#include "util.h"
@ -64,22 +64,23 @@ TEST(OStreamTest, Enum) {
EXPECT_EQ(L"0", fmt::format(L"{}", unstreamable_enum()));
}
using range = fmt::buffer_range<char>;
struct test_arg_formatter : fmt::arg_formatter<range> {
struct test_arg_formatter
: fmt::arg_formatter<fmt::format_context::iterator, char> {
fmt::format_parse_context parse_ctx;
test_arg_formatter(fmt::format_context& ctx, fmt::format_specs& s)
: fmt::arg_formatter<range>(ctx, &parse_ctx, &s), parse_ctx("") {}
: fmt::arg_formatter<fmt::format_context::iterator, char>(
ctx, &parse_ctx, &s),
parse_ctx("") {}
};
TEST(OStreamTest, CustomArg) {
fmt::memory_buffer buffer;
fmt::internal::buffer<char>& base = buffer;
fmt::detail::buffer<char>& base = buffer;
fmt::format_context ctx(std::back_inserter(base), fmt::format_args());
fmt::format_specs spec;
test_arg_formatter af(ctx, spec);
fmt::visit_format_arg(
af, fmt::internal::make_arg<fmt::format_context>(streamable_enum()));
af, fmt::detail::make_arg<fmt::format_context>(streamable_enum()));
EXPECT_EQ("streamable_enum", std::string(buffer.data(), buffer.size()));
}
@ -140,18 +141,18 @@ TEST(OStreamTest, WriteToOStream) {
fmt::memory_buffer buffer;
const char* foo = "foo";
buffer.append(foo, foo + std::strlen(foo));
fmt::internal::write(os, buffer);
fmt::detail::write(os, buffer);
EXPECT_EQ("foo", os.str());
}
TEST(OStreamTest, WriteToOStreamMaxSize) {
std::size_t max_size = fmt::internal::max_value<std::size_t>();
std::streamsize max_streamsize = fmt::internal::max_value<std::streamsize>();
if (max_size <= fmt::internal::to_unsigned(max_streamsize)) return;
size_t max_size = fmt::detail::max_value<size_t>();
std::streamsize max_streamsize = fmt::detail::max_value<std::streamsize>();
if (max_size <= fmt::detail::to_unsigned(max_streamsize)) return;
struct test_buffer : fmt::internal::buffer<char> {
explicit test_buffer(std::size_t size) { resize(size); }
void grow(std::size_t) {}
struct test_buffer : fmt::detail::buffer<char> {
explicit test_buffer(size_t size) { resize(size); }
void grow(size_t) {}
} buffer(max_size);
struct mock_streambuf : std::streambuf {
@ -171,13 +172,13 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
typedef std::make_unsigned<std::streamsize>::type ustreamsize;
ustreamsize size = max_size;
do {
auto n = std::min(size, fmt::internal::to_unsigned(max_streamsize));
auto n = std::min(size, fmt::detail::to_unsigned(max_streamsize));
EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n)))
.WillOnce(testing::Return(max_streamsize));
data += n;
size -= n;
} while (size != 0);
fmt::internal::write(os, buffer);
fmt::detail::write(os, buffer);
}
TEST(OStreamTest, Join) {
@ -258,7 +259,7 @@ TEST(OStreamTest, DisableBuiltinOStreamOperators) {
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>
String, const char*, size_t>::value>::type>
explicit operator String() const {
return String("foo", 3u);
}
@ -269,13 +270,13 @@ std::ostream& operator<<(std::ostream& os,
return os << "bar";
}
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
TEST(OStreamTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#ifdef FMT_USE_STRING_VIEW
struct explicitly_convertible_to_std_string_view {
explicit operator fmt::internal::std_string_view<char>() const {
explicit operator fmt::detail::std_string_view<char>() const {
return {"foo", 3u};
}
};
@ -285,8 +286,24 @@ std::ostream& operator<<(std::ostream& os,
return os << "bar";
}
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
TEST(OStreamTest, FormatExplicitlyConvertibleToStdStringView) {
EXPECT_EQ("bar", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#endif // FMT_USE_STRING_VIEW
struct copyfmt_test {};
std::ostream& operator<<(std::ostream& os, copyfmt_test) {
std::ios ios(nullptr);
ios.copyfmt(os);
return os << "foo";
}
TEST(OStreamTest, CopyFmt) {
EXPECT_EQ("foo", fmt::format("{}", copyfmt_test()));
}
TEST(OStreamTest, CompileTimeString) {
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
}

View File

@ -11,13 +11,15 @@
#endif
#include "posix-mock.h"
#include "../src/os.cc"
#include <errno.h>
#include <fcntl.h>
#include <climits>
#include <memory>
#include "../src/os.cc"
#ifdef _WIN32
# include <io.h>
# undef max
@ -47,11 +49,11 @@ int pipe_count;
int fopen_count;
int fclose_count;
int fileno_count;
std::size_t read_nbyte;
std::size_t write_nbyte;
size_t read_nbyte;
size_t write_nbyte;
bool sysconf_error;
enum FStatSimulation { NONE, MAX_SIZE, ERROR } fstat_sim;
enum { NONE, MAX_SIZE, ERROR } fstat_sim;
} // namespace
#define EMULATE_EINTR(func, error_result) \
@ -215,10 +217,10 @@ TEST(UtilTest, GetPageSize) {
}
TEST(FileTest, OpenRetry) {
write_file("test", "there must be something here");
write_file("temp", "there must be something here");
std::unique_ptr<file> f{nullptr};
EXPECT_RETRY(f.reset(new file("test", file::RDONLY)), open,
"cannot open file test");
EXPECT_RETRY(f.reset(new file("temp", file::RDONLY)), open,
"cannot open file temp");
# ifndef _WIN32
char c = 0;
f->read(&c, 1);
@ -230,7 +232,8 @@ TEST(FileTest, CloseNoRetryInDtor) {
file::pipe(read_end, write_end);
std::unique_ptr<file> f(new file(std::move(read_end)));
int saved_close_count = 0;
EXPECT_WRITE(stderr,
EXPECT_WRITE(
stderr,
{
close_count = 1;
f.reset(nullptr);
@ -252,13 +255,13 @@ TEST(FileTest, CloseNoRetry) {
TEST(FileTest, Size) {
std::string content = "top secret, destroy before reading";
write_file("test", content);
file f("test", file::RDONLY);
write_file("temp", content);
file f("temp", file::RDONLY);
EXPECT_GE(f.size(), 0);
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
# ifdef _WIN32
fmt::memory_buffer message;
fmt::internal::format_windows_error(message, ERROR_ACCESS_DENIED,
fmt::detail::format_windows_error(message, ERROR_ACCESS_DENIED,
"cannot get file size");
fstat_sim = ERROR;
EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message));
@ -270,8 +273,8 @@ TEST(FileTest, Size) {
}
TEST(FileTest, MaxSize) {
write_file("test", "");
file f("test", file::RDONLY);
write_file("temp", "");
file f("temp", file::RDONLY);
fstat_sim = MAX_SIZE;
EXPECT_GE(f.size(), 0);
EXPECT_EQ(max_file_size(), f.size());
@ -285,7 +288,7 @@ TEST(FileTest, ReadRetry) {
write_end.write("test", SIZE);
write_end.close();
char buffer[SIZE];
std::size_t count = 0;
size_t count = 0;
EXPECT_RETRY(count = read_end.read(buffer, SIZE), read,
"cannot read from file");
EXPECT_EQ_POSIX(static_cast<std::streamsize>(SIZE), count);
@ -295,7 +298,7 @@ TEST(FileTest, WriteRetry) {
file read_end, write_end;
file::pipe(read_end, write_end);
enum { SIZE = 4 };
std::size_t count = 0;
size_t count = 0;
EXPECT_RETRY(count = write_end.write("test", SIZE), write,
"cannot write to file");
write_end.close();
@ -313,8 +316,8 @@ TEST(FileTest, ConvertReadCount) {
file read_end, write_end;
file::pipe(read_end, write_end);
char c;
std::size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t)) ++size;
size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(size_t)) ++size;
read_count = 1;
read_nbyte = 0;
EXPECT_THROW(read_end.read(&c, size), fmt::system_error);
@ -326,8 +329,8 @@ TEST(FileTest, ConvertWriteCount) {
file read_end, write_end;
file::pipe(read_end, write_end);
char c;
std::size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(std::size_t)) ++size;
size_t size = UINT_MAX;
if (sizeof(unsigned) != sizeof(size_t)) ++size;
write_count = 1;
write_nbyte = 0;
EXPECT_THROW(write_end.write(&c, size), fmt::system_error);
@ -385,10 +388,10 @@ TEST(FileTest, FdopenNoRetry) {
}
TEST(BufferedFileTest, OpenRetry) {
write_file("test", "there must be something here");
write_file("temp", "there must be something here");
std::unique_ptr<buffered_file> f{nullptr};
EXPECT_RETRY(f.reset(new buffered_file("test", "r")), fopen,
"cannot open file test");
EXPECT_RETRY(f.reset(new buffered_file("temp", "r")), fopen,
"cannot open file temp");
# ifndef _WIN32
char c = 0;
if (fread(&c, 1, 1, f->get()) < 1)
@ -401,7 +404,8 @@ TEST(BufferedFileTest, CloseNoRetryInDtor) {
file::pipe(read_end, write_end);
std::unique_ptr<buffered_file> f(new buffered_file(read_end.fdopen("r")));
int saved_fclose_count = 0;
EXPECT_WRITE(stderr,
EXPECT_WRITE(
stderr,
{
fclose_count = 1;
f.reset(nullptr);
@ -433,33 +437,33 @@ TEST(BufferedFileTest, FilenoNoRetry) {
}
#endif // FMT_USE_FCNTL
struct TestMock {
static TestMock* instance;
} * TestMock::instance;
struct test_mock {
static test_mock* instance;
} * test_mock::instance;
TEST(ScopedMock, Scope) {
{
ScopedMock<TestMock> mock;
EXPECT_EQ(&mock, TestMock::instance);
TestMock& copy = mock;
ScopedMock<test_mock> mock;
EXPECT_EQ(&mock, test_mock::instance);
test_mock& copy = mock;
static_cast<void>(copy);
}
EXPECT_EQ(nullptr, TestMock::instance);
EXPECT_EQ(nullptr, test_mock::instance);
}
#ifdef FMT_LOCALE
typedef fmt::Locale::type LocaleType;
typedef fmt::locale::type locale_type;
struct LocaleMock {
static LocaleMock* instance;
MOCK_METHOD3(newlocale, LocaleType(int category_mask, const char* locale,
LocaleType base));
MOCK_METHOD1(freelocale, void(LocaleType locale));
struct locale_mock {
static locale_mock* instance;
MOCK_METHOD3(newlocale, locale_type(int category_mask, const char* locale,
locale_type base));
MOCK_METHOD1(freelocale, void(locale_type locale));
MOCK_METHOD3(strtod_l,
double(const char* nptr, char** endptr, LocaleType locale));
} * LocaleMock::instance;
double(const char* nptr, char** endptr, locale_type locale));
} * locale_mock::instance;
# ifdef _MSC_VER
# pragma warning(push)
@ -470,15 +474,15 @@ struct LocaleMock {
# endif
_locale_t _create_locale(int category, const char* locale) {
return LocaleMock::instance->newlocale(category, locale, 0);
return locale_mock::instance->newlocale(category, locale, 0);
}
void _free_locale(_locale_t locale) {
LocaleMock::instance->freelocale(locale);
locale_mock::instance->freelocale(locale);
}
double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
return locale_mock::instance->strtod_l(nptr, endptr, locale);
}
# ifdef __clang__
# pragma clang diagnostic pop
@ -492,11 +496,6 @@ double _strtod_l(const char* nptr, char** endptr, _locale_t locale) {
# define FMT_LOCALE_THROW
# endif
LocaleType newlocale(int category_mask, const char* locale,
LocaleType base) FMT_LOCALE_THROW {
return LocaleMock::instance->newlocale(category_mask, locale, base);
}
# if defined(__APPLE__) || \
(defined(__FreeBSD__) && __FreeBSD_version < 1200002)
typedef int FreeLocaleResult;
@ -504,49 +503,55 @@ typedef int FreeLocaleResult;
typedef void FreeLocaleResult;
# endif
FreeLocaleResult freelocale(LocaleType locale) FMT_LOCALE_THROW {
LocaleMock::instance->freelocale(locale);
FreeLocaleResult freelocale(locale_type locale) FMT_LOCALE_THROW {
locale_mock::instance->freelocale(locale);
return FreeLocaleResult();
}
double strtod_l(const char* nptr, char** endptr,
LocaleType locale) FMT_LOCALE_THROW {
return LocaleMock::instance->strtod_l(nptr, endptr, locale);
locale_type locale) FMT_LOCALE_THROW {
return locale_mock::instance->strtod_l(nptr, endptr, locale);
}
# undef FMT_LOCALE_THROW
TEST(LocaleTest, LocaleMock) {
ScopedMock<LocaleMock> mock;
LocaleType locale = reinterpret_cast<LocaleType>(11);
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
newlocale(222, "foo", locale);
# ifndef _WIN32
locale_t test::newlocale(int category_mask, const char* locale, locale_t base) {
return locale_mock::instance->newlocale(category_mask, locale, base);
}
TEST(LocaleTest, LocaleMock) {
ScopedMock<locale_mock> mock;
locale_type locale = reinterpret_cast<locale_type>(11);
EXPECT_CALL(mock, newlocale(222, StrEq("foo"), locale));
FMT_SYSTEM(newlocale(222, "foo", locale));
}
# endif
TEST(LocaleTest, Locale) {
# ifndef LC_NUMERIC_MASK
enum { LC_NUMERIC_MASK = LC_NUMERIC };
# endif
ScopedMock<LocaleMock> mock;
LocaleType impl = reinterpret_cast<LocaleType>(42);
ScopedMock<locale_mock> mock;
locale_type impl = reinterpret_cast<locale_type>(42);
EXPECT_CALL(mock, newlocale(LC_NUMERIC_MASK, StrEq("C"), nullptr))
.WillOnce(Return(impl));
EXPECT_CALL(mock, freelocale(impl));
fmt::Locale locale;
EXPECT_EQ(impl, locale.get());
fmt::locale loc;
EXPECT_EQ(impl, loc.get());
}
TEST(LocaleTest, Strtod) {
ScopedMock<LocaleMock> mock;
ScopedMock<locale_mock> mock;
EXPECT_CALL(mock, newlocale(_, _, _))
.WillOnce(Return(reinterpret_cast<LocaleType>(42)));
.WillOnce(Return(reinterpret_cast<locale_type>(42)));
EXPECT_CALL(mock, freelocale(_));
fmt::Locale locale;
fmt::locale loc;
const char* str = "4.2";
char end = 'x';
EXPECT_CALL(mock, strtod_l(str, _, locale.get()))
EXPECT_CALL(mock, strtod_l(str, _, loc.get()))
.WillOnce(testing::DoAll(testing::SetArgPointee<1>(&end), Return(777)));
EXPECT_EQ(777, locale.strtod(str));
EXPECT_EQ(777, loc.strtod(str));
EXPECT_EQ(&end, str);
}

View File

@ -9,7 +9,11 @@
#define FMT_POSIX_TEST_H
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#ifdef __APPLE__
# include <xlocale.h>
#endif
#ifdef _WIN32
# include <windows.h>
@ -62,6 +66,10 @@ int pipe(int* pfds, unsigned psize, int textmode);
FILE* fopen(const char* filename, const char* mode);
int fclose(FILE* stream);
int(fileno)(FILE* stream);
#if defined(FMT_LOCALE) && !defined(_WIN32)
locale_t newlocale(int category_mask, const char* locale, locale_t base);
#endif
} // namespace test
#define FMT_SYSTEM(call) test::call

View File

@ -17,7 +17,7 @@
using fmt::format;
using fmt::format_error;
using fmt::internal::max_value;
using fmt::detail::max_value;
const unsigned BIG_NUM = INT_MAX + 1u;
@ -113,14 +113,13 @@ TEST(PrintfTest, SwitchArgIndexing) {
TEST(PrintfTest, InvalidArgIndex) {
EXPECT_THROW_MSG(test_sprintf("%0$d", 42), format_error,
"argument index out of range");
"argument not found");
EXPECT_THROW_MSG(test_sprintf("%2$d", 42), format_error,
"argument index out of range");
"argument not found");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", INT_MAX), 42), format_error,
"argument index out of range");
"argument not found");
EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error,
"argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%2$", 42), format_error, "argument not found");
EXPECT_THROW_MSG(test_sprintf(format("%{}$d", BIG_NUM), 42), format_error,
"number is too big");
}
@ -140,8 +139,11 @@ TEST(PrintfTest, ZeroFlag) {
EXPECT_PRINTF("+00042", "%00+6d", 42);
EXPECT_PRINTF(" 42", "%05.d", 42);
EXPECT_PRINTF(" 0042", "%05.4d", 42);
// '0' flag is ignored for non-numeric types.
EXPECT_PRINTF("0000x", "%05c", 'x');
EXPECT_PRINTF(" x", "%05c", 'x');
}
TEST(PrintfTest, PlusFlag) {
@ -152,11 +154,38 @@ TEST(PrintfTest, PlusFlag) {
// '+' flag is ignored for non-numeric types.
EXPECT_PRINTF("x", "%+c", 'x');
// '+' flag wins over space flag
EXPECT_PRINTF("+42", "%+ d", 42);
EXPECT_PRINTF("-42", "%+ d", -42);
EXPECT_PRINTF("+42", "% +d", 42);
EXPECT_PRINTF("-42", "% +d", -42);
EXPECT_PRINTF("+0042", "% +05d", 42);
EXPECT_PRINTF("+0042", "%0+ 5d", 42);
// '+' flag and space flag are both ignored for non-numeric types.
EXPECT_PRINTF("x", "%+ c", 'x');
EXPECT_PRINTF("x", "% +c", 'x');
}
TEST(PrintfTest, MinusFlag) {
EXPECT_PRINTF("abc ", "%-5s", "abc");
EXPECT_PRINTF("abc ", "%0--5s", "abc");
EXPECT_PRINTF("7 ", "%-5d", 7);
EXPECT_PRINTF("97 ", "%-5hhi", 'a');
EXPECT_PRINTF("a ", "%-5c", 'a');
// '0' flag is ignored if '-' flag is given
EXPECT_PRINTF("7 ", "%-05d", 7);
EXPECT_PRINTF("7 ", "%0-5d", 7);
EXPECT_PRINTF("a ", "%-05c", 'a');
EXPECT_PRINTF("a ", "%0-5c", 'a');
EXPECT_PRINTF("97 ", "%-05hhi", 'a');
EXPECT_PRINTF("97 ", "%0-5hhi", 'a');
// '-' and space flag don't interfere
EXPECT_PRINTF(" 42", "%- d", 42);
}
TEST(PrintfTest, SpaceFlag) {
@ -222,8 +251,7 @@ TEST(PrintfTest, DynamicWidth) {
EXPECT_EQ("42 ", test_sprintf("%*d", -5, 42));
EXPECT_THROW_MSG(test_sprintf("%*d", 5.0, 42), format_error,
"width is not integer");
EXPECT_THROW_MSG(test_sprintf("%*d"), format_error,
"argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%*d"), format_error, "argument not found");
EXPECT_THROW_MSG(test_sprintf("%*d", BIG_NUM, 42), format_error,
"number is too big");
}
@ -259,6 +287,11 @@ TEST(PrintfTest, FloatPrecision) {
EXPECT_PRINTF(buffer, "%.3a", 1234.5678);
}
TEST(PrintfTest, StringPrecision) {
char test[] = {'H', 'e', 'l', 'l', 'o'};
EXPECT_EQ(fmt::sprintf("%.4s", test), "Hell");
}
TEST(PrintfTest, IgnorePrecisionForNonNumericArg) {
EXPECT_PRINTF("abc", "%.5s", "abc");
}
@ -268,8 +301,7 @@ TEST(PrintfTest, DynamicPrecision) {
EXPECT_EQ("42", test_sprintf("%.*d", -5, 42));
EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error,
"precision is not integer");
EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error,
"argument index out of range");
EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error, "argument not found");
EXPECT_THROW_MSG(test_sprintf("%.*d", BIG_NUM, 42), format_error,
"number is too big");
if (sizeof(long long) != sizeof(int)) {
@ -298,7 +330,7 @@ void TestLength(const char* length_spec, U value) {
unsigned long long unsigned_value = 0;
// Apply integer promotion to the argument.
unsigned long long max = max_value<U>();
using fmt::internal::const_check;
using fmt::detail::const_check;
if (const_check(max <= static_cast<unsigned>(max_value<int>()))) {
signed_value = static_cast<int>(value);
unsigned_value = static_cast<unsigned long long>(value);
@ -367,7 +399,7 @@ TEST(PrintfTest, Length) {
TestLength<long long>("ll");
TestLength<unsigned long long>("ll");
TestLength<intmax_t>("j");
TestLength<std::size_t>("z");
TestLength<size_t>("z");
TestLength<std::ptrdiff_t>("t");
long double max = max_value<long double>();
EXPECT_PRINTF(fmt::format("{:.6}", max), "%g", max);
@ -447,6 +479,12 @@ TEST(PrintfTest, String) {
EXPECT_PRINTF(L" (null)", L"%10s", null_wstr);
}
TEST(PrintfTest, UCharString) {
unsigned char str[] = "test";
unsigned char* pstr = str;
EXPECT_EQ("test", fmt::sprintf("%s", pstr));
}
TEST(PrintfTest, Pointer) {
int n;
void* p = &n;
@ -502,9 +540,7 @@ TEST(PrintfTest, PrintfError) {
TEST(PrintfTest, WideString) { EXPECT_EQ(L"abc", fmt::sprintf(L"%s", L"abc")); }
TEST(PrintfTest, PrintfCustom) {
// The test is disabled for now because it requires decoupling
// fallback_formatter::format from format_context.
//EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
EXPECT_EQ("abc", test_sprintf("%s", TestString("abc")));
}
TEST(PrintfTest, OStream) {
@ -571,51 +607,22 @@ TEST(PrintfTest, VSPrintfMakeWArgsExample) {
#endif
}
typedef fmt::printf_arg_formatter<fmt::buffer_range<char>> formatter_t;
typedef fmt::basic_printf_context<formatter_t::iterator, char> context_t;
TEST(PrintfTest, PrintfDetermineOutputSize) {
using backit = std::back_insert_iterator<std::vector<char>>;
using truncated_printf_context =
fmt::basic_printf_context<fmt::detail::truncating_iterator<backit>, char>;
// A custom printf argument formatter that doesn't print `-` for floating-point
// values rounded to 0.
class custom_printf_arg_formatter : public formatter_t {
public:
using formatter_t::iterator;
auto v = std::vector<char>{};
auto it = std::back_inserter(v);
custom_printf_arg_formatter(formatter_t::iterator iter,
formatter_t::format_specs& specs, context_t& ctx)
: formatter_t(iter, specs, ctx) {}
const auto format_string = "%s";
const auto format_arg = "Hello";
const auto expected_size = fmt::sprintf(format_string, format_arg).size();
using formatter_t::operator();
#if FMT_MSC_VER > 0 && FMT_MSC_VER <= 1804
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value){
#else
iterator operator()(double value) {
#endif
// Comparing a float to 0.0 is safe.
if (round(value * pow(10, specs()->precision)) == 0.0) value = 0;
return formatter_t::operator()(value);
}
}
;
typedef fmt::basic_format_args<context_t> format_args_t;
std::string custom_vformat(fmt::string_view format_str, format_args_t args) {
fmt::memory_buffer buffer;
fmt::vprintf<custom_printf_arg_formatter>(buffer, format_str, args);
return std::string(buffer.data(), buffer.size());
}
template <typename... Args>
std::string custom_format(const char* format_str, const Args&... args) {
auto va = fmt::make_printf_args(args...);
return custom_vformat(format_str, {va});
}
TEST(PrintfTest, CustomFormat) {
EXPECT_EQ("0.00", custom_format("%.2f", -.00001));
EXPECT_EQ("0.00", custom_format("%.2f", .00001));
EXPECT_EQ("1.00", custom_format("%.2f", 1.00001));
EXPECT_EQ("-1.00", custom_format("%.2f", -1.00001));
EXPECT_EQ((truncated_printf_context(
fmt::detail::truncating_iterator<backit>(it, 0), format_string,
fmt::make_format_args<truncated_printf_context>(format_arg))
.format()
.count()),
expected_size);
}

View File

@ -79,7 +79,7 @@ TEST(RangesTest, JoinInitializerList) {
struct my_struct {
int32_t i;
std::string str; // can throw
template <std::size_t N> decltype(auto) get() const noexcept {
template <size_t N> decltype(auto) get() const noexcept {
if constexpr (N == 0)
return i;
else if constexpr (N == 1)
@ -87,16 +87,15 @@ struct my_struct {
}
};
template <std::size_t N> decltype(auto) get(const my_struct& s) noexcept {
template <size_t N> decltype(auto) get(const my_struct& s) noexcept {
return s.get<N>();
}
namespace std {
template <>
struct tuple_size<my_struct> : std::integral_constant<std::size_t, 2> {};
template <> struct tuple_size<my_struct> : std::integral_constant<size_t, 2> {};
template <std::size_t N> struct tuple_element<N, my_struct> {
template <size_t N> struct tuple_element<N, my_struct> {
using type = decltype(std::declval<my_struct>().get<N>());
};
@ -140,3 +139,17 @@ TEST(RangesTest, FormatStringLike) {
EXPECT_EQ("foo", fmt::format("{}", string_like()));
}
#endif // FMT_USE_STRING_VIEW
struct zstring_sentinel {};
bool operator==(const char* p, zstring_sentinel) { return *p == '\0'; }
bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
struct zstring {
const char* p;
const char* begin() const { return p; }
zstring_sentinel end() const { return {}; }
};
TEST(RangesTest, JoinSentinel) {
zstring hello{"hello"};
EXPECT_EQ("{'h', 'e', 'l', 'l', 'o'}", fmt::format("{}", hello));
EXPECT_EQ("h_e_l_l_o", fmt::format("{}", fmt::join(hello, "_")));
}

View File

@ -5,12 +5,14 @@
//
// For the license information refer to format.h.
#include "scan.h"
#include <time.h>
#include <climits>
#include "gmock.h"
#include "gtest-extra.h"
#include "scan.h"
TEST(ScanTest, ReadText) {
fmt::string_view s = "foo";
@ -73,7 +75,7 @@ template <> struct scanner<tm> {
if (it != ctx.end() && *it == ':') ++it;
auto end = it;
while (end != ctx.end() && *end != '}') ++end;
format.reserve(internal::to_unsigned(end - it + 1));
format.reserve(detail::to_unsigned(end - it + 1));
format.append(it, end);
format.push_back('\0');
return end;

View File

@ -31,7 +31,7 @@ class scan_parse_context {
FMT_CONSTEXPR iterator end() const { return format_.end(); }
void advance_to(iterator it) {
format_.remove_prefix(internal::to_unsigned(it - begin()));
format_.remove_prefix(detail::to_unsigned(it - begin()));
}
};
@ -48,11 +48,11 @@ struct scan_context {
iterator end() const { return begin() + input_.size(); }
void advance_to(iterator it) {
input_.remove_prefix(internal::to_unsigned(it - begin()));
input_.remove_prefix(detail::to_unsigned(it - begin()));
}
};
namespace internal {
namespace detail {
enum class scan_type {
none_type,
int_type,
@ -107,20 +107,20 @@ class scan_arg {
ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx));
}
};
} // namespace internal
} // namespace detail
struct scan_args {
int size;
const internal::scan_arg* data;
const detail::scan_arg* data;
template <size_t N>
scan_args(const std::array<internal::scan_arg, N>& store)
scan_args(const std::array<detail::scan_arg, N>& store)
: size(N), data(store.data()) {
static_assert(N < INT_MAX, "too many arguments");
}
};
namespace internal {
namespace detail {
struct scan_handler : error_handler {
private:
@ -169,14 +169,15 @@ struct scan_handler : error_handler {
scan_ctx_.advance_to(it + size);
}
void on_arg_id() { on_arg_id(next_arg_id_++); }
void on_arg_id(int id) {
int on_arg_id() { return on_arg_id(next_arg_id_++); }
int on_arg_id(int id) {
if (id >= args_.size) on_error("argument index out of range");
arg_ = args_.data[id];
return id;
}
void on_arg_id(string_view) { on_error("invalid format"); }
int on_arg_id(string_view) { return on_error("invalid format"), 0; }
void on_replacement_field(const char*) {
void on_replacement_field(int, const char*) {
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
switch (arg_.type) {
case scan_type::int_type:
@ -208,24 +209,24 @@ struct scan_handler : error_handler {
}
}
const char* on_format_specs(const char* begin, const char*) {
const char* on_format_specs(int, const char* begin, const char*) {
if (arg_.type != scan_type::custom_type) return begin;
parse_ctx_.advance_to(begin);
arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_);
return parse_ctx_.begin();
}
};
} // namespace internal
} // namespace detail
template <typename... Args>
std::array<internal::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) {
std::array<detail::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) {
return {{args...}};
}
string_view::iterator vscan(string_view input, string_view format_str,
scan_args args) {
internal::scan_handler h(format_str, input, args);
internal::parse_format_string<false>(format_str, h);
detail::scan_handler h(format_str, input, args);
detail::parse_format_string<false>(format_str, h);
return input.begin() + (h.pos() - &*input.begin());
}

View File

@ -1,4 +1,5 @@
#include <format>
#include "gtest.h"
TEST(StdFormatTest, Escaping) {
@ -123,7 +124,7 @@ template <> struct std::formatter<S> {
if constexpr (!is_integral_v<type> || is_same_v<type, bool>)
throw format_error("width is not integral");
// else if (value < 0 || value > numeric_limits<int>::max())
else if (fmt::internal::is_negative(value) ||
else if (fmt::detail::is_negative(value) ||
value > numeric_limits<int>::max())
throw format_error("invalid width");
else

View File

@ -9,6 +9,7 @@
#define FMT_TEST_ASSERT_H_
#include <stdexcept>
#include "gtest.h"
class assertion_failure : public std::logic_error {

View File

@ -6,6 +6,7 @@
// For the license information refer to format.h.
#include <cstdlib>
#include "gtest.h"
#ifdef _WIN32

View File

@ -6,6 +6,7 @@
// For the license information refer to format.h.
#include "util.h"
#include <cstring>
void increment(char* s) {

View File

@ -19,7 +19,7 @@ enum { BUFFER_SIZE = 256 };
# define FMT_VSNPRINTF vsnprintf
#endif
template <std::size_t SIZE>
template <size_t SIZE>
void safe_sprintf(char (&buffer)[SIZE], const char* format, ...) {
std::va_list args;
va_start(args, format);