Externals: Update fmt to 6.1.2
This commit is contained in:
parent
f36c735856
commit
1873b7a398
|
@ -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.
|
||||||
|
|
|
@ -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!
|
|
@ -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!
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
|
||||||
|
|
|
@ -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
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
@ -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));
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
@ -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_
|
||||||
|
|
||||||
|
|
|
@ -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_
|
|
|
@ -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>);
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 )
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 %*
|
|
|
@ -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:
|
||||||
|
|
|
@ -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/>`_.
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue