Externals: Update fmt to 6.1.2

This commit is contained in:
Lioncash 2020-02-04 14:50:46 -05:00
parent f36c735856
commit 1873b7a398
32 changed files with 9602 additions and 5324 deletions

View File

@ -27,21 +27,24 @@ endfunction()
# Set the default CMAKE_BUILD_TYPE to Release. # Set the default CMAKE_BUILD_TYPE to Release.
# This should be done before the project command since the latter can set # This should be done before the project command since the latter can set
# CMAKE_BUILD_TYPE itself (it does so for nmake). # CMAKE_BUILD_TYPE itself (it does so for nmake).
if (NOT CMAKE_BUILD_TYPE) if (MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
join(doc "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or " join(doc "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") "CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
set(CMAKE_BUILD_TYPE Release CACHE STRING ${doc}) set(CMAKE_BUILD_TYPE Release CACHE STRING ${doc})
endif () endif ()
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF) option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
option(FMT_WERROR "Halt the compilation with an error on compiler warnings." OFF) option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
OFF)
# Options that control generation of various targets. # Options that control generation of various targets.
option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT}) option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT})
option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT}) option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
option(FMT_TEST "Generate the test 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)
project(FMT) project(FMT CXX)
# Get version from core.h # Get version from core.h
file(READ include/fmt/core.h core_h) file(READ include/fmt/core.h core_h)
@ -66,6 +69,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
include(cxx14) include(cxx14)
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
set(FMT_REQUIRED_FEATURES cxx_auto_type cxx_variadic_templates)
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
-Wold-style-cast -Wundef -Wold-style-cast -Wundef
@ -74,7 +79,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
-Wcast-align -Wnon-virtual-dtor -Wcast-align -Wnon-virtual-dtor
-Wctor-dtor-privacy -Wdisabled-optimization -Wctor-dtor-privacy -Wdisabled-optimization
-Winvalid-pch -Woverloaded-virtual -Winvalid-pch -Woverloaded-virtual
-Wconversion -Wconversion -Wswitch-enum
-Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow) -Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow)
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6) if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept
@ -93,7 +98,8 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
endif () endif ()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wno-sign-conversion) set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion
-Wno-sign-conversion -Wdeprecated -Wweak-vtables)
check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING) check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
if (HAS_NULLPTR_WARNING) if (HAS_NULLPTR_WARNING)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
@ -123,11 +129,16 @@ if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*") ${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
endif () endif ()
set(strtod_l_headers stdlib.h)
if (APPLE)
set(strtod_l_headers ${strtod_l_headers} xlocale.h)
endif ()
include(CheckSymbolExists) include(CheckSymbolExists)
if (WIN32) if (WIN32)
check_symbol_exists(open io.h HAVE_OPEN) check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
else () else ()
check_symbol_exists(open fcntl.h HAVE_OPEN) check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
endif () endif ()
function(add_headers VAR) function(add_headers VAR)
@ -139,17 +150,17 @@ function(add_headers VAR)
endfunction() endfunction()
# Define the fmt library, its includes and the needed defines. # Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS chrono.h color.h core.h format.h format-inl.h locale.h add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h
ostream.h printf.h time.h ranges.h) locale.h ostream.h posix.h printf.h ranges.h)
set(FMT_SOURCES src/format.cc) set(FMT_SOURCES src/format.cc src/posix.cc)
if (HAVE_OPEN)
add_headers(FMT_HEADERS posix.h)
set(FMT_SOURCES ${FMT_SOURCES} src/posix.cc)
endif ()
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst) add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt) add_library(fmt::fmt ALIAS fmt)
if (HAVE_STRTOD_L)
target_compile_definitions(fmt PUBLIC FMT_LOCALE)
endif ()
if (FMT_WERROR) if (FMT_WERROR)
target_compile_options(fmt PRIVATE ${WERROR_FLAG}) target_compile_options(fmt PRIVATE ${WERROR_FLAG})
endif () endif ()
@ -157,6 +168,8 @@ if (FMT_PEDANTIC)
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS}) target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif () endif ()
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt PUBLIC target_include_directories(fmt PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>) $<INSTALL_INTERFACE:include>)
@ -166,19 +179,24 @@ set_target_properties(fmt PROPERTIES
DEBUG_POSTFIX d) DEBUG_POSTFIX d)
if (BUILD_SHARED_LIBS) if (BUILD_SHARED_LIBS)
if (UNIX AND NOT APPLE) if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
# Fix rpmlint warning: # Fix rpmlint warning:
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6. # unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
target_link_libraries(fmt -Wl,--as-needed) target_link_libraries(fmt -Wl,--as-needed)
endif () endif ()
target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED) target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED)
endif () endif ()
if (FMT_SAFE_DURATION_CAST)
target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST)
endif()
add_library(fmt-header-only INTERFACE) add_library(fmt-header-only INTERFACE)
add_library(fmt::fmt-header-only ALIAS fmt-header-only) add_library(fmt::fmt-header-only ALIAS fmt-header-only)
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1) target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES})
target_include_directories(fmt-header-only INTERFACE target_include_directories(fmt-header-only INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>) $<INSTALL_INTERFACE:include>)
@ -188,7 +206,7 @@ if (FMT_INSTALL)
include(GNUInstallDirs) include(GNUInstallDirs)
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
set(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING set(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, relative to ${CMAKE_INSTALL_PREFIX}.")
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake) set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake) set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc) set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
@ -205,8 +223,8 @@ if (FMT_INSTALL)
set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
"Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.") "Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.")
set(FMT_PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/share/pkgconfig" CACHE PATH set(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, relative to ${CMAKE_INSTALL_PREFIX}.")
# Generate the version, config and target files into the build directory. # Generate the version, config and target files into the build directory.
write_basic_package_version_file( write_basic_package_version_file(
@ -235,9 +253,12 @@ if (FMT_INSTALL)
# Install the library and headers. # Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
DESTINATION ${FMT_LIB_DIR}) LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}> DESTINATION ${FMT_LIB_DIR} OPTIONAL) install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR}) install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR})
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif () endif ()
@ -251,6 +272,11 @@ if (FMT_TEST)
add_subdirectory(test) add_subdirectory(test)
endif () endif ()
# Control fuzzing independent of the unit tests.
if (FMT_FUZZ)
add_subdirectory(test/fuzzing)
endif ()
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore) set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
if (MASTER_PROJECT AND EXISTS ${gitignore}) if (MASTER_PROJECT AND EXISTS ${gitignore})
# Get the list of ignored files from .gitignore. # Get the list of ignored files from .gitignore.

17
Externals/fmt/CONTRIBUTING.md vendored Normal file
View File

@ -0,0 +1,17 @@
Contributing to {fmt}
=====================
By submitting a pull request or a patch, you represent that you have the right
to license your contribution to the {fmt} project owners and the community,
agree that your contributions are licensed under the {fmt} license, and agree
to future changes to the licensing.
All C++ code must adhere to [Google C++ Style Guide](
https://google.github.io/styleguide/cppguide.html) with the following
exceptions:
* Exceptions are permitted
* snake_case should be used instead of UpperCamelCase for function and type
names
Thanks for contributing!

View File

@ -1,12 +0,0 @@
Contributing to fmt
===================
All C++ code must adhere to `Google C++ Style Guide
<https://google.github.io/styleguide/cppguide.html>`_ with the following
exceptions:
* Exceptions are permitted
* snake_case should be used instead of UpperCamelCase for function and type
names
Thanks for contributing!

View File

@ -1,3 +1,682 @@
6.1.2 - 2019-12-11
------------------
* Fixed ABI compatibility with ``libfmt.so.6.0.0``
(`#1471 <https://github.com/fmtlib/fmt/issues/1471>`_).
* Fixed handling types convertible to ``std::string_view``
(`#1451 <https://github.com/fmtlib/fmt/pull/1451>`_).
Thanks `@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_.
* Made CUDA test an opt-in enabled via the ``FMT_CUDA_TEST`` CMake option.
* Fixed sign conversion warnings
(`#1440 <https://github.com/fmtlib/fmt/pull/1440>`_).
Thanks `@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_.
6.1.1 - 2019-12-04
------------------
* Fixed shared library build on Windows
(`#1443 <https://github.com/fmtlib/fmt/pull/1443>`_,
`#1445 <https://github.com/fmtlib/fmt/issues/1445>`_,
`#1446 <https://github.com/fmtlib/fmt/pull/1446>`_,
`#1450 <https://github.com/fmtlib/fmt/issues/1450>`_).
Thanks `@egorpugin (Egor Pugin) <https://github.com/egorpugin>`_,
`@bbolli (Beat Bolli) <https://github.com/bbolli>`_.
* Added a missing decimal point in exponent notation with trailing zeros.
* Removed deprecated ``format_arg_store::TYPES``.
6.1.0 - 2019-12-01
------------------
* {fmt} now formats IEEE 754 ``float`` and ``double`` using the shortest decimal
representation with correct rounding by default:
.. code:: c++
#include <cmath>
#include <fmt/core.h>
int main() {
fmt::print("{}", M_PI);
}
prints ``3.141592653589793``.
* Made the fast binary to decimal floating-point formatter the default,
simplified it and improved performance. {fmt} is now 15 times faster than
libc++'s ``std::ostringstream``, 11 times faster than ``printf`` and 10%
faster than double-conversion on `dtoa-benchmark
<https://github.com/fmtlib/dtoa-benchmark>`_:
================== ========= =======
Function Time (ns) Speedup
================== ========= =======
ostringstream 1,346.30 1.00x
ostrstream 1,195.74 1.13x
sprintf 995.08 1.35x
doubleconv 99.10 13.59x
fmt 88.34 15.24x
================== ========= =======
.. image:: https://user-images.githubusercontent.com/576385/
69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
* {fmt} no longer converts ``float`` arguments to ``double``. In particular this
improves the default (shortest) representation of floats and makes
``fmt::format`` consistent with ``std::format`` specs
(`#1336 <https://github.com/fmtlib/fmt/issues/1336>`_,
`#1353 <https://github.com/fmtlib/fmt/issues/1353>`_,
`#1360 <https://github.com/fmtlib/fmt/pull/1360>`_,
`#1361 <https://github.com/fmtlib/fmt/pull/1361>`_):
.. code:: c++
fmt::print("{}", 0.1f);
prints ``0.1`` instead of ``0.10000000149011612``.
Thanks `@orivej (Orivej Desh) <https://github.com/orivej>`_.
* Made floating-point formatting output consistent with ``printf``/iostreams
(`#1376 <https://github.com/fmtlib/fmt/issues/1376>`_,
`#1417 <https://github.com/fmtlib/fmt/issues/1417>`_).
* Added support for 128-bit integers
(`#1287 <https://github.com/fmtlib/fmt/pull/1287>`_):
.. code:: c++
fmt::print("{}", std::numeric_limits<__int128_t>::max());
prints ``170141183460469231731687303715884105727``.
Thanks `@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_.
* The overload of ``print`` that takes ``text_style`` is now atomic, i.e. the
output from different threads doesn't interleave
(`#1351 <https://github.com/fmtlib/fmt/pull/1351>`_).
Thanks `@tankiJong (Tanki Zhang) <https://github.com/tankiJong>`_.
* Made compile time in the header-only mode ~20% faster by reducing the number
of template instantiations. ``wchar_t`` overload of ``vprint`` was moved from
``fmt/core.h`` to ``fmt/format.h``.
* Added an overload of ``fmt::join`` that works with tuples
(`#1322 <https://github.com/fmtlib/fmt/issues/1322>`_,
`#1330 <https://github.com/fmtlib/fmt/pull/1330>`_):
.. code:: c++
#include <tuple>
#include <fmt/ranges.h>
int main() {
std::tuple<char, int, float> t{'a', 1, 2.0f};
fmt::print("{}", t);
}
prints ``('a', 1, 2.0)``.
Thanks `@jeremyong (Jeremy Ong) <https://github.com/jeremyong>`_.
* Changed formatting of octal zero with prefix from "00" to "0":
.. code:: c++
fmt::print("{:#o}", 0);
prints ``0``.
* The locale is now passed to ostream insertion (``<<``) operators
(`#1406 <https://github.com/fmtlib/fmt/pull/1406>`_):
.. code:: c++
#include <fmt/locale.h>
#include <fmt/ostream.h>
struct S {
double value;
};
std::ostream& operator<<(std::ostream& os, S s) {
return os << s.value;
}
int main() {
auto s = fmt::format(std::locale("fr_FR.UTF-8"), "{}", S{0.42});
// s == "0,42"
}
Thanks `@dlaugt (Daniel Laügt) <https://github.com/dlaugt>`_.
* Locale-specific number formatting now uses grouping
(`#1393 <https://github.com/fmtlib/fmt/issues/1393>`_
`#1394 <https://github.com/fmtlib/fmt/pull/1394>`_).
Thanks `@skrdaniel <https://github.com/skrdaniel>`_.
* Fixed handling of types with deleted implicit rvalue conversion to
``const char**`` (`#1421 <https://github.com/fmtlib/fmt/issues/1421>`_):
.. code:: c++
struct mystring {
operator const char*() const&;
operator const char*() &;
operator const char*() const&& = delete;
operator const char*() && = delete;
};
mystring str;
fmt::print("{}", str); // now compiles
* Enums are now mapped to correct underlying types instead of ``int``
(`#1286 <https://github.com/fmtlib/fmt/pull/1286>`_).
Thanks `@agmt (Egor Seredin) <https://github.com/agmt>`_.
* Enum classes are no longer implicitly converted to ``int``
(`#1424 <https://github.com/fmtlib/fmt/issues/1424>`_).
* Added ``basic_format_parse_context`` for consistency with C++20
``std::format`` and deprecated ``basic_parse_context``.
* Fixed handling of UTF-8 in precision
(`#1389 <https://github.com/fmtlib/fmt/issues/1389>`_,
`#1390 <https://github.com/fmtlib/fmt/pull/1390>`_).
Thanks `@tajtiattila (Attila Tajti) <https://github.com/tajtiattila>`_.
* {fmt} can now be installed on Linux, macOS and Windows with
`Conda <https://docs.conda.io/en/latest/>`__ using its
`conda-forge <https://conda-forge.org>`__
`package <https://github.com/conda-forge/fmt-feedstock>`__
(`#1410 <https://github.com/fmtlib/fmt/pull/1410>`_)::
conda install -c conda-forge fmt
Thanks `@tdegeus (Tom de Geus) <https://github.com/tdegeus>`_.
* Added a CUDA test (`#1285 <https://github.com/fmtlib/fmt/pull/1285>`_,
`#1317 <https://github.com/fmtlib/fmt/pull/1317>`_).
Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_ and
`@risa2000 <https://github.com/risa2000>`_.
* Improved documentation (`#1276 <https://github.com/fmtlib/fmt/pull/1276>`_,
`#1291 <https://github.com/fmtlib/fmt/issues/1291>`_,
`#1296 <https://github.com/fmtlib/fmt/issues/1296>`_,
`#1315 <https://github.com/fmtlib/fmt/pull/1315>`_,
`#1332 <https://github.com/fmtlib/fmt/pull/1332>`_,
`#1337 <https://github.com/fmtlib/fmt/pull/1337>`_,
`#1395 <https://github.com/fmtlib/fmt/issues/1395>`_
`#1418 <https://github.com/fmtlib/fmt/pull/1418>`_).
Thanks
`@waywardmonkeys (Bruce Mitchener) <https://github.com/waywardmonkeys>`_,
`@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_,
`@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_.
* Various code improvements
(`#1358 <https://github.com/fmtlib/fmt/pull/1358>`_,
`#1407 <https://github.com/fmtlib/fmt/pull/1407>`_).
Thanks `@orivej (Orivej Desh) <https://github.com/orivej>`_,
`@dpacbach (David P. Sicilia) <https://github.com/dpacbach>`_,
* Fixed compile-time format string checks for user-defined types
(`#1292 <https://github.com/fmtlib/fmt/issues/1292>`_).
* Worked around a false positive in ``unsigned-integer-overflow`` sanitizer
(`#1377 <https://github.com/fmtlib/fmt/issues/1377>`_).
* Fixed various warnings and compilation issues
(`#1273 <https://github.com/fmtlib/fmt/issues/1273>`_,
`#1278 <https://github.com/fmtlib/fmt/pull/1278>`_,
`#1280 <https://github.com/fmtlib/fmt/pull/1280>`_,
`#1281 <https://github.com/fmtlib/fmt/issues/1281>`_,
`#1288 <https://github.com/fmtlib/fmt/issues/1288>`_,
`#1290 <https://github.com/fmtlib/fmt/pull/1290>`_,
`#1301 <https://github.com/fmtlib/fmt/pull/1301>`_,
`#1305 <https://github.com/fmtlib/fmt/issues/1305>`_,
`#1306 <https://github.com/fmtlib/fmt/issues/1306>`_,
`#1309 <https://github.com/fmtlib/fmt/issues/1309>`_,
`#1312 <https://github.com/fmtlib/fmt/pull/1312>`_,
`#1313 <https://github.com/fmtlib/fmt/issues/1313>`_,
`#1316 <https://github.com/fmtlib/fmt/issues/1316>`_,
`#1319 <https://github.com/fmtlib/fmt/issues/1319>`_,
`#1320 <https://github.com/fmtlib/fmt/pull/1320>`_,
`#1326 <https://github.com/fmtlib/fmt/pull/1326>`_,
`#1328 <https://github.com/fmtlib/fmt/pull/1328>`_,
`#1344 <https://github.com/fmtlib/fmt/issues/1344>`_,
`#1345 <https://github.com/fmtlib/fmt/pull/1345>`_,
`#1347 <https://github.com/fmtlib/fmt/pull/1347>`_,
`#1349 <https://github.com/fmtlib/fmt/pull/1349>`_,
`#1354 <https://github.com/fmtlib/fmt/issues/1354>`_,
`#1362 <https://github.com/fmtlib/fmt/issues/1362>`_,
`#1366 <https://github.com/fmtlib/fmt/issues/1366>`_,
`#1364 <https://github.com/fmtlib/fmt/pull/1364>`_,
`#1370 <https://github.com/fmtlib/fmt/pull/1370>`_,
`#1371 <https://github.com/fmtlib/fmt/pull/1371>`_,
`#1385 <https://github.com/fmtlib/fmt/issues/1385>`_,
`#1388 <https://github.com/fmtlib/fmt/issues/1388>`_,
`#1397 <https://github.com/fmtlib/fmt/pull/1397>`_,
`#1414 <https://github.com/fmtlib/fmt/pull/1414>`_,
`#1416 <https://github.com/fmtlib/fmt/pull/1416>`_,
`#1422 <https://github.com/fmtlib/fmt/issues/1422>`_
`#1427 <https://github.com/fmtlib/fmt/pull/1427>`_,
`#1431 <https://github.com/fmtlib/fmt/issues/1431>`_,
`#1433 <https://github.com/fmtlib/fmt/pull/1433>`_).
Thanks `@hhb <https://github.com/hhb>`_,
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@gabime (Gabi Melman) <https://github.com/gabime>`_,
`@neheb (Rosen Penev) <https://github.com/neheb>`_,
`@vedranmiletic (Vedran Miletić) <https://github.com/vedranmiletic>`_,
`@dkavolis (Daumantas Kavolis) <https://github.com/dkavolis>`_,
`@mwinterb <https://github.com/mwinterb>`_,
`@orivej (Orivej Desh) <https://github.com/orivej>`_,
`@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_
`@leonklingele <https://github.com/leonklingele>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@kent-tri <https://github.com/kent-tri>`_,
`@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_,
`@marti4d (Chris Martin) <https://github.com/marti4d>`_.
6.0.0 - 2019-08-26
------------------
* Switched to the `MIT license
<https://github.com/fmtlib/fmt/blob/5a4b24613ba16cc689977c3b5bd8274a3ba1dd1f/LICENSE.rst>`_
with an optional exception that allows distributing binary code without
attribution.
* Floating-point formatting is now locale-independent by default:
.. code:: c++
#include <locale>
#include <fmt/core.h>
int main() {
std::locale::global(std::locale("ru_RU.UTF-8"));
fmt::print("value = {}", 4.2);
}
prints "value = 4.2" regardless of the locale.
For locale-specific formatting use the ``n`` specifier:
.. code:: c++
std::locale::global(std::locale("ru_RU.UTF-8"));
fmt::print("value = {:n}", 4.2);
prints "value = 4,2".
* Added an experimental Grisu floating-point formatting algorithm
implementation (disabled by default). To enable it compile with the
``FMT_USE_GRISU`` macro defined to 1:
.. code:: c++
#define FMT_USE_GRISU 1
#include <fmt/format.h>
auto s = fmt::format("{}", 4.2); // formats 4.2 using Grisu
With Grisu enabled, {fmt} is 13x faster than ``std::ostringstream`` (libc++)
and 10x faster than ``sprintf`` on `dtoa-benchmark
<https://github.com/fmtlib/dtoa-benchmark>`_ (`full results
<https://fmt.dev/unknown_mac64_clang10.0.html>`_):
.. image:: https://user-images.githubusercontent.com/576385/
54883977-9fe8c000-4e28-11e9-8bde-272d122e7c52.jpg
* Separated formatting and parsing contexts for consistency with
`C++20 std::format <http://eel.is/c++draft/format>`_, removing the
undocumented ``basic_format_context::parse_context()`` function.
* Added `oss-fuzz <https://github.com/google/oss-fuzz>`_ support
(`#1199 <https://github.com/fmtlib/fmt/pull/1199>`_).
Thanks `@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_.
* ``formatter`` specializations now always take precedence over ``operator<<``
(`#952 <https://github.com/fmtlib/fmt/issues/952>`_):
.. code:: c++
#include <iostream>
#include <fmt/ostream.h>
struct S {};
std::ostream& operator<<(std::ostream& os, S) {
return os << 1;
}
template <>
struct fmt::formatter<S> : fmt::formatter<int> {
auto format(S, format_context& ctx) {
return formatter<int>::format(2, ctx);
}
};
int main() {
std::cout << S() << "\n"; // prints 1 using operator<<
fmt::print("{}\n", S()); // prints 2 using formatter
}
* Introduced the experimental ``fmt::compile`` function that does format string
compilation (`#618 <https://github.com/fmtlib/fmt/issues/618>`_,
`#1169 <https://github.com/fmtlib/fmt/issues/1169>`_,
`#1171 <https://github.com/fmtlib/fmt/pull/1171>`_):
.. code:: c++
#include <fmt/compile.h>
auto f = fmt::compile<int>("{}");
std::string s = fmt::format(f, 42); // can be called multiple times to format
// different values
// s == "42"
It moves the cost of parsing a format string outside of the format function
which can be beneficial when identically formatting many objects of the same
types. Thanks `@stryku (Mateusz Janek) <https://github.com/stryku>`_.
* Added the ``%`` format specifier that formats floating-point values as
percentages (`#1060 <https://github.com/fmtlib/fmt/pull/1060>`_,
`#1069 <https://github.com/fmtlib/fmt/pull/1069>`_,
`#1071 <https://github.com/fmtlib/fmt/pull/1071>`_):
.. code:: c++
auto s = fmt::format("{:.1%}", 0.42); // s == "42.0%"
Thanks `@gawain-bolton (Gawain Bolton) <https://github.com/gawain-bolton>`_.
* Implemented precision for floating-point durations
(`#1004 <https://github.com/fmtlib/fmt/issues/1004>`_,
`#1012 <https://github.com/fmtlib/fmt/pull/1012>`_):
.. code:: c++
auto s = fmt::format("{:.1}", std::chrono::duration<double>(1.234));
// s == 1.2s
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Implemented ``chrono`` format specifiers ``%Q`` and ``%q`` that give the value
and the unit respectively (`#1019 <https://github.com/fmtlib/fmt/pull/1019>`_):
.. code:: c++
auto value = fmt::format("{:%Q}", 42s); // value == "42"
auto unit = fmt::format("{:%q}", 42s); // unit == "s"
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Fixed handling of dynamic width in chrono formatter:
.. code:: c++
auto s = fmt::format("{0:{1}%H:%M:%S}", std::chrono::seconds(12345), 12);
// ^ width argument index ^ width
// s == "03:25:45 "
Thanks Howard Hinnant.
* Removed deprecated ``fmt/time.h``. Use ``fmt/chrono.h`` instead.
* Added ``fmt::format`` and ``fmt::vformat`` overloads that take ``text_style``
(`#993 <https://github.com/fmtlib/fmt/issues/993>`_,
`#994 <https://github.com/fmtlib/fmt/pull/994>`_):
.. code:: c++
#include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}.", 42);
Thanks `@Naios (Denis Blank) <https://github.com/Naios>`_.
* Removed the deprecated color API (``print_colored``). Use the new API, namely
``print`` overloads that take ``text_style`` instead.
* Made ``std::unique_ptr`` and ``std::shared_ptr`` formattable as pointers via
``fmt::ptr`` (`#1121 <https://github.com/fmtlib/fmt/pull/1121>`_):
.. code:: c++
std::unique_ptr<int> p = ...;
fmt::print("{}", fmt::ptr(p)); // prints p as a pointer
Thanks `@sighingnow (Tao He) <https://github.com/sighingnow>`_.
* Made ``print`` and ``vprint`` report I/O errors
(`#1098 <https://github.com/fmtlib/fmt/issues/1098>`_,
`#1099 <https://github.com/fmtlib/fmt/pull/1099>`_).
Thanks `@BillyDonahue (Billy Donahue) <https://github.com/BillyDonahue>`_.
* Marked deprecated APIs with the ``[[deprecated]]`` attribute and removed
internal uses of deprecated APIs
(`#1022 <https://github.com/fmtlib/fmt/pull/1022>`_).
Thanks `@eliaskosunen (Elias Kosunen) <https://github.com/eliaskosunen>`_.
* Modernized the codebase using more C++11 features and removing workarounds.
Most importantly, ``buffer_context`` is now an alias template, so
use ``buffer_context<T>`` instead of ``buffer_context<T>::type``.
These features require GCC 4.8 or later.
* ``formatter`` specializations now always take precedence over implicit
conversions to ``int`` and the undocumented ``convert_to_int`` trait
is now deprecated.
* Moved the undocumented ``basic_writer``, ``writer``, and ``wwriter`` types
to the ``internal`` namespace.
* Removed deprecated ``basic_format_context::begin()``. Use ``out()`` instead.
* Disallowed passing the result of ``join`` as an lvalue to prevent misuse.
* Refactored the undocumented structs that represent parsed format specifiers
to simplify the API and allow multibyte fill.
* Moved SFINAE to template parameters to reduce symbol sizes.
* Switched to ``fputws`` for writing wide strings so that it's no longer
required to call ``_setmode`` on Windows
(`#1229 <https://github.com/fmtlib/fmt/issues/1229>`_,
`#1243 <https://github.com/fmtlib/fmt/pull/1243>`_).
Thanks `@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_.
* Improved literal-based API
(`#1254 <https://github.com/fmtlib/fmt/pull/1254>`_).
Thanks `@sylveon (Charles Milette) <https://github.com/sylveon>`_.
* Added support for exotic platforms without ``uintptr_t`` such as IBM i
(AS/400) which has 128-bit pointers and only 64-bit integers
(`#1059 <https://github.com/fmtlib/fmt/issues/1059>`_).
* Added `Sublime Text syntax highlighting config
<https://github.com/fmtlib/fmt/blob/master/support/C%2B%2B.sublime-syntax>`_
(`#1037 <https://github.com/fmtlib/fmt/issues/1037>`_).
Thanks `@Kronuz (Germán Méndez Bravo) <https://github.com/Kronuz>`_.
* Added the ``FMT_ENFORCE_COMPILE_STRING`` macro to enforce the use of
compile-time format strings
(`#1231 <https://github.com/fmtlib/fmt/pull/1231>`_).
Thanks `@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_.
* Stopped setting ``CMAKE_BUILD_TYPE`` if {fmt} is a subproject
(`#1081 <https://github.com/fmtlib/fmt/issues/1081>`_).
* Various build improvements
(`#1039 <https://github.com/fmtlib/fmt/pull/1039>`_,
`#1078 <https://github.com/fmtlib/fmt/pull/1078>`_,
`#1091 <https://github.com/fmtlib/fmt/pull/1091>`_,
`#1103 <https://github.com/fmtlib/fmt/pull/1103>`_,
`#1177 <https://github.com/fmtlib/fmt/pull/1177>`_).
Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_,
`@jasonszang (Jason Shuo Zang) <https://github.com/jasonszang>`_,
`@olafhering (Olaf Hering) <https://github.com/olafhering>`_,
`@Lecetem <https://github.com/Lectem>`_,
`@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_.
* Improved documentation
(`#1049 <https://github.com/fmtlib/fmt/issues/1049>`_,
`#1051 <https://github.com/fmtlib/fmt/pull/1051>`_,
`#1083 <https://github.com/fmtlib/fmt/pull/1083>`_,
`#1113 <https://github.com/fmtlib/fmt/pull/1113>`_,
`#1114 <https://github.com/fmtlib/fmt/pull/1114>`_,
`#1146 <https://github.com/fmtlib/fmt/issues/1146>`_,
`#1180 <https://github.com/fmtlib/fmt/issues/1180>`_,
`#1250 <https://github.com/fmtlib/fmt/pull/1250>`_,
`#1252 <https://github.com/fmtlib/fmt/pull/1252>`_,
`#1265 <https://github.com/fmtlib/fmt/pull/1265>`_).
Thanks `@mikelui (Michael Lui) <https://github.com/mikelui>`_,
`@foonathan (Jonathan Müller) <https://github.com/foonathan>`_,
`@BillyDonahue (Billy Donahue) <https://github.com/BillyDonahue>`_,
`@jwakely (Jonathan Wakely) <https://github.com/jwakely>`_,
`@kaisbe (Kais Ben Salah) <https://github.com/kaisbe>`_,
`@sdebionne (Samuel Debionne) <https://github.com/sdebionne>`_.
* Fixed ambiguous formatter specialization in ``fmt/ranges.h``
(`#1123 <https://github.com/fmtlib/fmt/issues/1123>`_).
* Fixed formatting of a non-empty ``std::filesystem::path`` which is an
infinitely deep range of its components
(`#1268 <https://github.com/fmtlib/fmt/issues/1268>`_).
* Fixed handling of general output iterators when formatting characters
(`#1056 <https://github.com/fmtlib/fmt/issues/1056>`_,
`#1058 <https://github.com/fmtlib/fmt/pull/1058>`_).
Thanks `@abolz (Alexander Bolz) <https://github.com/abolz>`_.
* Fixed handling of output iterators in ``formatter`` specialization for
ranges (`#1064 <https://github.com/fmtlib/fmt/issues/1064>`_).
* Fixed handling of exotic character types
(`#1188 <https://github.com/fmtlib/fmt/issues/1188>`_).
* Made chrono formatting work with exceptions disabled
(`#1062 <https://github.com/fmtlib/fmt/issues/1062>`_).
* Fixed DLL visibility issues
(`#1134 <https://github.com/fmtlib/fmt/pull/1134>`_,
`#1147 <https://github.com/fmtlib/fmt/pull/1147>`_).
Thanks `@denchat <https://github.com/denchat>`_.
* Disabled the use of UDL template extension on GCC 9
(`#1148 <https://github.com/fmtlib/fmt/issues/1148>`_).
* Removed misplaced ``format`` compile-time checks from ``printf``
(`#1173 <https://github.com/fmtlib/fmt/issues/1173>`_).
* Fixed issues in the experimental floating-point formatter
(`#1072 <https://github.com/fmtlib/fmt/issues/1072>`_,
`#1129 <https://github.com/fmtlib/fmt/issues/1129>`_,
`#1153 <https://github.com/fmtlib/fmt/issues/1153>`_,
`#1155 <https://github.com/fmtlib/fmt/pull/1155>`_,
`#1210 <https://github.com/fmtlib/fmt/issues/1210>`_,
`#1222 <https://github.com/fmtlib/fmt/issues/1222>`_).
Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
* Fixed bugs discovered by fuzzing or during fuzzing integration
(`#1124 <https://github.com/fmtlib/fmt/issues/1124>`_,
`#1127 <https://github.com/fmtlib/fmt/issues/1127>`_,
`#1132 <https://github.com/fmtlib/fmt/issues/1132>`_,
`#1135 <https://github.com/fmtlib/fmt/pull/1135>`_,
`#1136 <https://github.com/fmtlib/fmt/issues/1136>`_,
`#1141 <https://github.com/fmtlib/fmt/issues/1141>`_,
`#1142 <https://github.com/fmtlib/fmt/issues/1142>`_,
`#1178 <https://github.com/fmtlib/fmt/issues/1178>`_,
`#1179 <https://github.com/fmtlib/fmt/issues/1179>`_,
`#1194 <https://github.com/fmtlib/fmt/issues/1194>`_).
Thanks `@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_.
* Fixed building tests on FreeBSD and Hurd
(`#1043 <https://github.com/fmtlib/fmt/issues/1043>`_).
Thanks `@jackyf (Eugene V. Lyubimkin) <https://github.com/jackyf>`_.
* Fixed various warnings and compilation issues
(`#998 <https://github.com/fmtlib/fmt/pull/998>`_,
`#1006 <https://github.com/fmtlib/fmt/pull/1006>`_,
`#1008 <https://github.com/fmtlib/fmt/issues/1008>`_,
`#1011 <https://github.com/fmtlib/fmt/issues/1011>`_,
`#1025 <https://github.com/fmtlib/fmt/issues/1025>`_,
`#1027 <https://github.com/fmtlib/fmt/pull/1027>`_,
`#1028 <https://github.com/fmtlib/fmt/pull/1028>`_,
`#1029 <https://github.com/fmtlib/fmt/pull/1029>`_,
`#1030 <https://github.com/fmtlib/fmt/pull/1030>`_,
`#1031 <https://github.com/fmtlib/fmt/pull/1031>`_,
`#1054 <https://github.com/fmtlib/fmt/pull/1054>`_,
`#1063 <https://github.com/fmtlib/fmt/issues/1063>`_,
`#1068 <https://github.com/fmtlib/fmt/pull/1068>`_,
`#1074 <https://github.com/fmtlib/fmt/pull/1074>`_,
`#1075 <https://github.com/fmtlib/fmt/pull/1075>`_,
`#1079 <https://github.com/fmtlib/fmt/pull/1079>`_,
`#1086 <https://github.com/fmtlib/fmt/pull/1086>`_,
`#1088 <https://github.com/fmtlib/fmt/issues/1088>`_,
`#1089 <https://github.com/fmtlib/fmt/pull/1089>`_,
`#1094 <https://github.com/fmtlib/fmt/pull/1094>`_,
`#1101 <https://github.com/fmtlib/fmt/issues/1101>`_,
`#1102 <https://github.com/fmtlib/fmt/pull/1102>`_,
`#1105 <https://github.com/fmtlib/fmt/issues/1105>`_,
`#1107 <https://github.com/fmtlib/fmt/pull/1107>`_,
`#1115 <https://github.com/fmtlib/fmt/issues/1115>`_,
`#1117 <https://github.com/fmtlib/fmt/issues/1117>`_,
`#1118 <https://github.com/fmtlib/fmt/issues/1118>`_,
`#1120 <https://github.com/fmtlib/fmt/issues/1120>`_,
`#1123 <https://github.com/fmtlib/fmt/issues/1123>`_,
`#1139 <https://github.com/fmtlib/fmt/pull/1139>`_,
`#1140 <https://github.com/fmtlib/fmt/issues/1140>`_,
`#1143 <https://github.com/fmtlib/fmt/issues/1143>`_,
`#1144 <https://github.com/fmtlib/fmt/pull/1144>`_,
`#1150 <https://github.com/fmtlib/fmt/pull/1150>`_,
`#1151 <https://github.com/fmtlib/fmt/pull/1151>`_,
`#1152 <https://github.com/fmtlib/fmt/issues/1152>`_,
`#1154 <https://github.com/fmtlib/fmt/issues/1154>`_,
`#1156 <https://github.com/fmtlib/fmt/issues/1156>`_,
`#1159 <https://github.com/fmtlib/fmt/pull/1159>`_,
`#1175 <https://github.com/fmtlib/fmt/issues/1175>`_,
`#1181 <https://github.com/fmtlib/fmt/issues/1181>`_,
`#1186 <https://github.com/fmtlib/fmt/issues/1186>`_,
`#1187 <https://github.com/fmtlib/fmt/pull/1187>`_,
`#1191 <https://github.com/fmtlib/fmt/pull/1191>`_,
`#1197 <https://github.com/fmtlib/fmt/issues/1197>`_,
`#1200 <https://github.com/fmtlib/fmt/issues/1200>`_,
`#1203 <https://github.com/fmtlib/fmt/issues/1203>`_,
`#1205 <https://github.com/fmtlib/fmt/issues/1205>`_,
`#1206 <https://github.com/fmtlib/fmt/pull/1206>`_,
`#1213 <https://github.com/fmtlib/fmt/issues/1213>`_,
`#1214 <https://github.com/fmtlib/fmt/issues/1214>`_,
`#1217 <https://github.com/fmtlib/fmt/pull/1217>`_,
`#1228 <https://github.com/fmtlib/fmt/issues/1228>`_,
`#1230 <https://github.com/fmtlib/fmt/pull/1230>`_,
`#1232 <https://github.com/fmtlib/fmt/issues/1232>`_,
`#1235 <https://github.com/fmtlib/fmt/pull/1235>`_,
`#1236 <https://github.com/fmtlib/fmt/pull/1236>`_,
`#1240 <https://github.com/fmtlib/fmt/issues/1240>`_).
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_,
`@mwinterb <https://github.com/mwinterb>`_,
`@eliaskosunen (Elias Kosunen) <https://github.com/eliaskosunen>`_,
`@morinmorin <https://github.com/morinmorin>`_,
`@ricco19 (Brian Ricciardelli) <https://github.com/ricco19>`_,
`@waywardmonkeys (Bruce Mitchener) <https://github.com/waywardmonkeys>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@remyabel <https://github.com/remyabel>`_,
`@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_,
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@rcane (Ronny Krüger) <https://github.com/rcane>`_,
`@mocabe <https://github.com/mocabe>`_,
`@denchat <https://github.com/denchat>`_,
`@cjdb (Christopher Di Bella) <https://github.com/cjdb>`_,
`@HazardyKnusperkeks (Björn Schäpers) <https://github.com/HazardyKnusperkeks>`_,
`@vedranmiletic (Vedran Miletić) <https://github.com/vedranmiletic>`_,
`@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_,
`@DaanDeMeyer (Daan De Meyer) <https://github.com/DaanDeMeyer>`_,
`@starkmapper (Mark Stapper) <https://github.com/starkmapper>`_.
5.3.0 - 2018-12-28 5.3.0 - 2018-12-28
------------------ ------------------
@ -142,7 +821,7 @@
// s == "1~234~567" // s == "1~234~567"
* Constrained formatting functions on proper iterator types * Constrained formatting functions on proper iterator types
(`#921 <https://github.com/fmtlib/fmt/pull/921>`_): (`#921 <https://github.com/fmtlib/fmt/pull/921>`_).
Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_. Thanks `@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
* Added ``make_printf_args`` and ``make_wprintf_args`` functions * Added ``make_printf_args`` and ``make_wprintf_args`` functions
@ -386,7 +1065,7 @@
``color`` enum) is now deprecated. ``color`` enum) is now deprecated.
(`#762 <https://github.com/fmtlib/fmt/issues/762>`_ (`#762 <https://github.com/fmtlib/fmt/issues/762>`_
`#767 <https://github.com/fmtlib/fmt/pull/767>`_). `#767 <https://github.com/fmtlib/fmt/pull/767>`_).
thanks `@remotion (remo) <https://github.com/remotion>`_. thanks `@Remotion (Remo) <https://github.com/Remotion>`_.
* Added quotes to strings in ranges and tuples * Added quotes to strings in ranges and tuples
(`#766 <https://github.com/fmtlib/fmt/pull/766>`_). (`#766 <https://github.com/fmtlib/fmt/pull/766>`_).
@ -467,7 +1146,7 @@
* Implemented ``constexpr`` parsing of format strings and `compile-time format * Implemented ``constexpr`` parsing of format strings and `compile-time format
string checks string checks
<http://fmtlib.net/dev/api.html#compile-time-format-string-checks>`_. For <https://fmt.dev/dev/api.html#compile-time-format-string-checks>`_. For
example example
.. code:: c++ .. code:: c++
@ -528,7 +1207,7 @@
throw format_error("invalid specifier"); throw format_error("invalid specifier");
* Added `iterator support * Added `iterator support
<http://fmtlib.net/dev/api.html#output-iterator-support>`_: <https://fmt.dev/dev/api.html#output-iterator-support>`_:
.. code:: c++ .. code:: c++
@ -539,7 +1218,7 @@
fmt::format_to(std::back_inserter(out), "{}", 42); fmt::format_to(std::back_inserter(out), "{}", 42);
* Added the `format_to_n * Added the `format_to_n
<http://fmtlib.net/dev/api.html#_CPPv2N3fmt11format_to_nE8OutputItNSt6size_tE11string_viewDpRK4Args>`_ <https://fmt.dev/dev/api.html#_CPPv2N3fmt11format_to_nE8OutputItNSt6size_tE11string_viewDpRK4Args>`_
function that restricts the output to the specified number of characters function that restricts the output to the specified number of characters
(`#298 <https://github.com/fmtlib/fmt/issues/298>`_): (`#298 <https://github.com/fmtlib/fmt/issues/298>`_):
@ -550,7 +1229,7 @@
// out == "1234" (without terminating '\0') // out == "1234" (without terminating '\0')
* Added the `formatted_size * Added the `formatted_size
<http://fmtlib.net/dev/api.html#_CPPv2N3fmt14formatted_sizeE11string_viewDpRK4Args>`_ <https://fmt.dev/dev/api.html#_CPPv2N3fmt14formatted_sizeE11string_viewDpRK4Args>`_
function for computing the output size: function for computing the output size:
.. code:: c++ .. code:: c++
@ -560,7 +1239,7 @@
auto size = fmt::formatted_size("{}", 12345); // size == 5 auto size = fmt::formatted_size("{}", 12345); // size == 5
* Improved compile times by reducing dependencies on standard headers and * Improved compile times by reducing dependencies on standard headers and
providing a lightweight `core API <http://fmtlib.net/dev/api.html#core-api>`_: providing a lightweight `core API <https://fmt.dev/dev/api.html#core-api>`_:
.. code:: c++ .. code:: c++
@ -572,7 +1251,7 @@
<https://github.com/fmtlib/fmt#compile-time-and-code-bloat>`_. <https://github.com/fmtlib/fmt#compile-time-and-code-bloat>`_.
* Added the `make_format_args * Added the `make_format_args
<http://fmtlib.net/dev/api.html#_CPPv2N3fmt16make_format_argsEDpRK4Args>`_ <https://fmt.dev/dev/api.html#_CPPv2N3fmt16make_format_argsEDpRK4Args>`_
function for capturing formatting arguments: function for capturing formatting arguments:
.. code:: c++ .. code:: c++
@ -654,7 +1333,7 @@
fmt::format("{} {two}", 1, fmt::arg("two", 2)); fmt::format("{} {two}", 1, fmt::arg("two", 2));
* Removed the write API in favor of the `format API * Removed the write API in favor of the `format API
<http://fmtlib.net/dev/api.html#format-api>`_ with compile-time handling of <https://fmt.dev/dev/api.html#format-api>`_ with compile-time handling of
format strings. format strings.
* Disallowed formatting of multibyte strings into a wide character target * Disallowed formatting of multibyte strings into a wide character target
@ -1057,7 +1736,7 @@
3.0.1 - 2016-11-01 3.0.1 - 2016-11-01
------------------ ------------------
* Fixed handling of thousands seperator * Fixed handling of thousands separator
(`#353 <https://github.com/fmtlib/fmt/issues/353>`_). (`#353 <https://github.com/fmtlib/fmt/issues/353>`_).
* Fixed handling of ``unsigned char`` strings * Fixed handling of ``unsigned char`` strings
@ -1115,10 +1794,10 @@
Including ``format.h`` from the ``cppformat`` directory is deprecated Including ``format.h`` from the ``cppformat`` directory is deprecated
but works via a proxy header which will be removed in the next major version. but works via a proxy header which will be removed in the next major version.
The documentation is now available at http://fmtlib.net. The documentation is now available at https://fmt.dev.
* Added support for `strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_-like * Added support for `strftime <http://en.cppreference.com/w/cpp/chrono/c/strftime>`_-like
`date and time formatting <http://fmtlib.net/3.0.0/api.html#date-and-time-formatting>`_ `date and time formatting <https://fmt.dev/3.0.0/api.html#date-and-time-formatting>`_
(`#283 <https://github.com/fmtlib/fmt/issues/283>`_): (`#283 <https://github.com/fmtlib/fmt/issues/283>`_):
.. code:: c++ .. code:: c++
@ -1150,7 +1829,7 @@
// s == "The date is 2012-12-9" // s == "The date is 2012-12-9"
* Added support for `custom argument formatters * Added support for `custom argument formatters
<http://fmtlib.net/3.0.0/api.html#argument-formatters>`_ <https://fmt.dev/3.0.0/api.html#argument-formatters>`_
(`#235 <https://github.com/fmtlib/fmt/issues/235>`_). (`#235 <https://github.com/fmtlib/fmt/issues/235>`_).
* Added support for locale-specific integer formatting with the ``n`` specifier * Added support for locale-specific integer formatting with the ``n`` specifier
@ -1530,7 +2209,7 @@ Documentation
* Added `Building the documentation * Added `Building the documentation
<http://fmtlib.net/2.0.0/usage.html#building-the-documentation>`_ <https://fmt.dev/2.0.0/usage.html#building-the-documentation>`_
section to the documentation. section to the documentation.
* Documentation build script is now compatible with Python 3 and newer pip versions. * Documentation build script is now compatible with Python 3 and newer pip versions.

View File

@ -1,23 +1,27 @@
Copyright (c) 2012 - 2016, Victor Zverovich Copyright (c) 2012 - present, Victor Zverovich
All rights reserved. Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
Redistribution and use in source and binary forms, with or without The above copyright notice and this permission notice shall be
modification, are permitted provided that the following conditions are met: included in all copies or substantial portions of the Software.
1. Redistributions of source code must retain the above copyright notice, this THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
list of conditions and the following disclaimer. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2. Redistributions in binary form must reproduce the above copyright notice, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
this list of conditions and the following disclaimer in the documentation NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
and/or other materials provided with the distribution. LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND --- Optional exception to the license ---
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE As an exception, if, as a result of your compiling your source code, portions
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR of this Software are embedded into a machine-executable object form of such
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES source code, you may redistribute such embedded portions in such object form
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; without including the above copyright and permission notices.
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -6,90 +6,90 @@
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v .. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
:target: https://ci.appveyor.com/project/vitaut/fmt :target: https://ci.appveyor.com/project/vitaut/fmt
.. image:: https://badges.gitter.im/Join%20Chat.svg .. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg
:alt: Join the chat at https://gitter.im/fmtlib/fmt :alt: fmt is continuously fuzzed att oss-fuzz
:target: https://gitter.im/fmtlib/fmt :target: https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dlibfmt&can=1
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
:alt: Ask questions at StackOverflow with the tag fmt
:target: http://stackoverflow.com/questions/tagged/fmt
**{fmt}** is an open-source formatting library for C++. **{fmt}** is an open-source formatting library for C++.
It can be used as a safe and fast alternative to (s)printf and IOStreams. It can be used as a safe and fast alternative to (s)printf and iostreams.
`Documentation <http://fmtlib.net/latest/>`__ `Documentation <https://fmt.dev/latest/>`__
This is a development branch that implements the C++ standards proposal `P0645 Q&A: ask questions on `StackOverflow with the tag fmt <http://stackoverflow.com/questions/tagged/fmt>`_.
Text Formatting <http://fmtlib.net/Text%20Formatting.html>`__.
Released versions are available from the `Releases page
<https://github.com/fmtlib/fmt/releases>`__.
Features Features
-------- --------
* Replacement-based `format API <http://fmtlib.net/dev/api.html>`_ with * Replacement-based `format API <https://fmt.dev/dev/api.html>`_ with
positional arguments for localization. positional arguments for localization.
* `Format string syntax <http://fmtlib.net/dev/syntax.html>`_ similar to the one * `Format string syntax <https://fmt.dev/dev/syntax.html>`_ similar to the one
of `str.format <https://docs.python.org/2/library/stdtypes.html#str.format>`_ of `str.format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
in Python. in Python.
* Safe `printf implementation * Safe `printf implementation
<http://fmtlib.net/latest/api.html#printf-formatting>`_ including <https://fmt.dev/latest/api.html#printf-formatting>`_ including
the POSIX extension for positional arguments. the POSIX extension for positional arguments.
* Implementation of `C++20 std::format <https://fmt.dev/Text%20Formatting.html>`__.
* Support for user-defined types. * Support for user-defined types.
* High speed: performance of the format API is close to that of glibc's `printf * High performance: faster than common standard library implementations of
<http://en.cppreference.com/w/cpp/io/c/fprintf>`_ and better than the `printf <http://en.cppreference.com/w/cpp/io/c/fprintf>`_ and
performance of IOStreams. See `Speed tests`_ and iostreams. See `Speed tests`_ and `Fast integer to string conversion in C++
`Fast integer to string conversion in C++
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_. <http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
* Small code size both in terms of source code (the minimum configuration * Small code size both in terms of source code (the minimum configuration
consists of just three header files, ``core.h``, ``format.h`` and consists of just three header files, ``core.h``, ``format.h`` and
``format-inl.h``) and compiled code. See `Compile time and code bloat`_. ``format-inl.h``) and compiled code. See `Compile time and code bloat`_.
* Reliability: the library has an extensive set of `unit tests * Reliability: the library has an extensive set of `unit tests
<https://github.com/fmtlib/fmt/tree/master/test>`_. <https://github.com/fmtlib/fmt/tree/master/test>`_ and is continuously fuzzed.
* Safety: the library is fully type safe, errors in format strings can be * Safety: the library is fully type safe, errors in format strings can be
reported at compile time, automatic memory management prevents buffer overflow reported at compile time, automatic memory management prevents buffer overflow
errors. errors.
* Ease of use: small self-contained code base, no external dependencies, * Ease of use: small self-contained code base, no external dependencies,
permissive BSD `license permissive MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_ <https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
* `Portability <http://fmtlib.net/latest/index.html#portability>`_ with * `Portability <https://fmt.dev/latest/index.html#portability>`_ with
consistent output across platforms and support for older compilers. consistent output across platforms and support for older compilers.
* Clean warning-free codebase even on high warning levels * Clean warning-free codebase even on high warning levels
(``-Wall -Wextra -pedantic``). (``-Wall -Wextra -pedantic``).
* Support for wide strings. * Support for wide strings.
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro. * Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro.
See the `documentation <http://fmtlib.net/latest/>`_ for more details. See the `documentation <https://fmt.dev/latest/>`_ for more details.
Examples Examples
-------- --------
This prints ``Hello, world!`` to stdout: Print ``Hello, world!`` to ``stdout``:
.. code:: c++ .. code:: c++
fmt::print("Hello, {}!", "world"); // uses Python-like format string syntax fmt::print("Hello, {}!", "world"); // Python-like format string syntax
fmt::printf("Hello, %s!", "world"); // uses printf format string syntax fmt::printf("Hello, %s!", "world"); // printf format string syntax
Arguments can be accessed by position and arguments' indices can be repeated: Format a string and use positional arguments:
.. code:: c++ .. code:: c++
std::string s = fmt::format("{0}{1}{0}", "abra", "cad"); std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
// s == "abracadabra" // s == "I'd rather be happy than right."
Format strings can be checked at compile time: Check a format string at compile time:
.. code:: c++ .. code:: c++
// test.cc // test.cc
#define FMT_STRING_ALIAS 1
#include <fmt/format.h> #include <fmt/format.h>
std::string s = format(fmt("{2}"), 42); std::string s = format(FMT_STRING("{2}"), 42);
.. code:: .. code::
$ c++ -Iinclude -std=c++14 test.cc $ c++ -Iinclude -std=c++14 test.cc
... ...
test.cc:4:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here test.cc:4:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here
std::string s = format(fmt("{2}"), 42); std::string s = format(FMT_STRING("{2}"), 42);
^ ^
include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression
ErrorHandler::on_error(message); ErrorHandler::on_error(message);
@ -98,7 +98,7 @@ Format strings can be checked at compile time:
context_.on_error("argument index out of range"); context_.on_error("argument index out of range");
^ ^
{fmt} can be used as a safe portable replacement for ``itoa`` Use {fmt} as a safe portable replacement for ``itoa``
(`godbolt <https://godbolt.org/g/NXmpU4>`_): (`godbolt <https://godbolt.org/g/NXmpU4>`_):
.. code:: c++ .. code:: c++
@ -106,10 +106,10 @@ Format strings can be checked at compile time:
fmt::memory_buffer buf; fmt::memory_buffer buf;
format_to(buf, "{}", 42); // replaces itoa(42, buffer, 10) format_to(buf, "{}", 42); // replaces itoa(42, buffer, 10)
format_to(buf, "{:x}", 42); // replaces itoa(42, buffer, 16) format_to(buf, "{:x}", 42); // replaces itoa(42, buffer, 16)
// access the string using to_string(buf) or buf.data() // access the string with to_string(buf) or buf.data()
Formatting of user-defined types is supported via a simple Format objects of user-defined types via a simple `extension API
`extension API <http://fmtlib.net/latest/api.html#formatting-user-defined-types>`_: <https://fmt.dev/latest/api.html#formatting-user-defined-types>`_:
.. code:: c++ .. code:: c++
@ -121,11 +121,10 @@ Formatting of user-defined types is supported via a simple
template <> template <>
struct fmt::formatter<date> { struct fmt::formatter<date> {
template <typename ParseContext> constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
template <typename FormatContext> template <typename FormatContext>
auto format(const date &d, FormatContext &ctx) { auto format(const date& d, FormatContext& ctx) {
return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day); return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day);
} }
}; };
@ -133,29 +132,135 @@ Formatting of user-defined types is supported via a simple
std::string s = fmt::format("The date is {}", date{2012, 12, 9}); std::string s = fmt::format("The date is {}", date{2012, 12, 9});
// s == "The date is 2012-12-9" // s == "The date is 2012-12-9"
You can create your own functions similar to `format Create your own functions similar to `format
<http://fmtlib.net/latest/api.html#format>`_ and <https://fmt.dev/latest/api.html#format>`_ and
`print <http://fmtlib.net/latest/api.html#print>`_ `print <https://fmt.dev/latest/api.html#print>`_
which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_): which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_):
.. code:: c++ .. code:: c++
// Prints formatted error message. // Prints formatted error message.
void vreport_error(const char *format, fmt::format_args args) { void vreport_error(const char* format, fmt::format_args args) {
fmt::print("Error: "); fmt::print("Error: ");
fmt::vprint(format, args); fmt::vprint(format, args);
} }
template <typename... Args> template <typename... Args>
void report_error(const char *format, const Args & ... args) { void report_error(const char* format, const Args & ... args) {
vreport_error(format, fmt::make_format_args(args...)); vreport_error(format, fmt::make_format_args(args...));
} }
report_error("file not found: {}", path); report_error("file not found: {}", path);
Note that ``vreport_error`` is not parameterized on argument types which can Note that ``vreport_error`` is not parameterized on argument types which can
improve compile times and reduce code size compared to fully parameterized improve compile times and reduce code size compared to a fully parameterized
version. version.
Benchmarks
----------
Speed tests
~~~~~~~~~~~
================= ============= ===========
Library Method Run Time, s
================= ============= ===========
libc printf 1.04
libc++ std::ostream 3.05
{fmt} 6.1.1 fmt::print 0.75
Boost Format 1.67 boost::format 7.24
Folly Format folly::format 2.23
================= ============= ===========
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``.
The above results were generated by building ``tinyformat_test.cpp`` on macOS
10.14.6 with ``clang++ -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of
three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
further details refer to the `source
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
{fmt} is 10x faster than ``std::ostringstream`` and ``sprintf`` on floating-point
formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
and as fast as `double-conversion <https://github.com/google/double-conversion>`_:
.. image:: https://user-images.githubusercontent.com/576385/69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
:target: https://fmt.dev/unknown_mac64_clang10.0.html
Compile time and code bloat
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The script `bloat-test.py
<https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py>`_
from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
tests compile time and code bloat for nontrivial projects.
It generates 100 translation units and uses ``printf()`` or its alternative
five times in each to simulate a medium sized project. The resulting
executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42),
macOS Sierra, best of three) is shown in the following tables.
**Optimized build (-O3)**
============= =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============= =============== ==================== ==================
printf 2.6 29 26
printf+string 16.4 29 26
iostreams 31.1 59 55
{fmt} 19.0 37 34
Boost Format 91.9 226 203
Folly Format 115.7 101 88
============= =============== ==================== ==================
As you can see, {fmt} has 60% less overhead in terms of resulting binary code
size compared to iostreams and comes pretty close to ``printf``. Boost Format
and Folly Format have the largest overheads.
``printf+string`` is the same as ``printf`` but with extra ``<string>``
include to measure the overhead of the latter.
**Non-optimized build**
============= =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============= =============== ==================== ==================
printf 2.2 33 30
printf+string 16.0 33 30
iostreams 28.3 56 52
{fmt} 18.2 59 50
Boost Format 54.1 365 303
Folly Format 79.9 445 430
============= =============== ==================== ==================
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared libraries to
compare formatting function overhead only. Boost Format is a
header-only library so it doesn't provide any linkage options.
Running the tests
~~~~~~~~~~~~~~~~~
Please refer to `Building the library`__ for the instructions on how to build
the library and run the unit tests.
__ https://fmt.dev/latest/usage.html#building-the-library
Benchmarks reside in a separate repository,
`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_,
so to run the benchmarks you first need to clone this repository and
generate Makefiles with CMake::
$ git clone --recursive https://github.com/fmtlib/format-benchmark.git
$ cd format-benchmark
$ cmake .
Then you can run the speed test::
$ make speed-test
or the bloat test::
$ make bloat-test
Projects using this library Projects using this library
--------------------------- ---------------------------
@ -172,6 +277,8 @@ Projects using this library
* `Ceph <https://ceph.com/>`_: A scalable distributed storage system * `Ceph <https://ceph.com/>`_: A scalable distributed storage system
* `ccache <https://ccache.dev/>`_: A compiler cache
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater * `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
vehicle vehicle
@ -194,6 +301,8 @@ Projects using this library
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V * `FiveM <https://fivem.net/>`_: a modification framework for GTA V
* `MongoDB <https://mongodb.com/>`_: Distributed document database
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to * `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
generate randomized datasets generate randomized datasets
@ -235,7 +344,7 @@ Projects using this library
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source * `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
MMORPG framework MMORPG framework
`More... <https://github.com/search?q=cppformat&type=Code>`_ `More... <https://github.com/search?q=fmtlib&type=Code>`_
If you are aware of other projects using this library, please let me know If you are aware of other projects using this library, please let me know
by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an
@ -247,28 +356,28 @@ Motivation
So why yet another formatting library? So why yet another formatting library?
There are plenty of methods for doing this task, from standard ones like There are plenty of methods for doing this task, from standard ones like
the printf family of function and IOStreams to Boost Format library and the printf family of function and iostreams to Boost Format and FastFormat
FastFormat. The reason for creating a new library is that every existing libraries. The reason for creating a new library is that every existing
solution that I found either had serious issues or didn't provide solution that I found either had serious issues or didn't provide
all the features I needed. all the features I needed.
Printf printf
~~~~~~ ~~~~~~
The good thing about printf is that it is pretty fast and readily available The good thing about ``printf`` is that it is pretty fast and readily available
being a part of the C standard library. The main drawback is that it being a part of the C standard library. The main drawback is that it
doesn't support user-defined types. Printf also has safety issues although doesn't support user-defined types. ``printf`` also has safety issues although
they are mostly solved with `__attribute__ ((format (printf, ...)) they are somewhat mitigated with `__attribute__ ((format (printf, ...))
<http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC. <http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC.
There is a POSIX extension that adds positional arguments required for There is a POSIX extension that adds positional arguments required for
`i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_ `i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_
to printf but it is not a part of C99 and may not be available on some to ``printf`` but it is not a part of C99 and may not be available on some
platforms. platforms.
IOStreams iostreams
~~~~~~~~~ ~~~~~~~~~
The main issue with IOStreams is best illustrated with an example: The main issue with iostreams is best illustrated with an example:
.. code:: c++ .. code:: c++
@ -280,21 +389,20 @@ which is a lot of typing compared to printf:
printf("%.2f\n", 1.23456); printf("%.2f\n", 1.23456);
Matthew Wilson, the author of FastFormat, referred to this situation with Matthew Wilson, the author of FastFormat, called this "chevron hell". iostreams
IOStreams as "chevron hell". IOStreams doesn't support positional arguments don't support positional arguments by design.
by design.
The good part is that IOStreams supports user-defined types and is safe The good part is that iostreams support user-defined types and are safe although
although error reporting is awkward. error handling is awkward.
Boost Format library Boost Format
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~
This is a very powerful library which supports both printf-like format This is a very powerful library which supports both ``printf``-like format
strings and positional arguments. Its main drawback is performance. strings and positional arguments. Its main drawback is performance. According to
According to various benchmarks it is much slower than other methods various benchmarks it is much slower than other methods considered here. Boost
considered here. Boost Format also has excessive build times and severe Format also has excessive build times and severe code bloat issues (see
code bloat issues (see `Benchmarks`_). `Benchmarks`_).
FastFormat FastFormat
~~~~~~~~~~ ~~~~~~~~~~
@ -312,142 +420,16 @@ arguments. However it has significant limitations, citing its author:
It is also quite big and has a heavy dependency, STLSoft, which might be It is also quite big and has a heavy dependency, STLSoft, which might be
too restrictive for using it in some projects. too restrictive for using it in some projects.
Loki SafeFormat
~~~~~~~~~~~~~~~
SafeFormat is a formatting library which uses printf-like format strings
and is type safe. It doesn't support user-defined types or positional
arguments. It makes unconventional use of ``operator()`` for passing
format arguments.
Tinyformat
~~~~~~~~~~
This library supports printf-like format strings and is very small and
fast. Unfortunately it doesn't support positional arguments and wrapping
it in C++98 is somewhat difficult. Also its performance and code compactness
are limited by IOStreams.
Boost Spirit.Karma Boost Spirit.Karma
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
This is not really a formatting library but I decided to include it here This is not really a formatting library but I decided to include it here for
for completeness. As IOStreams it suffers from the problem of mixing completeness. As iostreams, it suffers from the problem of mixing verbatim text
verbatim text with arguments. The library is pretty fast, but slower with arguments. The library is pretty fast, but slower on integer formatting
on integer formatting than ``fmt::Writer`` on Karma's own benchmark, than ``fmt::format_int`` on Karma's own benchmark,
see `Fast integer to string conversion in C++ see `Fast integer to string conversion in C++
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_. <http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
Benchmarks
----------
Speed tests
~~~~~~~~~~~
The following speed tests results were generated by building
``tinyformat_test.cpp`` on Ubuntu GNU/Linux 14.04.1 with
``g++-4.8.2 -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of three
runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` or
equivalent is filled 2000000 times with output sent to ``/dev/null``; for
further details see the `source
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
================= ============= ===========
Library Method Run Time, s
================= ============= ===========
libc printf 1.35
libc++ std::ostream 3.42
fmt 534bff7 fmt::print 1.56
tinyformat 2.0.1 tfm::printf 3.73
Boost Format 1.54 boost::format 8.44
Folly Format folly::format 2.54
================= ============= ===========
As you can see ``boost::format`` is much slower than the alternative methods; this
is confirmed by `other tests <http://accu.org/index.php/journals/1539>`_.
Tinyformat is quite good coming close to IOStreams. Unfortunately tinyformat
cannot be faster than the IOStreams because it uses them internally.
Performance of fmt is close to that of printf, being `faster than printf on integer
formatting <http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_,
but slower on floating-point formatting which dominates this benchmark.
Compile time and code bloat
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The script `bloat-test.py
<https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py>`_
from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
tests compile time and code bloat for nontrivial projects.
It generates 100 translation units and uses ``printf()`` or its alternative
five times in each to simulate a medium sized project. The resulting
executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42),
macOS Sierra, best of three) is shown in the following tables.
**Optimized build (-O3)**
============= =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============= =============== ==================== ==================
printf 2.6 29 26
printf+string 16.4 29 26
IOStreams 31.1 59 55
fmt 19.0 37 34
tinyformat 44.0 103 97
Boost Format 91.9 226 203
Folly Format 115.7 101 88
============= =============== ==================== ==================
As you can see, fmt has 60% less overhead in terms of resulting binary code
size compared to IOStreams and comes pretty close to ``printf``. Boost Format
and Folly Format have the largest overheads.
``printf+string`` is the same as ``printf`` but with extra ``<string>``
include to measure the overhead of the latter.
**Non-optimized build**
============= =============== ==================== ==================
Method Compile Time, s Executable size, KiB Stripped size, KiB
============= =============== ==================== ==================
printf 2.2 33 30
printf+string 16.0 33 30
IOStreams 28.3 56 52
fmt 18.2 59 50
tinyformat 32.6 88 82
Boost Format 54.1 365 303
Folly Format 79.9 445 430
============= =============== ==================== ==================
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared
libraries to compare formatting function overhead only. Boost Format
and tinyformat are header-only libraries so they don't provide any
linkage options.
Running the tests
~~~~~~~~~~~~~~~~~
Please refer to `Building the library`__ for the instructions on how to build
the library and run the unit tests.
__ http://fmtlib.net/latest/usage.html#building-the-library
Benchmarks reside in a separate repository,
`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_,
so to run the benchmarks you first need to clone this repository and
generate Makefiles with CMake::
$ git clone --recursive https://github.com/fmtlib/format-benchmark.git
$ cd format-benchmark
$ cmake .
Then you can run the speed test::
$ make speed-test
or the bloat test::
$ make bloat-test
FAQ FAQ
--- ---
@ -474,11 +456,11 @@ A: use ``std::tuple``:
License License
------- -------
fmt is distributed under the BSD `license {fmt} is distributed under the MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_. <https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
The `Format String Syntax The `Format String Syntax
<http://fmtlib.net/latest/syntax.html>`_ <https://fmt.dev/latest/syntax.html>`_
section in the documentation is based on the one from Python `string module section in the documentation is based on the one from Python `string module
documentation <https://docs.python.org/3/library/string.html#module-string>`_ documentation <https://docs.python.org/3/library/string.html#module-string>`_
adapted for the current library. For this reason the documentation is adapted for the current library. For this reason the documentation is
@ -490,7 +472,7 @@ It only applies if you distribute the documentation of fmt.
Acknowledgments Acknowledgments
--------------- ---------------
The fmt library is maintained by Victor Zverovich (`vitaut The {fmt} library is maintained by Victor Zverovich (`vitaut
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan <https://github.com/vitaut>`_) and Jonathan Müller (`foonathan
<https://github.com/foonathan>`_) with contributions from many other people. <https://github.com/foonathan>`_) with contributions from many other people.
See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
@ -507,7 +489,7 @@ Some ideas used in the implementation are borrowed from `Loki
<http://clang.llvm.org/doxygen/classclang_1_1Diagnostic.html>`_ in <http://clang.llvm.org/doxygen/classclang_1_1Diagnostic.html>`_ in
`Clang <http://clang.llvm.org/>`_. `Clang <http://clang.llvm.org/>`_.
Format string syntax and the documentation are based on Python's `str.format Format string syntax and the documentation are based on Python's `str.format
<http://docs.python.org/2/library/stdtypes.html#str.format>`_. <https://docs.python.org/3/library/stdtypes.html#str.format>`_.
Thanks `Doug Turnbull <https://github.com/softwaredoug>`_ for his valuable Thanks `Doug Turnbull <https://github.com/softwaredoug>`_ for his valuable
comments and contribution to the design of the type-safe API and comments and contribution to the design of the type-safe API and
`Gregory Czajkowski <https://github.com/gcflymoto>`_ for implementing binary `Gregory Czajkowski <https://github.com/gcflymoto>`_ for implementing binary

File diff suppressed because it is too large Load Diff

View File

@ -12,184 +12,149 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
#ifdef FMT_DEPRECATED_COLORS
// color and (v)print_colored are deprecated.
enum color { black, red, green, yellow, blue, magenta, cyan, white };
FMT_API void vprint_colored(color c, string_view format, format_args args);
FMT_API void vprint_colored(color c, wstring_view format, wformat_args args);
template <typename... Args>
inline void print_colored(color c, string_view format_str,
const Args & ... args) {
vprint_colored(c, format_str, make_format_args(args...));
}
template <typename... Args>
inline void print_colored(color c, wstring_view format_str,
const Args & ... args) {
vprint_colored(c, format_str, make_format_args<wformat_context>(args...));
}
inline void vprint_colored(color c, string_view format, format_args args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
vprint(format, args);
std::fputs(internal::data::RESET_COLOR, stdout);
}
inline void vprint_colored(color c, wstring_view format, wformat_args args) {
wchar_t escape[] = L"\x1b[30m";
escape[3] = static_cast<wchar_t>('0' + c);
std::fputws(escape, stdout);
vprint(format, args);
std::fputws(internal::data::WRESET_COLOR, stdout);
}
#else
enum class color : uint32_t { enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255) alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215) antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255) aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212) aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255) azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220) beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196) bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0) black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205) blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255) blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226) blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42) brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135) burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160) cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0) chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30) chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80) coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237) cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220) cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60) crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255) cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139) dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139) dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11) dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169) dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0) dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107) dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139) dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47) dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0) dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204) dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0) dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122) dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143) dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139) dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209) dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211) dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147) deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255) deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105) dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255) dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34) fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240) floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34) forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255) fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220) gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255) ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0) gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32) golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128) gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0) green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47) green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240) honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180) hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92) indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130) indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240) ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140) khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250) lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245) lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0) lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205) lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230) light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128) light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255) light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211) light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144) light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193) light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122) light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170) light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250) light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153) light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222) light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224) light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0) lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50) lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230) linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255) magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0) maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170) medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205) medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211) medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219) medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113) medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238) medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154) medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204) medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133) medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112) midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250) mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225) misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181) moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173) navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128) navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230) old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0) olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35) olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0) orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0) orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214) orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152) pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238) pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147) pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213) papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185) peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63) peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203) pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221) plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230) powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128) purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153) rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0) red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143) rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225) royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19) saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114) salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96) sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87) sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238) sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45) sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192) silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235) sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205) slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144) slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250) snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127) spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180) steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140) tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128) teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216) thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71) tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208) turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238) violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179) wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255) white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245) white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0) yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32 // rgb(154,205,50) yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color }; // enum class color
enum class terminal_color : uint8_t { enum class terminal_color : uint8_t {
black = 30, black = 30,
@ -208,27 +173,26 @@ enum class terminal_color : uint8_t {
bright_magenta, bright_magenta,
bright_cyan, bright_cyan,
bright_white bright_white
}; // enum class terminal_color };
enum class emphasis : uint8_t { enum class emphasis : uint8_t {
bold = 1, bold = 1,
italic = 1 << 1, italic = 1 << 1,
underline = 1 << 2, underline = 1 << 2,
strikethrough = 1 << 3 strikethrough = 1 << 3
}; // enum class emphasis };
// rgb is a struct for red, green and blue colors. // rgb is a struct for red, green and blue colors.
// We use rgb as name because some editors will show it as color direct in the // Using the name "rgb" makes some editors show the color in a tooltip.
// editor.
struct rgb { struct rgb {
FMT_CONSTEXPR_DECL rgb() : r(0), g(0), b(0) {} FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_) FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
: r(r_), g(g_), b(b_) {} FMT_CONSTEXPR rgb(uint32_t hex)
FMT_CONSTEXPR_DECL rgb(uint32_t hex) : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex) & 0xFF) {} FMT_CONSTEXPR rgb(color hex)
FMT_CONSTEXPR_DECL rgb(color hex) : r((uint32_t(hex) >> 16) & 0xFF),
: r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {} b(uint32_t(hex) & 0xFF) {}
uint8_t r; uint8_t r;
uint8_t g; uint8_t g;
uint8_t b; uint8_t b;
@ -238,19 +202,17 @@ namespace internal {
// color is a struct of either a rgb color or a terminal color. // color is a struct of either a rgb color or a terminal color.
struct color_type { struct color_type {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
: is_rgb(), value{} {} FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT value{} {
: is_rgb(true), value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color); value.rgb_color = static_cast<uint32_t>(rgb_color);
} }
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
: is_rgb(true), value{} { value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
| (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
} }
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
: is_rgb(), value{} { value{} {
value.term_color = static_cast<uint8_t>(term_color); value.term_color = static_cast<uint8_t>(term_color);
} }
bool is_rgb; bool is_rgb;
@ -259,21 +221,23 @@ struct color_type {
uint32_t rgb_color; uint32_t rgb_color;
} value; } value;
}; };
} // namespace internal } // namespace internal
// Experimental text formatting support. // Experimental text formatting support.
class text_style { class text_style {
public: public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
: set_foreground_color(), set_background_color(), ems(em) {} : set_foreground_color(),
set_background_color(),
ems(em) {}
FMT_CONSTEXPR text_style &operator|=(const text_style &rhs) { FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
if (!set_foreground_color) { if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color; set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color; foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) { } else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
throw format_error("can't OR a terminal color"); FMT_THROW(format_error("can't OR a terminal color"));
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
} }
@ -282,7 +246,7 @@ class text_style {
background_color = rhs.background_color; background_color = rhs.background_color;
} else if (rhs.set_background_color) { } else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb) if (!background_color.is_rgb || !rhs.background_color.is_rgb)
throw format_error("can't OR a terminal color"); FMT_THROW(format_error("can't OR a terminal color"));
background_color.value.rgb_color |= rhs.background_color.value.rgb_color; background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
} }
@ -291,18 +255,18 @@ class text_style {
return *this; return *this;
} }
friend FMT_CONSTEXPR friend FMT_CONSTEXPR text_style operator|(text_style lhs,
text_style operator|(text_style lhs, const text_style &rhs) { const text_style& rhs) {
return lhs |= rhs; return lhs |= rhs;
} }
FMT_CONSTEXPR text_style &operator&=(const text_style &rhs) { FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
if (!set_foreground_color) { if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color; set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color; foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) { } else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
throw format_error("can't AND a terminal color"); FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
} }
@ -311,7 +275,7 @@ class text_style {
background_color = rhs.background_color; background_color = rhs.background_color;
} else if (rhs.set_background_color) { } else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb) if (!background_color.is_rgb || !rhs.background_color.is_rgb)
throw format_error("can't AND a terminal color"); FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color; background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
} }
@ -320,8 +284,8 @@ class text_style {
return *this; return *this;
} }
friend FMT_CONSTEXPR friend FMT_CONSTEXPR text_style operator&(text_style lhs,
text_style operator&(text_style lhs, const text_style &rhs) { const text_style& rhs) {
return lhs &= rhs; return lhs &= rhs;
} }
@ -335,32 +299,32 @@ class text_style {
return static_cast<uint8_t>(ems) != 0; return static_cast<uint8_t>(ems) != 0;
} }
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT { FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
assert(has_foreground() && "no foreground specified for this style"); FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color; return foreground_color;
} }
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT { FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
assert(has_background() && "no background specified for this style"); FMT_ASSERT(has_background(), "no background specified for this style");
return background_color; return background_color;
} }
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT { FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
assert(has_emphasis() && "no emphasis specified for this style"); FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems; return ems;
} }
private: private:
FMT_CONSTEXPR text_style(bool is_foreground, FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT internal::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(), : set_foreground_color(),
set_background_color(), set_background_color(),
ems() { ems() {
if (is_foreground) { if (is_foreground) {
foreground_color = text_color; foreground_color = text_color;
set_foreground_color = true; set_foreground_color = true;
} else { } else {
background_color = text_color; background_color = text_color;
set_background_color = true; set_background_color = true;
} }
} }
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground) friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
FMT_NOEXCEPT; FMT_NOEXCEPT;
@ -388,19 +352,17 @@ FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
namespace internal { namespace internal {
template <typename Char> template <typename Char> struct ansi_color_escape {
struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color, FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
const char * esc) FMT_NOEXCEPT { const char* esc) FMT_NOEXCEPT {
// If we have a terminal color, we need to output another escape code // If we have a terminal color, we need to output another escape code
// sequence. // sequence.
if (!text_color.is_rgb) { if (!text_color.is_rgb) {
bool is_background = esc == internal::data::BACKGROUND_COLOR; bool is_background = esc == internal::data::background_color;
uint32_t value = text_color.value.term_color; uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with // Background ASCII codes are the same as the foreground ones but with
// 10 more. // 10 more.
if (is_background) if (is_background) value += 10u;
value += 10u;
std::size_t index = 0; std::size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
@ -422,7 +384,7 @@ struct ansi_color_escape {
buffer[i] = static_cast<Char>(esc[i]); buffer[i] = static_cast<Char>(esc[i]);
} }
rgb color(text_color.value.rgb_color); rgb color(text_color.value.rgb_color);
to_esc(color.r, buffer + 7, ';'); to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';'); to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm'); to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0); buffer[19] = static_cast<Char>(0);
@ -430,19 +392,15 @@ struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
uint8_t em_codes[4] = {}; uint8_t em_codes[4] = {};
uint8_t em_bits = static_cast<uint8_t>(em); uint8_t em_bits = static_cast<uint8_t>(em);
if (em_bits & static_cast<uint8_t>(emphasis::bold)) if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
em_codes[0] = 1; if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::italic)) if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
em_codes[1] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::underline))
em_codes[2] = 4;
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough)) if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9; em_codes[3] = 9;
std::size_t index = 0; std::size_t index = 0;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) if (!em_codes[i]) continue;
continue;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('['); buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]); buffer[index++] = static_cast<Char>('0' + em_codes[i]);
@ -450,12 +408,17 @@ struct ansi_color_escape {
} }
buffer[index++] = static_cast<Char>(0); buffer[index++] = static_cast<Char>(0);
} }
FMT_CONSTEXPR operator const Char *() const FMT_NOEXCEPT { return buffer; } FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
private: FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
return buffer + std::strlen(buffer);
}
private:
Char buffer[7u + 3u * 4u + 1u]; Char buffer[7u + 3u * 4u + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char *out, static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_NOEXCEPT { char delimiter) FMT_NOEXCEPT {
out[0] = static_cast<Char>('0' + c / 100); out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10); out[1] = static_cast<Char>('0' + c / 10 % 10);
@ -465,78 +428,84 @@ private:
}; };
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT { internal::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR); return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
make_background_color(internal::color_type background) FMT_NOEXCEPT { internal::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR); return ansi_color_escape<Char>(background, internal::data::background_color);
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
make_emphasis(emphasis em) FMT_NOEXCEPT {
return ansi_color_escape<Char>(em); return ansi_color_escape<Char>(em);
} }
template <typename Char> template <typename Char>
inline void fputs(const Char *chars, FILE *stream) FMT_NOEXCEPT { inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
std::fputs(chars, stream); std::fputs(chars, stream);
} }
template <> template <>
inline void fputs<wchar_t>(const wchar_t *chars, FILE *stream) FMT_NOEXCEPT { inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
std::fputws(chars, stream); std::fputws(chars, stream);
} }
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::reset_color, stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::wreset_color, stream);
}
template <typename Char> template <typename Char>
inline void reset_color(FILE *stream) FMT_NOEXCEPT { inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
fputs(internal::data::RESET_COLOR, stream); const char* begin = data::reset_color;
const char* end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end);
} }
template <> template <typename Char>
inline void reset_color<wchar_t>(FILE *stream) FMT_NOEXCEPT { void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
fputs(internal::data::WRESET_COLOR, stream); basic_string_view<Char> format_str,
} basic_format_args<buffer_context<Char>> args) {
// The following specialiazation disables using std::FILE as a character type,
// which is needed because or else
// fmt::print(stderr, fmt::emphasis::bold, "");
// would take stderr (a std::FILE *) as the format string.
template <>
struct is_string<std::FILE *> : std::false_type {};
template <>
struct is_string<const std::FILE *> : std::false_type {};
} // namespace internal
template <
typename S, typename Char = typename internal::char_t<S>::type>
void vprint(std::FILE *f, const text_style &ts, const S &format,
basic_format_args<typename buffer_context<Char>::type> args) {
bool has_style = false; bool has_style = false;
if (ts.has_emphasis()) { if (ts.has_emphasis()) {
has_style = true; has_style = true;
internal::fputs<Char>( auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
internal::make_emphasis<Char>(ts.get_emphasis()), f); buf.append(emphasis.begin(), emphasis.end());
} }
if (ts.has_foreground()) { if (ts.has_foreground()) {
has_style = true; has_style = true;
internal::fputs<Char>( auto foreground =
internal::make_foreground_color<Char>(ts.get_foreground()), f); internal::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
} }
if (ts.has_background()) { if (ts.has_background()) {
has_style = true; has_style = true;
internal::fputs<Char>( auto background =
internal::make_background_color<Char>(ts.get_background()), f); internal::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
} }
vprint(f, format, args); vformat_to(buf, format_str, args);
if (has_style) { if (has_style) {
internal::reset_color<Char>(f); internal::reset_color<Char>(buf);
} }
} }
} // namespace internal
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);
buf.push_back(Char(0));
internal::fputs(buf.data(), f);
}
/** /**
Formats a string and prints it to the specified file stream using ANSI Formats a string and prints it to the specified file stream using ANSI
@ -545,15 +514,14 @@ void vprint(std::FILE *f, const text_style &ts, const S &format,
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23); "Elapsed time: {0:.2f} seconds", 1.23);
*/ */
template <typename String, typename... Args> template <typename S, typename... Args,
typename std::enable_if<internal::is_string<String>::value>::type print( FMT_ENABLE_IF(internal::is_string<S>::value)>
std::FILE *f, const text_style &ts, const String &format_str, void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args &... args) { const Args&... args) {
internal::check_format_string<Args...>(format_str); internal::check_format_string<Args...>(format_str);
typedef typename internal::char_t<String>::type char_t; using context = buffer_context<char_t<S>>;
typedef typename buffer_context<char_t>::type context_t; format_arg_store<context, Args...> as{args...};
format_arg_store<context_t, Args...> as{args...}; vprint(f, ts, format_str, basic_format_args<context>(as));
vprint(f, ts, format_str, basic_format_args<context_t>(as));
} }
/** /**
@ -563,14 +531,39 @@ typename std::enable_if<internal::is_string<String>::value>::type print(
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23); "Elapsed time: {0:.2f} seconds", 1.23);
*/ */
template <typename String, typename... Args> template <typename S, typename... Args,
typename std::enable_if<internal::is_string<String>::value>::type print( FMT_ENABLE_IF(internal::is_string<S>::value)>
const text_style &ts, const String &format_str, void print(const text_style& ts, const S& format_str, const Args&... args) {
const Args &... args) {
return print(stdout, ts, format_str, args...); return print(stdout, ts, format_str, args...);
} }
#endif template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
}
/**
\rst
Formats arguments and returns the result as a string using ANSI
escape sequences to specify text formatting.
**Example**::
#include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42);
\endrst
*/
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...)});
}
FMT_END_NAMESPACE FMT_END_NAMESPACE

585
Externals/fmt/include/fmt/compile.h vendored Normal file
View File

@ -0,0 +1,585 @@
// Formatting library for C++ - experimental format string compilation
//
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#include <vector>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template <typename Char> struct format_part {
enum class kind { arg_index, arg_name, text, replacement };
struct replacement {
arg_ref<Char> arg_id;
dynamic_format_specs<Char> specs;
};
kind part_kind;
union value {
int arg_index;
basic_string_view<Char> str;
replacement repl;
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
FMT_CONSTEXPR value(replacement r) : repl(r) {}
} val;
// Position past the end of the argument id.
const Char* arg_id_end = nullptr;
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {}
static FMT_CONSTEXPR format_part make_arg_index(int index) {
return format_part(kind::arg_index, index);
}
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
return format_part(kind::arg_name, name);
}
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
return format_part(kind::text, text);
}
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
return format_part(kind::replacement, repl);
}
};
template <typename Char> struct part_counter {
unsigned num_parts = 0;
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
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 void on_replacement_field(const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned brace_counter = 0;
for (; begin != end; ++begin) {
if (*begin == '{') {
++brace_counter;
} else if (*begin == '}') {
if (brace_counter == 0u) break;
--brace_counter;
}
}
return begin;
}
FMT_CONSTEXPR void on_error(const char*) {}
};
// Counts the number of parts in a format string.
template <typename Char>
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
part_counter<Char> counter;
parse_format_string<true>(format_str, counter);
return counter.num_parts;
}
template <typename Char, typename PartHandler>
class format_string_compiler : public error_handler {
private:
using part = format_part<Char>;
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_format_parse_context<Char> parse_context_;
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
PartHandler handler)
: handler_(handler),
format_str_(format_str),
parse_context_(format_str) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end)
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
FMT_CONSTEXPR void on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id());
}
FMT_CONSTEXPR void on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
}
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
? arg_ref<Char>(part_.val.arg_index)
: arg_ref<Char>(part_.val.str);
auto part = part::make_replacement(repl);
part.arg_id_end = begin;
handler_(part);
return it;
}
};
// Compiles a format string and invokes handler(part) for each parsed part.
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
PartHandler handler) {
parse_format_string<IS_CONSTEXPR>(
format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
template <typename Range, typename Context, typename Id>
void format_arg(
basic_format_parse_context<typename Range::value_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(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 {
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);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
using format_part_t = format_part<char_type>;
switch (part.part_kind) {
case format_part_t::kind::text: {
const auto text = value.str;
auto output = ctx.out();
auto&& it = reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
break;
}
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);
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);
break;
case format_part_t::kind::replacement: {
const auto& arg_id_value = value.repl.arg_id.val;
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.name);
auto specs = value.repl.specs;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision,
specs.precision_ref, ctx);
error_handler h;
numeric_specs_checker<error_handler> checker(h, arg.type());
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
break;
}
}
}
return ctx.out();
}
} // namespace cf
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>>;
parts_container compiled_parts;
explicit compiled_format_base(basic_string_view<char_type> format_str) {
compile_format_string<false>(format_str,
[this](const format_part<char_type>& part) {
compiled_parts.push_back(part);
});
}
const parts_container& parts() const { return compiled_parts; }
};
template <typename Char, unsigned N> struct format_part_array {
format_part<Char> data[N] = {};
FMT_CONSTEXPR format_part_array() = default;
};
template <typename Char, unsigned N>
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
basic_string_view<Char> format_str) {
format_part_array<Char, N> parts;
unsigned counter = 0;
// This is not a lambda for compatibility with older compilers.
struct {
format_part<Char>* parts;
unsigned* counter;
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
parts[(*counter)++] = part;
}
} collector{parts.data, &counter};
compile_format_string<true>(format_str, collector);
if (counter < N) {
parts.data[counter] =
format_part<Char>::make_text(basic_string_view<Char>());
}
return parts;
}
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template <typename S>
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
: basic_compiled_format {
using char_type = char_t<S>;
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
// Workaround for old compilers. Format string compilation will not be
// performed there anyway.
#if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
constexpr_max(count_parts(to_string_view(S())), 1u);
#else
static const unsigned num_format_parts = 1;
#endif
using parts_container = format_part<char_type>[num_format_parts];
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()));
return compiled_parts.data;
}
};
template <typename S, typename... Args>
class compiled_format : private compiled_format_base<S> {
public:
using typename compiled_format_base<S>::char_type;
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename Range, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
public:
compiled_format() = delete;
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
: compiled_format_base<S>(format_str), format_str_(format_str) {}
};
#ifdef __cpp_if_constexpr
template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get(const T& first, const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
else
return get<N - 1>(rest...);
}
template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
};
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
// TODO: reserve
return copy_str<Char>(data.begin(), data.end(), out);
}
};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
return {{&s[pos], size}};
}
template <typename Char, typename OutputIt, typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0>
OutputIt format_default(OutputIt out, T value) {
// TODO: reserve
format_int fi(value);
return std::copy(fi.data(), fi.data() + fi.size(), out);
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) {
writer w(out);
w.write(value);
return w.out();
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, Char value) {
*out++ = value;
return out;
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, const Char* value) {
auto length = std::char_traits<Char>::length(value);
return copy_str<Char>(value, value + length, out);
}
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
return format_default<Char>(out, arg);
}
};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS != to_string_view(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
else
return make_concat(head, tail);
} else {
return head;
}
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) {
using char_type = typename S::char_type;
constexpr basic_string_view<char_type> str = format_str;
if constexpr (str[POS] == '{') {
if (POS + 1 == str.size())
throw format_error("unmatched '{' in format string");
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') {
using type = get_type<ID, Args>;
if constexpr (std::is_same<type, int>::value) {
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
format_str);
} else {
return unknown_format();
}
} else {
return unknown_format();
}
} else if constexpr (str[POS] == '}') {
if (POS + 1 == str.size())
throw format_error("unmatched '}' in format string");
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else {
constexpr auto end = parse_text(str, POS + 1);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
}
}
#endif // __cpp_if_constexpr
} // namespace internal
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
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);
} else {
constexpr auto result =
internal::compile_format_string<internal::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));
} else {
return result;
}
}
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_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...);
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# 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));
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
// 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...>(
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,
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,
{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,
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, {make_format_args<context>(args...)});
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::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...);
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();
}
FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,65 +8,65 @@
#ifndef FMT_LOCALE_H_ #ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_ #define FMT_LOCALE_H_
#include "format.h"
#include <locale> #include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
template <typename Char> template <typename Char>
typename buffer_context<Char>::type::iterator vformat_to( typename buffer_context<Char>::iterator vformat_to(
const std::locale &loc, basic_buffer<Char> &buf, const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<buffer_context<Char>> args) {
typedef back_insert_range<basic_buffer<Char> > range; using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>( return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
buf, to_string_view(format_str), args, internal::locale_ref(loc)); internal::locale_ref(loc));
} }
template <typename Char> template <typename Char>
std::basic_string<Char> vformat( std::basic_string<Char> vformat(const std::locale& loc,
const std::locale &loc, basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args); internal::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer); return fmt::to_string(buffer);
} }
} } // namespace internal
template <typename S, typename Char = FMT_CHAR(S)> template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat( inline std::basic_string<Char> vformat(
const std::locale &loc, const S &format_str, const std::locale& loc, const S& format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<buffer_context<Char>> args) {
return internal::vformat(loc, to_string_view(format_str), args); return internal::vformat(loc, to_string_view(format_str), args);
} }
template <typename S, typename... Args> template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<FMT_CHAR(S)> format( inline std::basic_string<Char> format(const std::locale& loc,
const std::locale &loc, const S &format_str, const Args &... args) { const S& format_str, Args&&... args) {
return internal::vformat( return internal::vformat(
loc, to_string_view(format_str), loc, to_string_view(format_str),
*internal::checked_args<S, Args...>(format_str, args...)); {internal::make_args_checked<Args...>(format_str, args...)});
} }
template <typename String, typename OutputIt, typename... Args> template <typename S, typename OutputIt, typename... Args,
inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value, typename Char = enable_if_t<
OutputIt>::type internal::is_output_iterator<OutputIt>::value, char_t<S>>>
vformat_to(OutputIt out, const std::locale &loc, const String &format_str, inline OutputIt vformat_to(OutputIt out, const std::locale& loc,
typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) { const S& format_str,
typedef output_range<OutputIt, FMT_CHAR(String)> range; format_args_t<OutputIt, Char> args) {
using range = internal::output_range<OutputIt, Char>;
return vformat_to<arg_formatter<range>>( return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc)); range(out), to_string_view(format_str), args, internal::locale_ref(loc));
} }
template <typename OutputIt, typename S, typename... Args> template <typename OutputIt, typename S, typename... Args,
inline typename std::enable_if< FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value && internal::is_string<S>::value)>
internal::is_output_iterator<OutputIt>::value, OutputIt>::type inline OutputIt format_to(OutputIt out, const std::locale& loc,
format_to(OutputIt out, const std::locale &loc, const S &format_str, const S& format_str, Args&&... args) {
const Args &... args) {
internal::check_format_string<Args...>(format_str); internal::check_format_string<Args...>(format_str);
typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context; using context = format_context_t<OutputIt, char_t<S>>;
format_arg_store<context, Args...> as{args...}; format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str), return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as)); basic_format_args<context>(as));

View File

@ -8,22 +8,21 @@
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
#include "format.h"
#include <ostream> #include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
template <class Char> template <class Char> class formatbuf : public std::basic_streambuf<Char> {
class formatbuf : public std::basic_streambuf<Char> {
private: private:
typedef typename std::basic_streambuf<Char>::int_type int_type; using int_type = typename std::basic_streambuf<Char>::int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type; using traits_type = typename std::basic_streambuf<Char>::traits_type;
basic_buffer<Char> &buffer_; buffer<Char>& buffer_;
public: public:
formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {} formatbuf(buffer<Char>& buf) : buffer_(buf) {}
protected: protected:
// The put-area is actually always empty. This makes the implementation // The put-area is actually always empty. This makes the implementation
@ -39,33 +38,36 @@ class formatbuf : public std::basic_streambuf<Char> {
return ch; return ch;
} }
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE { std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count); buffer_.append(s, s + count);
return count; return count;
} }
}; };
template <typename Char> template <typename Char> struct test_stream : std::basic_ostream<Char> {
struct test_stream : std::basic_ostream<Char> {
private: private:
struct null;
// Hide all operator<< from std::basic_ostream<Char>. // Hide all operator<< from std::basic_ostream<Char>.
void operator<<(null); void_t<> operator<<(null<>);
void_t<> operator<<(const Char*);
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
!std::is_enum<T>::value)>
void_t<> operator<<(T);
}; };
// Checks if T has a user-defined operator<< (e.g. not a member of std::ostream). // Checks if T has a user-defined operator<< (e.g. not a member of
template <typename T, typename Char> // std::ostream).
class is_streamable { template <typename T, typename Char> class is_streamable {
private: private:
template <typename U> template <typename U>
static decltype( static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
internal::declval<test_stream<Char>&>() << std::declval<U>()),
<< internal::declval<U>(), std::true_type()) test(int); void_t<>>::value>
test(int);
template <typename> template <typename> static std::false_type test(...);
static std::false_type test(...);
typedef decltype(test<T>(0)) result; using result = decltype(test<T>(0));
public: public:
static const bool value = result::value; static const bool value = result::value;
@ -73,65 +75,52 @@ class is_streamable {
// Write the content of buf to os. // Write the content of buf to os.
template <typename Char> template <typename Char>
void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) { void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char *data = buf.data(); const Char* buf_data = buf.data();
typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize; using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
UnsignedStreamSize size = buf.size(); unsigned_streamsize size = buf.size();
UnsignedStreamSize max_size = unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
do { do {
UnsignedStreamSize n = size <= max_size ? size : max_size; unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(data, static_cast<std::streamsize>(n)); os.write(buf_data, static_cast<std::streamsize>(n));
data += n; buf_data += n;
size -= n; size -= n;
} while (size != 0); } while (size != 0);
} }
template <typename Char, typename T> template <typename Char, typename T>
void format_value(basic_buffer<Char> &buffer, const T &value) { void format_value(buffer<Char>& buf, const T& value,
internal::formatbuf<Char> format_buf(buffer); locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf); std::basic_ostream<Char> output(&format_buf);
if (loc) output.imbue(loc.get<std::locale>());
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value; output << value;
buffer.resize(buffer.size()); buf.resize(buf.size());
} }
} // namespace internal
// Disable conversion to int if T has an overloaded operator<< which is a free
// function (not a member of std::ostream).
template <typename T, typename Char>
struct convert_to_int<T, Char, void> {
static const bool value =
convert_to_int<T, Char, int>::value &&
!internal::is_streamable<T, Char>::value;
};
// Formats an object of type T that has an overloaded ostream operator<<. // Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<T, Char, struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
typename std::enable_if<
internal::is_streamable<T, Char>::value &&
!internal::format_type<
typename buffer_context<Char>::type, T>::value>::type>
: formatter<basic_string_view<Char>, Char> { : formatter<basic_string_view<Char>, Char> {
template <typename Context> template <typename Context>
auto format(const T &value, Context &ctx) -> decltype(ctx.out()) { auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::format_value(buffer, value); format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size()); basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx); return formatter<basic_string_view<Char>, Char>::format(str, ctx);
} }
}; };
} // namespace internal
template <typename Char> template <typename Char>
inline void vprint(std::basic_ostream<Char> &os, void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_string_view<Char> format_str, basic_format_args<buffer_context<Char>> args) {
basic_format_args<typename buffer_context<Char>::type> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args); internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer); internal::write(os, buffer);
} }
/** /**
\rst \rst
Prints formatted data to the stream *os*. Prints formatted data to the stream *os*.
@ -141,12 +130,11 @@ inline void vprint(std::basic_ostream<Char> &os,
fmt::print(cerr, "Don't {}!", "panic"); fmt::print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
template <typename S, typename... Args> template <typename S, typename... Args,
inline typename std::enable_if<internal::is_string<S>::value>::type typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
print(std::basic_ostream<FMT_CHAR(S)> &os, const S &format_str, void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
const Args & ... args) { vprint(os, to_string_view(format_str),
internal::checked_args<S, Args...> ca(format_str, args...); {internal::make_args_checked<Args...>(format_str, args...)});
vprint(os, to_string_view(format_str), *ca);
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -10,54 +10,65 @@
#if defined(__MINGW32__) || defined(__CYGWIN__) #if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. // Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__ # undef __STRICT_ANSI__
#endif #endif
#include <errno.h> #include <cerrno>
#include <fcntl.h> // for O_RDONLY #include <clocale> // for locale_t
#include <locale.h> // for locale_t #include <cstdio>
#include <stdio.h> #include <cstdlib> // for strtod_l
#include <stdlib.h> // for strtod_l
#include <cstddef> #include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__) #if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X # include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif #endif
#include "format.h" #include "format.h"
// UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
#else
# define FMT_USE_FCNTL 0
#endif
#ifndef FMT_POSIX #ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__) # if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols. // Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call # define FMT_POSIX(call) _##call
# else # else
# define FMT_POSIX(call) call # define FMT_POSIX(call) call
# endif # endif
#endif #endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability. // Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM #ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call) # define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else #else
# define FMT_SYSTEM(call) call # define FMT_SYSTEM(call) call
# ifdef _WIN32 # ifdef _WIN32
// Fix warnings about deprecated symbols. // Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call # define FMT_POSIX_CALL(call) ::_##call
# else # else
# define FMT_POSIX_CALL(call) ::call # define FMT_POSIX_CALL(call) ::call
# endif # endif
#endif #endif
// Retries the expression while it evaluates to error_result and errno // Retries the expression while it evaluates to error_result and errno
// equals to EINTR. // equals to EINTR.
#ifndef _WIN32 #ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \ # define FMT_RETRY_VAL(result, expression, error_result) \
do { \ do { \
result = (expression); \ (result) = (expression); \
} while (result == error_result && errno == EINTR) } while ((result) == (error_result) && errno == EINTR)
#else #else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression) # define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif #endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
@ -69,7 +80,7 @@ FMT_BEGIN_NAMESPACE
A reference to a null-terminated string. It can be constructed from a C A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``. string or ``std::string``.
You can use one of the following typedefs for common character types: You can use one of the following type aliases for common character types:
+---------------+-----------------------------+ +---------------+-----------------------------+
| Type | Definition | | Type | Definition |
@ -89,28 +100,27 @@ FMT_BEGIN_NAMESPACE
format(std::string("{}"), 42); format(std::string("{}"), 42);
\endrst \endrst
*/ */
template <typename Char> template <typename Char> class basic_cstring_view {
class basic_cstring_view {
private: private:
const Char *data_; const Char* data_;
public: public:
/** Constructs a string reference object from a C string. */ /** Constructs a string reference object from a C string. */
basic_cstring_view(const Char *s) : data_(s) {} basic_cstring_view(const Char* s) : data_(s) {}
/** /**
\rst \rst
Constructs a string reference from an ``std::string`` object. Constructs a string reference from an ``std::string`` object.
\endrst \endrst
*/ */
basic_cstring_view(const std::basic_string<Char> &s) : data_(s.c_str()) {} basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */ /** Returns the pointer to a C string. */
const Char *c_str() const { return data_; } const Char* c_str() const { return data_; }
}; };
typedef basic_cstring_view<char> cstring_view; using cstring_view = basic_cstring_view<char>;
typedef basic_cstring_view<wchar_t> wcstring_view; using wcstring_view = basic_cstring_view<wchar_t>;
// An error code. // An error code.
class error_code { class error_code {
@ -126,33 +136,31 @@ class error_code {
// A buffered file. // A buffered file.
class buffered_file { class buffered_file {
private: private:
FILE *file_; FILE* file_;
friend class file; friend class file;
explicit buffered_file(FILE *f) : file_(f) {} explicit buffered_file(FILE* f) : file_(f) {}
public: public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file. // Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {} buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT; FMT_API ~buffered_file() FMT_NOEXCEPT;
private:
buffered_file(const buffered_file &) = delete;
void operator=(const buffered_file &) = delete;
public: public:
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) { buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = FMT_NULL; other.file_ = nullptr;
} }
buffered_file& operator=(buffered_file &&other) { buffered_file& operator=(buffered_file&& other) {
close(); close();
file_ = other.file_; file_ = other.file_;
other.file_ = FMT_NULL; other.file_ = nullptr;
return *this; return *this;
} }
@ -163,22 +171,23 @@ class buffered_file {
FMT_API void close(); FMT_API void close();
// Returns the pointer to a FILE object representing this file. // Returns the pointer to a FILE object representing this file.
FILE *get() const FMT_NOEXCEPT { return file_; } FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions // We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro. // of MinGW that define fileno as a macro.
FMT_API int (fileno)() const; FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) { void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args); fmt::vprint(file_, format_str, args);
} }
template <typename... Args> template <typename... Args>
inline void print(string_view format_str, const Args & ... args) { inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...)); vprint(format_str, make_format_args(args...));
} }
}; };
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1. // A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw // Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as // fmt::system_error in case of failure. Note that some errors such as
@ -195,9 +204,9 @@ class file {
public: public:
// Possible values for the oflag argument to the constructor. // Possible values for the oflag argument to the constructor.
enum { enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
}; };
// Constructs a file object which doesn't represent any file. // Constructs a file object which doesn't represent any file.
@ -206,16 +215,13 @@ class file {
// Opens a file and constructs a file object representing this file. // Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag); FMT_API file(cstring_view path, int oflag);
private:
file(const file &) = delete;
void operator=(const file &) = delete;
public: public:
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) { file(const file&) = delete;
other.fd_ = -1; void operator=(const file&) = delete;
}
file& operator=(file &&other) { file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) FMT_NOEXCEPT {
close(); close();
fd_ = other.fd_; fd_ = other.fd_;
other.fd_ = -1; other.fd_ = -1;
@ -236,10 +242,10 @@ class file {
FMT_API long long size() const; FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer. // 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 std::size_t read(void* buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file. // 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 std::size_t write(const void* buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns // Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object. // the duplicate as a file object.
@ -251,68 +257,59 @@ class file {
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT; FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading // Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively. // and writing respectively.
FMT_API static void pipe(file &read_end, file &write_end); FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches // Creates a buffered_file object associated with this file and detaches
// this file object from the file. // this file object from the file.
FMT_API buffered_file fdopen(const char *mode); FMT_API buffered_file fdopen(const char* mode);
}; };
// Returns the memory page size. // Returns the memory page size.
long getpagesize(); long getpagesize();
#endif // FMT_USE_FCNTL
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
!defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) && \
!defined(__NEWLIB_H__)
# define FMT_LOCALE
#endif
#ifdef FMT_LOCALE #ifdef FMT_LOCALE
// A "C" numeric locale. // A "C" numeric locale.
class Locale { class Locale {
private: private:
# ifdef _MSC_VER # ifdef _WIN32
typedef _locale_t locale_t; using locale_t = _locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC }; enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char *locale, locale_t) { static locale_t newlocale(int category_mask, const char* locale, locale_t) {
return _create_locale(category_mask, locale); return _create_locale(category_mask, locale);
} }
static void freelocale(locale_t locale) { static void freelocale(locale_t locale) { _free_locale(locale); }
_free_locale(locale);
}
static double strtod_l(const char *nptr, char **endptr, _locale_t locale) { static double strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return _strtod_l(nptr, endptr, locale); return _strtod_l(nptr, endptr, locale);
} }
# endif # endif
locale_t locale_; locale_t locale_;
Locale(const Locale &) = delete;
void operator=(const Locale &) = delete;
public: public:
typedef locale_t Type; using type = locale_t;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) { Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
if (!locale_) if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
FMT_THROW(system_error(errno, "cannot create locale"));
} }
~Locale() { freelocale(locale_); } ~Locale() { freelocale(locale_); }
Type get() const { return locale_; } type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end // Converts string to floating-point number and advances str past the end
// of the parsed input. // of the parsed input.
double strtod(const char *&str) const { double strtod(const char*& str) const {
char *end = FMT_NULL; char* end = nullptr;
double result = strtod_l(str, &end, locale_); double result = strtod_l(str, &end, locale_);
str = end; str = end;
return result; return result;

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Formatting library for C++ - the core API // Formatting library for C++ - experimental range support
// //
// Copyright (c) 2012 - present, Victor Zverovich // Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved. // All rights reserved.
@ -12,20 +12,19 @@
#ifndef FMT_RANGES_H_ #ifndef FMT_RANGES_H_
#define FMT_RANGES_H_ #define FMT_RANGES_H_
#include "format.h"
#include <type_traits> #include <type_traits>
#include "format.h"
// output only up to N items from the range. // output only up to N items from the range.
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT #ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256 # define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
#endif #endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename Char> template <typename Char> struct formatting_base {
struct formatting_base {
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return ctx.begin();
} }
}; };
@ -33,7 +32,8 @@ struct formatting_base {
template <typename Char, typename Enable = void> template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> { struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit = static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range. FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix; Char prefix;
Char delimiter; Char delimiter;
Char postfix; Char postfix;
@ -55,87 +55,78 @@ struct formatting_tuple : formatting_base<Char> {
namespace internal { namespace internal {
template <typename RangeT, typename OutputIterator> template <typename RangeT, typename OutputIterator>
void copy(const RangeT &range, OutputIterator out) { OutputIterator copy(const RangeT& range, OutputIterator out) {
for (auto it = range.begin(), end = range.end(); it != end; ++it) for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it; *out++ = *it;
return out;
} }
template <typename OutputIterator> template <typename OutputIterator>
void copy(const char *str, OutputIterator out) { OutputIterator copy(const char* str, OutputIterator out) {
const char *p_curr = str; while (*str) *out++ = *str++;
while (*p_curr) { return out;
*out++ = *p_curr++;
}
} }
template <typename OutputIterator> template <typename OutputIterator>
void copy(char ch, OutputIterator out) { OutputIterator copy(char ch, OutputIterator out) {
*out++ = ch; *out++ = ch;
return out;
} }
/// Return true value if T has std::string interface, like std::string_view. /// Return true value if T has std::string interface, like std::string_view.
template <typename T> template <typename T> class is_like_std_string {
class is_like_std_string {
template <typename U> template <typename U>
static auto check(U *p) -> static auto check(U* p)
decltype(p->find('a'), p->length(), p->data(), int()); -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> template <typename> static void check(...);
static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(FMT_NULL))>::value; is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
}; };
template <typename Char> template <typename Char>
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {}; struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
template <typename... Ts> template <typename... Ts> struct conditional_helper {};
struct conditional_helper {};
template <typename T, typename _ = void> template <typename T, typename _ = void> struct is_range_ : std::false_type {};
struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800 #if !FMT_MSC_VER || FMT_MSC_VER > 1800
template <typename T> template <typename T>
struct is_range_<T, typename std::conditional< struct is_range_<
false, T, conditional_t<false,
conditional_helper<decltype(internal::declval<T>().begin()), conditional_helper<decltype(std::declval<T>().begin()),
decltype(internal::declval<T>().end())>, decltype(std::declval<T>().end())>,
void>::type> : std::true_type {}; void>> : std::true_type {};
#endif #endif
/// tuple_size and tuple_element check. /// tuple_size and tuple_element check.
template <typename T> template <typename T> class is_tuple_like_ {
class is_tuple_like_ {
template <typename U> template <typename U>
static auto check(U *p) -> static auto check(U* p)
decltype(std::tuple_size<U>::value, -> decltype(std::tuple_size<U>::value,
internal::declval<typename std::tuple_element<0, U>::type>(), int()); (void)std::declval<typename std::tuple_element<0, U>::type>(),
template <typename> int());
static void check(...); template <typename> static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(FMT_NULL))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
}; };
// Check for integer_sequence // Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N> template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>; using integer_sequence = std::integer_sequence<T, N...>;
template <std::size_t... N> template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
using index_sequence = std::index_sequence<N...>;
template <std::size_t N> template <std::size_t N>
using make_index_sequence = std::make_index_sequence<N>; using make_index_sequence = std::make_index_sequence<N>;
#else #else
template <typename T, T... N> template <typename T, T... N> struct integer_sequence {
struct integer_sequence { using value_type = T;
typedef T value_type;
static FMT_CONSTEXPR std::size_t size() { static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
return sizeof...(N);
}
}; };
template <std::size_t... N> template <std::size_t... N>
@ -151,7 +142,7 @@ using make_index_sequence = make_integer_sequence<std::size_t, N>;
#endif #endif
template <class Tuple, class F, size_t... Is> template <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT { void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
using std::get; using std::get;
// using free function get<I>(T) now. // using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
@ -159,26 +150,25 @@ void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT {
} }
template <class T> template <class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
get_indexes(T const &) { return {}; } T const&) {
return {};
}
template <class Tuple, class F> template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
void for_each(Tuple &&tup, F &&f) {
const auto indexes = get_indexes(tup); const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
} }
template<typename Arg> template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&, typename std::decay<Arg>::type>::value)>
typename std::enable_if< FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
!is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
return add_space ? " {}" : "{}"; return add_space ? " {}" : "{}";
} }
template<typename Arg> template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&, typename std::decay<Arg>::type>::value)>
typename std::enable_if< FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
return add_space ? " \"{}\"" : "\"{}\""; return add_space ? " \"{}\"" : "\"{}\"";
} }
@ -186,61 +176,58 @@ FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
return add_space ? " \"{}\"" : "\"{}\""; return add_space ? " \"{}\"" : "\"{}\"";
} }
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) { FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
return add_space ? L" \"{}\"" : L"\"{}\""; return add_space ? L" \"{}\"" : L"\"{}\"";
} }
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) { FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
return add_space ? " '{}'" : "'{}'"; return add_space ? " '{}'" : "'{}'";
} }
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'"; return add_space ? L" '{}'" : L"'{}'";
} }
} // namespace internal } // namespace internal
template <typename T> template <typename T> struct is_tuple_like {
struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value; internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
}; };
template <typename TupleT, typename Char> template <typename TupleT, typename Char>
struct formatter<TupleT, Char, struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type> { private:
private:
// C++11 generic lambda for format() // C++11 generic lambda for format()
template <typename FormatContext> template <typename FormatContext> struct format_each {
struct format_each { template <typename T> void operator()(const T& v) {
template <typename T>
void operator()(const T& v) {
if (i > 0) { if (i > 0) {
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) {
*out++ = ' '; *out++ = ' ';
} }
internal::copy(formatting.delimiter, out); out = internal::copy(formatting.delimiter, out);
} }
format_to(out, out = format_to(out,
internal::format_str_quoted( internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v), (formatting.add_delimiter_spaces && i > 0), v),
v); v);
++i; ++i;
} }
formatting_tuple<Char>& formatting; formatting_tuple<Char>& formatting;
std::size_t& i; std::size_t& i;
typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out; typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
}; };
public: public:
formatting_tuple<Char> formatting; formatting_tuple<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx); return formatting.parse(ctx);
} }
template <typename FormatContext = format_context> template <typename FormatContext = format_context>
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) { auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out(); auto out = ctx.out();
std::size_t i = 0; std::size_t i = 0;
internal::copy(formatting.prefix, out); internal::copy(formatting.prefix, out);
@ -255,54 +242,124 @@ public:
} }
}; };
template <typename T> template <typename T, typename Char> struct is_range {
struct is_range {
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value && !internal::is_like_std_string<T>::value; internal::is_range_<T>::value &&
!internal::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;
}; };
template <typename RangeT, typename Char> template <typename RangeT, typename Char>
struct formatter<RangeT, Char, struct formatter<RangeT, Char,
typename std::enable_if<fmt::is_range<RangeT>::value>::type> { enable_if_t<fmt::is_range<RangeT, Char>::value>> {
formatting_range<Char> formatting; formatting_range<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx); return formatting.parse(ctx);
} }
template <typename FormatContext> template <typename FormatContext>
typename FormatContext::iterator format( typename FormatContext::iterator format(const RangeT& values,
const RangeT &values, FormatContext &ctx) { FormatContext& ctx) {
auto out = ctx.out(); auto out = internal::copy(formatting.prefix, ctx.out());
internal::copy(formatting.prefix, out);
std::size_t i = 0; std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) { for (auto it = values.begin(), end = values.end(); it != end; ++it) {
if (i > 0) { if (i > 0) {
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) *out++ = ' ';
*out++ = ' '; out = internal::copy(formatting.delimiter, out);
}
internal::copy(formatting.delimiter, out);
} }
format_to(out, out = format_to(out,
internal::format_str_quoted( internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it), (formatting.add_delimiter_spaces && i > 0), *it),
*it); *it);
if (++i > formatting.range_length_limit) { if (++i > formatting.range_length_limit) {
format_to(out, " ... <other elements>"); out = format_to(out, " ... <other elements>");
break; break;
} }
} }
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) *out++ = ' ';
*out++ = ' '; return internal::copy(formatting.postfix, out);
}
internal::copy(formatting.postfix, out);
return ctx.out();
} }
}; };
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple{t}, sep{s} {}
};
template <typename Char, typename... T>
struct formatter<tuple_arg_join<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
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)>{});
}
private:
template <typename FormatContext, size_t... N>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
internal::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...);
}
template <typename FormatContext>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
// NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload.
return ctx.out();
}
template <typename FormatContext, typename Arg, typename... Args>
typename FormatContext::iterator format_args(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
const Arg& arg, const Args&... args) {
using base = formatter<typename std::decay<Arg>::type, Char>;
auto out = ctx.out();
out = base{}.format(arg, ctx);
if (sizeof...(Args) > 0) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
return format_args(value, ctx, args...);
}
return out;
}
};
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
**Example**::
std::tuple<int, char> t = {1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// Output: "1, a"
\endrst
*/
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
string_view sep) {
return {tuple, sep};
}
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
wstring_view sep) {
return {tuple, sep};
}
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_RANGES_H_ #endif // FMT_RANGES_H_

View File

@ -1,160 +0,0 @@
// Formatting library for C++ - time formatting
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_TIME_H_
#define FMT_TIME_H_
#include "format.h"
#include <ctime>
#include <locale>
FMT_BEGIN_NAMESPACE
// Prevents expansion of a preceding token as a function-style macro.
// Usage: f FMT_NOMACRO()
#define FMT_NOMACRO
namespace internal{
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
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
struct dispatcher {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(internal::null<>) {
using namespace fmt::internal;
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;
std::tm *tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != FMT_NULL;
}
#endif
};
dispatcher lt(time);
// Too big time values may be unsupported.
if (!lt.run())
FMT_THROW(format_error("time_t value out of range"));
return lt.tm_;
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) {
struct dispatcher {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(internal::null<>) {
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
std::tm *tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
return tm != FMT_NULL;
}
#endif
};
dispatcher gt(time);
// Too big time values may be unsupported.
if (!gt.run())
FMT_THROW(format_error("time_t value out of range"));
return gt.tm_;
}
namespace internal {
inline std::size_t strftime(char *str, std::size_t count, const char *format,
const std::tm *time) {
return std::strftime(str, count, format, time);
}
inline std::size_t strftime(wchar_t *str, std::size_t count,
const wchar_t *format, const std::tm *time) {
return std::wcsftime(str, count, format, time);
}
}
template <typename Char>
struct formatter<std::tm, Char> {
template <typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
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.append(it, end);
tm_format.push_back('\0');
return end;
}
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();
for (;;) {
std::size_t size = buf.capacity() - start;
std::size_t count =
internal::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) {
buf.resize(start + count);
break;
}
if (size >= tm_format.size() * 256) {
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
return std::copy(buf.begin(), buf.end(), ctx.out());
}
basic_memory_buffer<Char> tm_format;
};
FMT_END_NAMESPACE
#endif // FMT_TIME_H_

View File

@ -8,51 +8,168 @@
#include "fmt/format-inl.h" #include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template struct internal::basic_data<void>; namespace internal {
template FMT_API internal::locale_ref::locale_ref(const std::locale &loc);
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
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about nonliteral format string.
auto snprintf_ptr = FMT_SNPRINTF;
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;
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_API internal::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;
#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 std::locale internal::locale_ref::get<std::locale>() const;
#endif
// Explicit instantiations for char. // 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::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
template FMT_API void internal::basic_buffer<char>::append(const char *, const char *); template FMT_API void internal::buffer<char>::append(const char*, const char*);
template FMT_API void internal::arg_map<format_context>::init( template FMT_API void internal::arg_map<format_context>::init(
const basic_format_args<format_context> &args); const basic_format_args<format_context>& args);
template FMT_API int internal::char_traits<char>::format_float(
char *, std::size_t, const char *, int, double);
template FMT_API int internal::char_traits<char>::format_float(
char *, std::size_t, const char *, int, long double);
template FMT_API std::string internal::vformat<char>( template FMT_API std::string internal::vformat<char>(
string_view, basic_format_args<format_context>); string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to( template FMT_API format_context::iterator internal::vformat_to(
internal::buffer &, string_view, basic_format_args<format_context>); internal::buffer<char>&, string_view, basic_format_args<format_context>);
template FMT_API void internal::sprintf_format( template FMT_API int internal::snprintf_float(double, int,
double, internal::buffer &, core_format_specs); internal::float_specs,
template FMT_API void internal::sprintf_format( internal::buffer<char>&);
long double, internal::buffer &, core_format_specs); 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>&);
// Explicit instantiations for wchar_t. // 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::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API void internal::basic_buffer<wchar_t>::append( template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
const wchar_t *, const wchar_t *); const wchar_t*);
template FMT_API void internal::arg_map<wformat_context>::init(
const basic_format_args<wformat_context> &);
template FMT_API int internal::char_traits<wchar_t>::format_float(
wchar_t *, std::size_t, const wchar_t *, int, double);
template FMT_API int internal::char_traits<wchar_t>::format_float(
wchar_t *, std::size_t, const wchar_t *, int, long double);
template FMT_API std::wstring internal::vformat<wchar_t>( template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>); wstring_view, basic_format_args<wformat_context>);

View File

@ -7,49 +7,51 @@
// Disable bogus MSVC warnings. // Disable bogus MSVC warnings.
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER) #if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS # define _CRT_SECURE_NO_WARNINGS
#endif #endif
#include "fmt/posix.h" #include "fmt/posix.h"
#include <limits.h> #include <climits>
#include <sys/types.h>
#if FMT_USE_FCNTL
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#ifndef _WIN32 #ifndef _WIN32
# include <unistd.h> # include <unistd.h>
#else #else
# ifndef WIN32_LEAN_AND_MEAN # ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN
# endif # endif
# include <windows.h> # include <io.h>
# include <io.h> # include <windows.h>
# define O_CREAT _O_CREAT # define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC # define O_TRUNC _O_TRUNC
# ifndef S_IRUSR # ifndef S_IRUSR
# define S_IRUSR _S_IREAD # define S_IRUSR _S_IREAD
# endif # endif
# ifndef S_IWUSR # ifndef S_IWUSR
# define S_IWUSR _S_IWRITE # define S_IWUSR _S_IWRITE
# endif # endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
#endif // _WIN32 #endif // _WIN32
#endif // FMT_USE_FCNTL
#ifdef fileno #ifdef fileno
# undef fileno # undef fileno
#endif #endif
namespace { namespace {
#ifdef _WIN32 #ifdef _WIN32
// Return type of read and write functions. // Return type of read and write functions.
typedef int RWResult; using RWResult = int;
// On Windows the count argument to read and write is unsigned, so convert // On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow. // it from size_t preventing integer overflow.
@ -58,11 +60,11 @@ inline unsigned convert_rwcount(std::size_t count) {
} }
#else #else
// Return type of read and write functions. // Return type of read and write functions.
typedef ssize_t RWResult; using RWResult = ssize_t;
inline std::size_t convert_rwcount(std::size_t count) { return count; } inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif #endif
} } // namespace
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
@ -72,19 +74,17 @@ buffered_file::~buffered_file() FMT_NOEXCEPT {
} }
buffered_file::buffered_file(cstring_view filename, cstring_view mode) { buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
FMT_RETRY_VAL(file_, FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_NULL); nullptr);
if (!file_) if (!file_)
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str())); FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
} }
void buffered_file::close() { void buffered_file::close() {
if (!file_) if (!file_) return;
return;
int result = FMT_SYSTEM(fclose(file_)); int result = FMT_SYSTEM(fclose(file_));
file_ = FMT_NULL; file_ = nullptr;
if (result != 0) if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
FMT_THROW(system_error(errno, "cannot close file"));
} }
// A macro used to prevent expansion of fileno on broken versions of MinGW. // A macro used to prevent expansion of fileno on broken versions of MinGW.
@ -92,11 +92,11 @@ void buffered_file::close() {
int buffered_file::fileno() const { int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_)); int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1) if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
FMT_THROW(system_error(errno, "cannot get file descriptor"));
return fd; return fd;
} }
#if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) { file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR; int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__) #if defined(_WIN32) && !defined(__MINGW32__)
@ -117,14 +117,12 @@ file::~file() FMT_NOEXCEPT {
} }
void file::close() { void file::close() {
if (fd_ == -1) if (fd_ == -1) return;
return;
// Don't retry close in case of EINTR! // Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_)); int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1; fd_ = -1;
if (result != 0) if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
FMT_THROW(system_error(errno, "cannot close file"));
} }
long long file::size() const { long long file::size() const {
@ -143,29 +141,27 @@ long long file::size() const {
unsigned long long long_size = size_upper; unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower; return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
#else #else
typedef struct stat Stat; using Stat = struct stat;
Stat file_stat = Stat(); Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes")); FMT_THROW(system_error(errno, "cannot get file attributes"));
static_assert(sizeof(long long) >= sizeof(file_stat.st_size), static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
"return type of file::size is not large enough"); "return type of file::size is not large enough");
return file_stat.st_size; return file_stat.st_size;
#endif #endif
} }
std::size_t file::read(void *buffer, std::size_t count) { std::size_t file::read(void* buffer, std::size_t count) {
RWResult result = 0; RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0) if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
FMT_THROW(system_error(errno, "cannot read from file"));
return internal::to_unsigned(result); return internal::to_unsigned(result);
} }
std::size_t file::write(const void *buffer, std::size_t count) { std::size_t file::write(const void* buffer, std::size_t count) {
RWResult result = 0; RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0) if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
FMT_THROW(system_error(errno, "cannot write to file"));
return internal::to_unsigned(result); return internal::to_unsigned(result);
} }
@ -182,19 +178,18 @@ void file::dup2(int fd) {
int result = 0; int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) { if (result == -1) {
FMT_THROW(system_error(errno, FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
"cannot duplicate file descriptor {} to {}", fd_, fd)); fd_, fd));
} }
} }
void file::dup2(int fd, error_code &ec) FMT_NOEXCEPT { void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT {
int result = 0; int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) if (result == -1) ec = error_code(errno);
ec = error_code(errno);
} }
void file::pipe(file &read_end, file &write_end) { void file::pipe(file& read_end, file& write_end) {
// Close the descriptors first to make sure that assignments don't throw // Close the descriptors first to make sure that assignments don't throw
// and there are no leaks. // and there are no leaks.
read_end.close(); read_end.close();
@ -209,20 +204,19 @@ void file::pipe(file &read_end, file &write_end) {
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds)); int result = FMT_POSIX_CALL(pipe(fds));
#endif #endif
if (result != 0) if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
FMT_THROW(system_error(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd // The following assignments don't throw because read_fd and write_fd
// are closed. // are closed.
read_end = file(fds[0]); read_end = file(fds[0]);
write_end = file(fds[1]); write_end = file(fds[1]);
} }
buffered_file file::fdopen(const char *mode) { buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR. // Don't retry as fdopen doesn't return EINTR.
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode)); FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f) if (!f)
FMT_THROW(system_error(errno, FMT_THROW(
"cannot associate stream with file descriptor")); system_error(errno, "cannot associate stream with file descriptor"));
buffered_file bf(f); buffered_file bf(f);
fd_ = -1; fd_ = -1;
return bf; return bf;
@ -235,10 +229,9 @@ long getpagesize() {
return si.dwPageSize; return si.dwPageSize;
#else #else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0) if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
FMT_THROW(system_error(errno, "cannot get memory page size"));
return size; return size;
#endif #endif
} }
#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE FMT_END_NAMESPACE

2061
Externals/fmt/support/C++.sublime-syntax vendored Normal file

File diff suppressed because it is too large Load Diff

19
Externals/fmt/support/Vagrantfile vendored Normal file
View File

@ -0,0 +1,19 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# A vagrant config for testing against gcc-4.8.
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/xenial64"
config.vm.provider "virtualbox" do |vb|
vb.memory = "4096"
end
config.vm.provision "shell", inline: <<-SHELL
apt-get update
apt-get install -y g++ make wget git
wget -q https://github.com/Kitware/CMake/releases/download/v3.14.4/cmake-3.14.4-Linux-x86_64.tar.gz
tar xzf cmake-3.14.4-Linux-x86_64.tar.gz
ln -s `pwd`/cmake-3.14.4-Linux-x86_64/bin/cmake /usr/local/bin
SHELL
end

View File

@ -23,14 +23,17 @@ else:
# Add MSBuild 14.0 to PATH as described in # Add MSBuild 14.0 to PATH as described in
# http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc. # http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc.
os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\15.0\Bin;' + path os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\15.0\Bin;' + path
if image == 'Visual Studio 2013': if image == 'Visual Studio 2019':
generator = 'Visual Studio 12 2013' generator = 'Visual Studio 16 2019'
elif image == 'Visual Studio 2015': if platform == 'x64':
generator = 'Visual Studio 14 2015' cmake_command.extend(['-A', 'x64'])
elif image == 'Visual Studio 2017': else:
generator = 'Visual Studio 15 2017' if image == 'Visual Studio 2015':
if platform == 'x64': generator = 'Visual Studio 14 2015'
generator += ' Win64' elif image == 'Visual Studio 2017':
generator = 'Visual Studio 15 2017'
if platform == 'x64':
generator += ' Win64'
cmake_command.append('-G' + generator) cmake_command.append('-G' + generator)
build_command = ['cmake', '--build', '.', '--config', config, '--', '/m:4'] build_command = ['cmake', '--build', '.', '--config', config, '--', '/m:4']
test_command = ['ctest', '-C', config] test_command = ['ctest', '-C', config]

View File

@ -4,20 +4,27 @@ configuration:
clone_depth: 1 clone_depth: 1
image:
- Visual Studio 2015
- Visual Studio 2019
- Visual Studio 2017
platform: platform:
- Win32 - Win32
- x64 - x64
image:
- Visual Studio 2013
- Visual Studio 2015
- Visual Studio 2017
environment: environment:
CTEST_OUTPUT_ON_FAILURE: 1 CTEST_OUTPUT_ON_FAILURE: 1
MSVC_DEFAULT_OPTIONS: ON MSVC_DEFAULT_OPTIONS: ON
BUILD: msvc BUILD: msvc
matrix:
exclude:
- image: Visual Studio 2015
platform: Win32
- image: Visual Studio 2019
platform: Win32
before_build: before_build:
- mkdir build - mkdir build
- cd build - cd build

View File

@ -9,10 +9,12 @@ buildscript {
// //
// https://developer.android.com/studio/releases/gradle-plugin // https://developer.android.com/studio/releases/gradle-plugin
// //
// Notice that 3.1.3 here is the version of [Android Gradle Plugin] // Notice that 3.3.0 here is the version of [Android Gradle Plugin]
// Accroding to URL above you will need Gradle 4.4 or higher // Accroding to URL above you will need Gradle 5.0 or higher
// //
classpath 'com.android.tools.build:gradle:3.1.3' // If you are using Android Studio, and it is using Gradle's lower
// version, Use the plugin version 3.1.3 ~ 3.2.0 for Gradle 4.4 ~ 4.10
classpath 'com.android.tools.build:gradle:3.3.0'
} }
} }
repositories { repositories {
@ -43,8 +45,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 21 // Android 5.0+ minSdkVersion 21 // Android 5.0+
targetSdkVersion 25 // Follow Compile SDK targetSdkVersion 25 // Follow Compile SDK
versionCode 20 // Follow release count versionCode 21 // Follow release count
versionName "5.2.1" // Follow Official version versionName "5.3.0" // Follow Official version
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild { externalNativeBuild {

View File

@ -50,6 +50,7 @@ set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
# Check if variadic templates are working and not affected by GCC bug 39653: # Check if variadic templates are working and not affected by GCC bug 39653:
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653
# Can be removed once gcc 4.4 support is dropped.
check_cxx_source_compiles(" check_cxx_source_compiles("
template <class T, class ...Types> template <class T, class ...Types>
struct S { typedef typename S<Types...>::type type; }; struct S { typedef typename S<Types...>::type type; };
@ -58,33 +59,6 @@ if (NOT SUPPORTS_VARIADIC_TEMPLATES)
set (SUPPORTS_VARIADIC_TEMPLATES OFF) set (SUPPORTS_VARIADIC_TEMPLATES OFF)
endif () endif ()
# Check if initializer lists are supported.
check_cxx_source_compiles("
#include <initializer_list>
int main() {}" SUPPORTS_INITIALIZER_LIST)
if (NOT SUPPORTS_INITIALIZER_LIST)
set (SUPPORTS_INITIALIZER_LIST OFF)
endif ()
# Check if enum bases are available
check_cxx_source_compiles("
enum C : char {A};
int main() {}"
SUPPORTS_ENUM_BASE)
if (NOT SUPPORTS_ENUM_BASE)
set (SUPPORTS_ENUM_BASE OFF)
endif ()
# Check if type traits are available
check_cxx_source_compiles("
#include <type_traits>
class C { void operator=(const C&); };
int main() { static_assert(!std::is_copy_assignable<C>::value, \"\"); }"
SUPPORTS_TYPE_TRAITS)
if (NOT SUPPORTS_TYPE_TRAITS)
set (SUPPORTS_TYPE_TRAITS OFF)
endif ()
# Check if user-defined literals are available # Check if user-defined literals are available
check_cxx_source_compiles(" check_cxx_source_compiles("
void operator\"\" _udl(long double); void operator\"\" _udl(long double);
@ -94,4 +68,14 @@ if (NOT SUPPORTS_USER_DEFINED_LITERALS)
set (SUPPORTS_USER_DEFINED_LITERALS OFF) set (SUPPORTS_USER_DEFINED_LITERALS OFF)
endif () endif ()
# Check if <variant> is available
set(CMAKE_REQUIRED_FLAGS -std=c++1z)
check_cxx_source_compiles("
#include <variant>
int main() {}"
FMT_HAS_VARIANT)
if (NOT FMT_HAS_VARIANT)
set (FMT_HAS_VARIANT OFF)
endif ()
set(CMAKE_REQUIRED_FLAGS ) set(CMAKE_REQUIRED_FLAGS )

View File

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

View File

@ -1,11 +0,0 @@
@echo on
rem This scripts configures build environment and runs CMake.
rem Use it instead of running CMake directly when building with
rem the Microsoft SDK toolchain rather than Visual Studio.
rem It is used in the same way as cmake, for example:
rem
rem run-cmake -G "Visual Studio 10 Win64" .
for /F "delims=" %%i IN ('cmake "-DPRINT_PATH=1" -P %~dp0/FindSetEnv.cmake') DO set setenv=%%i
if NOT "%setenv%" == "" call "%setenv%"
cmake %*

View File

@ -5,6 +5,9 @@
Usage: Usage:
manage.py release [<branch>] manage.py release [<branch>]
manage.py site manage.py site
For the release command $FMT_TOKEN should contain a GitHub personal access token
obtained from https://github.com/settings/tokens.
""" """
from __future__ import print_function from __future__ import print_function
@ -142,6 +145,7 @@ def update_site(env):
b.data = b.data.replace('std::FILE*', 'std::FILE *') b.data = b.data.replace('std::FILE*', 'std::FILE *')
b.data = b.data.replace('unsigned int', 'unsigned') 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')
# Fix a broken link in index.rst. # Fix a broken link in index.rst.
index = os.path.join(target_doc_dir, 'index.rst') index = os.path.join(target_doc_dir, 'index.rst')
with rewrite(index) as b: with rewrite(index) as b:

View File

@ -1,2 +1,2 @@
If you are not redirected automatically, follow the If you are not redirected automatically, follow the
`link to the fmt documentation <http://fmtlib.net/latest/>`_. `link to the fmt documentation <https://fmt.dev/latest/>`_.

View File

@ -2,15 +2,15 @@
{% block extrahead %} {% block extrahead %}
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="refresh" content="1;url=http://fmtlib.net/latest/"> <meta http-equiv="refresh" content="1;url=https://fmt.dev/latest/">
<script type="text/javascript"> <script type="text/javascript">
window.location.href = "http://fmtlib.net/latest/" window.location.href = "https://fmt.dev/latest/"
</script> </script>
<title>Page Redirection</title> <title>Page Redirection</title>
{% endblock %} {% endblock %}
{% block document %} {% block document %}
If you are not redirected automatically, follow the <a href='http://fmtlib.net/latest/'>link to the fmt documentation</a>. If you are not redirected automatically, follow the <a href='https://fmt.dev/latest/'>link to the fmt documentation</a>.
{% endblock %} {% endblock %}
{% block footer %} {% block footer %}

View File

@ -83,19 +83,25 @@ install_dir = os.path.join(fmt_dir, "_install")
build_dir = os.path.join(fmt_dir, "_build") build_dir = os.path.join(fmt_dir, "_build")
test_build_dir = os.path.join(fmt_dir, "_build_test") test_build_dir = os.path.join(fmt_dir, "_build_test")
# Configure library. # Configure the library.
makedirs_if_not_exist(build_dir) makedirs_if_not_exist(build_dir)
cmake_flags = [ cmake_flags = [
'-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build, '-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build,
'-DCMAKE_CXX_STANDARD=' + standard '-DCMAKE_CXX_STANDARD=' + standard
] ]
# Make sure the fuzzers still compile.
main_cmake_flags = list(cmake_flags)
if 'ENABLE_FUZZING' in os.environ:
main_cmake_flags += ['-DFMT_FUZZ=ON', '-DFMT_FUZZ_LINKMAIN=On']
check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', '-DFMT_WERROR=ON', fmt_dir] + check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', '-DFMT_WERROR=ON', fmt_dir] +
cmake_flags, cwd=build_dir) main_cmake_flags, cwd=build_dir)
# Build library. # Build the library.
check_call(['make', '-j4'], cwd=build_dir) check_call(['cmake', '--build','.'], cwd=build_dir)
# Test library. # Test the library.
env = os.environ.copy() env = os.environ.copy()
env['CTEST_OUTPUT_ON_FAILURE'] = '1' env['CTEST_OUTPUT_ON_FAILURE'] = '1'
if call(['make', 'test'], env=env, cwd=build_dir): if call(['make', 'test'], env=env, cwd=build_dir):
@ -103,7 +109,7 @@ if call(['make', 'test'], env=env, cwd=build_dir):
print(f.read()) print(f.read())
sys.exit(-1) sys.exit(-1)
# Install library. # Install the library.
check_call(['make', 'install'], cwd=build_dir) check_call(['make', 'install'], cwd=build_dir)
# Test installation. # Test installation.