Externals: Update fmt to 8.1.1
This commit is contained in:
parent
07fd17445c
commit
1a2e88cbf3
|
@ -7,21 +7,34 @@ endif()
|
||||||
|
|
||||||
# Determine if fmt is built as a subproject (using add_subdirectory)
|
# Determine if fmt is built as a subproject (using add_subdirectory)
|
||||||
# or if it is the master project.
|
# or if it is the master project.
|
||||||
set(MASTER_PROJECT OFF)
|
if (NOT DEFINED FMT_MASTER_PROJECT)
|
||||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
set(FMT_MASTER_PROJECT OFF)
|
||||||
set(MASTER_PROJECT ON)
|
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||||
message(STATUS "CMake version: ${CMAKE_VERSION}")
|
set(FMT_MASTER_PROJECT ON)
|
||||||
|
message(STATUS "CMake version: ${CMAKE_VERSION}")
|
||||||
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# Joins arguments and places the results in ${result_var}.
|
# Joins arguments and places the results in ${result_var}.
|
||||||
function(join result_var)
|
function(join result_var)
|
||||||
set(result )
|
set(result "")
|
||||||
foreach (arg ${ARGN})
|
foreach (arg ${ARGN})
|
||||||
set(result "${result}${arg}")
|
set(result "${result}${arg}")
|
||||||
endforeach ()
|
endforeach ()
|
||||||
set(${result_var} "${result}" PARENT_SCOPE)
|
set(${result_var} "${result}" PARENT_SCOPE)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
function(enable_module target)
|
||||||
|
if (MSVC)
|
||||||
|
set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc)
|
||||||
|
target_compile_options(${target}
|
||||||
|
PRIVATE /interface /ifcOutput ${BMI}
|
||||||
|
INTERFACE /reference fmt=${BMI})
|
||||||
|
endif ()
|
||||||
|
set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI})
|
||||||
|
set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
include(CMakeParseArguments)
|
include(CMakeParseArguments)
|
||||||
|
|
||||||
# Sets a cache variable with a docstring joined from multiple arguments:
|
# Sets a cache variable with a docstring joined from multiple arguments:
|
||||||
|
@ -44,7 +57,7 @@ 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 (MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
|
if (FMT_MASTER_PROJECT AND NOT CMAKE_BUILD_TYPE)
|
||||||
set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING
|
set_verbose(CMAKE_BUILD_TYPE Release CACHE STRING
|
||||||
"Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
|
"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.")
|
||||||
|
@ -61,12 +74,33 @@ option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
|
||||||
OFF)
|
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." ${FMT_MASTER_PROJECT})
|
||||||
option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
|
option(FMT_INSTALL "Generate the install target." ${FMT_MASTER_PROJECT})
|
||||||
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
|
option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT})
|
||||||
option(FMT_FUZZ "Generate the fuzz target." OFF)
|
option(FMT_FUZZ "Generate the fuzz target." OFF)
|
||||||
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
|
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
|
||||||
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
|
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON)
|
||||||
|
option(FMT_MODULE "Build a module instead of a traditional library." OFF)
|
||||||
|
option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF)
|
||||||
|
|
||||||
|
set(FMT_CAN_MODULE OFF)
|
||||||
|
if (CMAKE_CXX_STANDARD GREATER 17 AND
|
||||||
|
# msvc 16.10-pre4
|
||||||
|
MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.29.30035)
|
||||||
|
set(FMT_CAN_MODULE OFF)
|
||||||
|
endif ()
|
||||||
|
if (NOT FMT_CAN_MODULE)
|
||||||
|
set(FMT_MODULE OFF)
|
||||||
|
message(STATUS "Module support is disabled.")
|
||||||
|
endif ()
|
||||||
|
if (FMT_TEST AND FMT_MODULE)
|
||||||
|
# The tests require {fmt} to be compiled as traditional library
|
||||||
|
message(STATUS "Testing is incompatible with build mode 'module'.")
|
||||||
|
endif ()
|
||||||
|
set(FMT_SYSTEM_HEADERS_ATTRIBUTE "")
|
||||||
|
if (FMT_SYSTEM_HEADERS)
|
||||||
|
set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM)
|
||||||
|
endif ()
|
||||||
|
|
||||||
# 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)
|
||||||
|
@ -102,6 +136,18 @@ if (${index} GREATER -1)
|
||||||
endif ()
|
endif ()
|
||||||
message(STATUS "Required features: ${FMT_REQUIRED_FEATURES}")
|
message(STATUS "Required features: ${FMT_REQUIRED_FEATURES}")
|
||||||
|
|
||||||
|
if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET)
|
||||||
|
set_verbose(CMAKE_CXX_VISIBILITY_PRESET hidden CACHE STRING
|
||||||
|
"Preset for the export of private symbols")
|
||||||
|
set_property(CACHE CMAKE_CXX_VISIBILITY_PRESET PROPERTY STRINGS
|
||||||
|
hidden default)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_VISIBILITY_INLINES_HIDDEN)
|
||||||
|
set_verbose(CMAKE_VISIBILITY_INLINES_HIDDEN ON CACHE BOOL
|
||||||
|
"Whether to add a compile flag to hide symbols of inline functions")
|
||||||
|
endif ()
|
||||||
|
|
||||||
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
|
||||||
|
@ -110,16 +156,16 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
-Wcast-align
|
-Wcast-align
|
||||||
-Wctor-dtor-privacy -Wdisabled-optimization
|
-Wctor-dtor-privacy -Wdisabled-optimization
|
||||||
-Winvalid-pch -Woverloaded-virtual
|
-Winvalid-pch -Woverloaded-virtual
|
||||||
-Wconversion -Wswitch-enum -Wundef
|
-Wconversion -Wundef
|
||||||
-Wno-ctor-dtor-privacy -Wno-format-nonliteral)
|
-Wno-ctor-dtor-privacy -Wno-format-nonliteral)
|
||||||
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}
|
||||||
-Wno-dangling-else -Wno-unused-local-typedefs)
|
-Wno-dangling-else -Wno-unused-local-typedefs)
|
||||||
endif ()
|
endif ()
|
||||||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
||||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion
|
||||||
-Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast
|
-Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast
|
||||||
-Wvector-operation-performance -Wsized-deallocation)
|
-Wvector-operation-performance -Wsized-deallocation -Wshadow)
|
||||||
endif ()
|
endif ()
|
||||||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
|
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
|
||||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
|
||||||
|
@ -130,7 +176,8 @@ endif ()
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wundef
|
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wundef
|
||||||
-Wdeprecated -Wweak-vtables)
|
-Wdeprecated -Wweak-vtables -Wshadow
|
||||||
|
-Wno-gnu-zero-variadic-macro-arguments)
|
||||||
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}
|
||||||
|
@ -144,7 +191,7 @@ if (MSVC)
|
||||||
set(WERROR_FLAG /WX)
|
set(WERROR_FLAG /WX)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||||
# If Microsoft SDK is installed create script run-msbuild.bat that
|
# If Microsoft SDK is installed create script run-msbuild.bat that
|
||||||
# calls SetEnv.cmd to set up build environment and runs msbuild.
|
# calls SetEnv.cmd to set up build environment and runs msbuild.
|
||||||
# It is useful when building Visual Studio projects with the SDK
|
# It is useful when building Visual Studio projects with the SDK
|
||||||
|
@ -183,9 +230,12 @@ 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 compile.h core.h format.h format-inl.h
|
add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h
|
||||||
locale.h os.h ostream.h posix.h printf.h ranges.h)
|
format-inl.h locale.h os.h ostream.h printf.h ranges.h
|
||||||
if (FMT_OS)
|
xchar.h)
|
||||||
|
if (FMT_MODULE)
|
||||||
|
set(FMT_SOURCES src/fmt.cc)
|
||||||
|
elseif (FMT_OS)
|
||||||
set(FMT_SOURCES src/format.cc src/os.cc)
|
set(FMT_SOURCES src/format.cc src/os.cc)
|
||||||
else()
|
else()
|
||||||
set(FMT_SOURCES src/format.cc)
|
set(FMT_SOURCES src/format.cc)
|
||||||
|
@ -199,7 +249,7 @@ if (HAVE_STRTOD_L)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (MINGW)
|
if (MINGW)
|
||||||
check_cxx_compiler_flag("Wa,-mbig-obj" FMT_HAS_MBIG_OBJ)
|
check_cxx_compiler_flag("-Wa,-mbig-obj" FMT_HAS_MBIG_OBJ)
|
||||||
if (${FMT_HAS_MBIG_OBJ})
|
if (${FMT_HAS_MBIG_OBJ})
|
||||||
target_compile_options(fmt PUBLIC "-Wa,-mbig-obj")
|
target_compile_options(fmt PUBLIC "-Wa,-mbig-obj")
|
||||||
endif()
|
endif()
|
||||||
|
@ -211,10 +261,13 @@ endif ()
|
||||||
if (FMT_PEDANTIC)
|
if (FMT_PEDANTIC)
|
||||||
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||||
endif ()
|
endif ()
|
||||||
|
if (FMT_MODULE)
|
||||||
|
enable_module(fmt)
|
||||||
|
endif ()
|
||||||
|
|
||||||
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
|
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES})
|
||||||
|
|
||||||
target_include_directories(fmt PUBLIC
|
target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||||
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
|
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
|
||||||
|
|
||||||
|
@ -250,7 +303,7 @@ 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_compile_features(fmt-header-only INTERFACE ${FMT_REQUIRED_FEATURES})
|
||||||
|
|
||||||
target_include_directories(fmt-header-only INTERFACE
|
target_include_directories(fmt-header-only ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||||
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
|
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
|
||||||
|
|
||||||
|
@ -294,6 +347,13 @@ if (FMT_INSTALL)
|
||||||
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
|
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
|
||||||
|
|
||||||
set(INSTALL_TARGETS fmt fmt-header-only)
|
set(INSTALL_TARGETS fmt fmt-header-only)
|
||||||
|
|
||||||
|
# Install the library and headers.
|
||||||
|
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
|
||||||
|
LIBRARY DESTINATION ${FMT_LIB_DIR}
|
||||||
|
ARCHIVE DESTINATION ${FMT_LIB_DIR}
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
|
||||||
# Use a namespace because CMake provides better diagnostics for namespaced
|
# Use a namespace because CMake provides better diagnostics for namespaced
|
||||||
# imported targets.
|
# imported targets.
|
||||||
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
|
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
|
||||||
|
@ -306,12 +366,6 @@ if (FMT_INSTALL)
|
||||||
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
|
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
|
||||||
NAMESPACE fmt::)
|
NAMESPACE fmt::)
|
||||||
|
|
||||||
# Install the library and headers.
|
|
||||||
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
|
|
||||||
LIBRARY DESTINATION ${FMT_LIB_DIR}
|
|
||||||
ARCHIVE DESTINATION ${FMT_LIB_DIR}
|
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
|
||||||
|
|
||||||
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
|
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
|
||||||
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
|
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
|
||||||
install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt")
|
install(FILES ${FMT_HEADERS} DESTINATION "${FMT_INC_DIR}/fmt")
|
||||||
|
@ -340,7 +394,7 @@ if (FMT_FUZZ)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
|
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
|
||||||
if (MASTER_PROJECT AND EXISTS ${gitignore})
|
if (FMT_MASTER_PROJECT AND EXISTS ${gitignore})
|
||||||
# Get the list of ignored files from .gitignore.
|
# Get the list of ignored files from .gitignore.
|
||||||
file (STRINGS ${gitignore} lines)
|
file (STRINGS ${gitignore} lines)
|
||||||
list(REMOVE_ITEM lines /doc/html)
|
list(REMOVE_ITEM lines /doc/html)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,10 +1,16 @@
|
||||||
{fmt}
|
{fmt}
|
||||||
=====
|
=====
|
||||||
|
|
||||||
.. image:: https://travis-ci.org/fmtlib/fmt.png?branch=master
|
.. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg
|
||||||
:target: https://travis-ci.org/fmtlib/fmt
|
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux
|
||||||
|
|
||||||
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
|
.. image:: https://github.com/fmtlib/fmt/workflows/macos/badge.svg
|
||||||
|
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos
|
||||||
|
|
||||||
|
.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg
|
||||||
|
:target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows
|
||||||
|
|
||||||
|
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v?svg=true
|
||||||
:target: https://ci.appveyor.com/project/vitaut/fmt
|
:target: https://ci.appveyor.com/project/vitaut/fmt
|
||||||
|
|
||||||
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
|
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
|
||||||
|
@ -20,9 +26,9 @@
|
||||||
**{fmt}** is an open-source formatting library providing a fast and safe
|
**{fmt}** is an open-source formatting library providing a fast and safe
|
||||||
alternative to C stdio and C++ iostreams.
|
alternative to C stdio and C++ iostreams.
|
||||||
|
|
||||||
If you like this project, please consider donating to BYSOL,
|
If you like this project, please consider donating to the BYSOL
|
||||||
an initiative to help victims of political repressions in Belarus:
|
Foundation that helps victims of political repressions in Belarus:
|
||||||
https://www.facebook.com/donate/759400044849707/108388587646909/.
|
https://bysol.org/en/bs/general/.
|
||||||
|
|
||||||
`Documentation <https://fmt.dev>`__
|
`Documentation <https://fmt.dev>`__
|
||||||
|
|
||||||
|
@ -131,16 +137,16 @@ Output::
|
||||||
|
|
||||||
Output::
|
Output::
|
||||||
|
|
||||||
{1, 2, 3}
|
[1, 2, 3]
|
||||||
|
|
||||||
**Check a format string at compile time**
|
**Check a format string at compile time**
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
std::string s = fmt::format(FMT_STRING("{:d}"), "don't panic");
|
std::string s = fmt::format("{:d}", "I am not a number");
|
||||||
|
|
||||||
This gives a compile-time error because ``d`` is an invalid format specifier for
|
This gives a compile-time error in C++20 because ``d`` is an invalid format
|
||||||
a string.
|
specifier for a string.
|
||||||
|
|
||||||
**Write a file from a single thread**
|
**Write a file from a single thread**
|
||||||
|
|
||||||
|
@ -199,7 +205,7 @@ The above results were generated by building ``tinyformat_test.cpp`` on macOS
|
||||||
best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
|
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
|
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
|
||||||
further details refer to the `source
|
further details refer to the `source
|
||||||
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
|
<https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc>`_.
|
||||||
|
|
||||||
{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on
|
{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on
|
||||||
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
|
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
|
||||||
|
@ -283,6 +289,13 @@ Then you can run the speed test::
|
||||||
or the bloat test::
|
or the bloat test::
|
||||||
|
|
||||||
$ make bloat-test
|
$ make bloat-test
|
||||||
|
|
||||||
|
Migrating code
|
||||||
|
--------------
|
||||||
|
|
||||||
|
`clang-tidy-fmt <https://github.com/mikecrowe/clang-tidy-fmt>`_ provides clang
|
||||||
|
tidy checks for converting occurrences of ``printf`` and ``fprintf`` to
|
||||||
|
``fmt::print``.
|
||||||
|
|
||||||
Projects using this library
|
Projects using this library
|
||||||
---------------------------
|
---------------------------
|
||||||
|
@ -290,6 +303,8 @@ Projects using this library
|
||||||
* `0 A.D. <https://play0ad.com/>`_: a free, open-source, cross-platform
|
* `0 A.D. <https://play0ad.com/>`_: a free, open-source, cross-platform
|
||||||
real-time strategy game
|
real-time strategy game
|
||||||
|
|
||||||
|
* `2GIS <https://2gis.ru/>`_: free business listings with a city map
|
||||||
|
|
||||||
* `AMPL/MP <https://github.com/ampl/mp>`_:
|
* `AMPL/MP <https://github.com/ampl/mp>`_:
|
||||||
an open-source library for mathematical programming
|
an open-source library for mathematical programming
|
||||||
|
|
||||||
|
@ -310,7 +325,7 @@ Projects using this library
|
||||||
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database
|
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database
|
||||||
management system
|
management system
|
||||||
|
|
||||||
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
|
* `CUAUV <https://cuauv.org/>`_: Cornell University's autonomous underwater
|
||||||
vehicle
|
vehicle
|
||||||
|
|
||||||
* `Drake <https://drake.mit.edu/>`_: a planning, control, and analysis toolbox
|
* `Drake <https://drake.mit.edu/>`_: a planning, control, and analysis toolbox
|
||||||
|
@ -321,8 +336,15 @@ Projects using this library
|
||||||
|
|
||||||
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
|
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
|
||||||
|
|
||||||
|
* `fmtlog <https://github.com/MengRao/fmtlog>`_: a performant fmtlib-style
|
||||||
|
logging library with latency in nanoseconds
|
||||||
|
|
||||||
* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library
|
* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library
|
||||||
|
|
||||||
|
* `Grand Mountain Adventure
|
||||||
|
<https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/>`_:
|
||||||
|
A beautiful open-world ski & snowboarding game
|
||||||
|
|
||||||
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
||||||
Player vs Player Gaming Network with tweaks
|
Player vs Player Gaming Network with tweaks
|
||||||
|
|
||||||
|
@ -447,7 +469,7 @@ 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. According to
|
strings and positional arguments. Its main drawback is performance. According to
|
||||||
various, benchmarks it is much slower than other methods considered here. Boost
|
various benchmarks, it is much slower than other methods considered here. Boost
|
||||||
Format also has excessive build times and severe code bloat issues (see
|
Format also has excessive build times and severe code bloat issues (see
|
||||||
`Benchmarks`_).
|
`Benchmarks`_).
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,234 @@
|
||||||
|
// Formatting library for C++ - dynamic format arguments
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_ARGS_H_
|
||||||
|
#define FMT_ARGS_H_
|
||||||
|
|
||||||
|
#include <functional> // std::reference_wrapper
|
||||||
|
#include <memory> // std::unique_ptr
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "core.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename T> struct is_reference_wrapper : std::false_type {};
|
||||||
|
template <typename T>
|
||||||
|
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T> const T& unwrap(const T& v) { return v; }
|
||||||
|
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
|
||||||
|
return static_cast<const T&>(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
class dynamic_arg_list {
|
||||||
|
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
|
||||||
|
// templates it doesn't complain about inability to deduce single translation
|
||||||
|
// unit for placing vtable. So storage_node_base is made a fake template.
|
||||||
|
template <typename = void> struct node {
|
||||||
|
virtual ~node() = default;
|
||||||
|
std::unique_ptr<node<>> next;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct typed_node : node<> {
|
||||||
|
T value;
|
||||||
|
|
||||||
|
template <typename Arg>
|
||||||
|
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
|
||||||
|
: value(arg.data(), arg.size()) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<node<>> head_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename T, typename Arg> const T& push(const Arg& arg) {
|
||||||
|
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
|
||||||
|
auto& value = new_node->value;
|
||||||
|
new_node->next = std::move(head_);
|
||||||
|
head_ = std::move(new_node);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
A dynamic version of `fmt::format_arg_store`.
|
||||||
|
It's equipped with a storage to potentially temporary objects which lifetimes
|
||||||
|
could be shorter than the format arguments object.
|
||||||
|
|
||||||
|
It can be implicitly converted into `~fmt::basic_format_args` for passing
|
||||||
|
into type-erased formatting functions such as `~fmt::vformat`.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename Context>
|
||||||
|
class dynamic_format_arg_store
|
||||||
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||||
|
// Workaround a GCC template argument substitution bug.
|
||||||
|
: public basic_format_args<Context>
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using char_type = typename Context::char_type;
|
||||||
|
|
||||||
|
template <typename T> struct need_copy {
|
||||||
|
static constexpr detail::type mapped_type =
|
||||||
|
detail::mapped_type_constant<T, Context>::value;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
value = !(detail::is_reference_wrapper<T>::value ||
|
||||||
|
std::is_same<T, basic_string_view<char_type>>::value ||
|
||||||
|
std::is_same<T, detail::std_string_view<char_type>>::value ||
|
||||||
|
(mapped_type != detail::type::cstring_type &&
|
||||||
|
mapped_type != detail::type::string_type &&
|
||||||
|
mapped_type != detail::type::custom_type))
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using stored_type = conditional_t<detail::is_string<T>::value &&
|
||||||
|
!has_formatter<T, Context>::value &&
|
||||||
|
!detail::is_reference_wrapper<T>::value,
|
||||||
|
std::basic_string<char_type>, T>;
|
||||||
|
|
||||||
|
// Storage of basic_format_arg must be contiguous.
|
||||||
|
std::vector<basic_format_arg<Context>> data_;
|
||||||
|
std::vector<detail::named_arg_info<char_type>> named_info_;
|
||||||
|
|
||||||
|
// Storage of arguments not fitting into basic_format_arg must grow
|
||||||
|
// without relocation because items in data_ refer to it.
|
||||||
|
detail::dynamic_arg_list dynamic_args_;
|
||||||
|
|
||||||
|
friend class basic_format_args<Context>;
|
||||||
|
|
||||||
|
unsigned long long get_types() const {
|
||||||
|
return detail::is_unpacked_bit | data_.size() |
|
||||||
|
(named_info_.empty()
|
||||||
|
? 0ULL
|
||||||
|
: static_cast<unsigned long long>(detail::has_named_args_bit));
|
||||||
|
}
|
||||||
|
|
||||||
|
const basic_format_arg<Context>* data() const {
|
||||||
|
return named_info_.empty() ? data_.data() : data_.data() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T> void emplace_arg(const T& arg) {
|
||||||
|
data_.emplace_back(detail::make_arg<Context>(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
|
||||||
|
if (named_info_.empty()) {
|
||||||
|
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
|
||||||
|
data_.insert(data_.begin(), {zero_ptr, 0});
|
||||||
|
}
|
||||||
|
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
|
||||||
|
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
|
||||||
|
data->pop_back();
|
||||||
|
};
|
||||||
|
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
|
||||||
|
guard{&data_, pop_one};
|
||||||
|
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
|
||||||
|
data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
|
||||||
|
guard.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr dynamic_format_arg_store() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Adds an argument into the dynamic store for later passing to a formatting
|
||||||
|
function.
|
||||||
|
|
||||||
|
Note that custom types and string types (but not string views) are copied
|
||||||
|
into the store dynamically allocating memory if necessary.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||||
|
store.push_back(42);
|
||||||
|
store.push_back("abc");
|
||||||
|
store.push_back(1.5f);
|
||||||
|
std::string result = fmt::vformat("{} and {} and {}", store);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename T> void push_back(const T& arg) {
|
||||||
|
if (detail::const_check(need_copy<T>::value))
|
||||||
|
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
|
||||||
|
else
|
||||||
|
emplace_arg(detail::unwrap(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Adds a reference to the argument into the dynamic store for later passing to
|
||||||
|
a formatting function.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||||
|
char band[] = "Rolling Stones";
|
||||||
|
store.push_back(std::cref(band));
|
||||||
|
band[9] = 'c'; // Changing str affects the output.
|
||||||
|
std::string result = fmt::vformat("{}", store);
|
||||||
|
// result == "Rolling Scones"
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename T> void push_back(std::reference_wrapper<T> arg) {
|
||||||
|
static_assert(
|
||||||
|
need_copy<T>::value,
|
||||||
|
"objects of built-in types and string views are always copied");
|
||||||
|
emplace_arg(arg.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Adds named argument into the dynamic store for later passing to a formatting
|
||||||
|
function. ``std::reference_wrapper`` is supported to avoid copying of the
|
||||||
|
argument. The name is always copied into the store.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void push_back(const detail::named_arg<char_type, T>& arg) {
|
||||||
|
const char_type* arg_name =
|
||||||
|
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
|
||||||
|
if (detail::const_check(need_copy<T>::value)) {
|
||||||
|
emplace_arg(
|
||||||
|
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
|
||||||
|
} else {
|
||||||
|
emplace_arg(fmt::arg(arg_name, arg.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Erase all elements from the store */
|
||||||
|
void clear() {
|
||||||
|
data_.clear();
|
||||||
|
named_info_.clear();
|
||||||
|
dynamic_args_ = detail::dynamic_arg_list();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Reserves space to store at least *new_cap* arguments including
|
||||||
|
*new_cap_named* named arguments.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
void reserve(size_t new_cap, size_t new_cap_named) {
|
||||||
|
FMT_ASSERT(new_cap >= new_cap_named,
|
||||||
|
"Set of arguments includes set of named arguments");
|
||||||
|
data_.reserve(new_cap);
|
||||||
|
named_info_.reserve(new_cap_named);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_ARGS_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -10,7 +10,15 @@
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
|
// __declspec(deprecated) is broken in some MSVC versions.
|
||||||
|
#if FMT_MSC_VER
|
||||||
|
# define FMT_DEPRECATED_NONMSVC
|
||||||
|
#else
|
||||||
|
# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED
|
||||||
|
#endif
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
|
|
||||||
enum class color : uint32_t {
|
enum class color : uint32_t {
|
||||||
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||||
|
@ -177,9 +185,13 @@ enum class terminal_color : uint8_t {
|
||||||
|
|
||||||
enum class emphasis : uint8_t {
|
enum class emphasis : uint8_t {
|
||||||
bold = 1,
|
bold = 1,
|
||||||
italic = 1 << 1,
|
faint = 1 << 1,
|
||||||
underline = 1 << 2,
|
italic = 1 << 2,
|
||||||
strikethrough = 1 << 3
|
underline = 1 << 3,
|
||||||
|
blink = 1 << 4,
|
||||||
|
reverse = 1 << 5,
|
||||||
|
conceal = 1 << 6,
|
||||||
|
strikethrough = 1 << 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
// rgb is a struct for red, green and blue colors.
|
// rgb is a struct for red, green and blue colors.
|
||||||
|
@ -198,7 +210,7 @@ struct rgb {
|
||||||
uint8_t b;
|
uint8_t b;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace detail {
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
|
|
||||||
// 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 {
|
||||||
|
@ -221,9 +233,10 @@ struct color_type {
|
||||||
uint32_t rgb_color;
|
uint32_t rgb_color;
|
||||||
} value;
|
} value;
|
||||||
};
|
};
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
// Experimental text formatting support.
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
|
/** A text style consisting of foreground and background colors and emphasis. */
|
||||||
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
|
||||||
|
@ -260,33 +273,14 @@ class text_style {
|
||||||
return lhs |= rhs;
|
return lhs |= rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
|
FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=(
|
||||||
if (!set_foreground_color) {
|
const text_style& rhs) {
|
||||||
set_foreground_color = rhs.set_foreground_color;
|
return and_assign(rhs);
|
||||||
foreground_color = rhs.foreground_color;
|
|
||||||
} else if (rhs.set_foreground_color) {
|
|
||||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
|
||||||
FMT_THROW(format_error("can't AND a terminal color"));
|
|
||||||
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!set_background_color) {
|
|
||||||
set_background_color = rhs.set_background_color;
|
|
||||||
background_color = rhs.background_color;
|
|
||||||
} else if (rhs.set_background_color) {
|
|
||||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
|
||||||
FMT_THROW(format_error("can't AND a terminal color"));
|
|
||||||
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
|
||||||
static_cast<uint8_t>(rhs.ems));
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
friend FMT_CONSTEXPR text_style operator&(text_style lhs,
|
FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style
|
||||||
const text_style& rhs) {
|
operator&(text_style lhs, const text_style& rhs) {
|
||||||
return lhs &= rhs;
|
return lhs.and_assign(rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
||||||
|
@ -326,8 +320,34 @@ class text_style {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEPRECATED!
|
||||||
|
FMT_CONSTEXPR text_style& and_assign(const text_style& rhs) {
|
||||||
|
if (!set_foreground_color) {
|
||||||
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
|
foreground_color = rhs.foreground_color;
|
||||||
|
} else if (rhs.set_foreground_color) {
|
||||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
|
FMT_THROW(format_error("can't AND a terminal color"));
|
||||||
|
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_background_color) {
|
||||||
|
set_background_color = rhs.set_background_color;
|
||||||
|
background_color = rhs.background_color;
|
||||||
|
} else if (rhs.set_background_color) {
|
||||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
|
FMT_THROW(format_error("can't AND a terminal color"));
|
||||||
|
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
||||||
|
static_cast<uint8_t>(rhs.ems));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
|
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
|
||||||
FMT_NOEXCEPT;
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
|
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
|
||||||
FMT_NOEXCEPT;
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
@ -338,19 +358,22 @@ class text_style {
|
||||||
emphasis ems;
|
emphasis ems;
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
|
/** Creates a text style from the foreground (text) color. */
|
||||||
return text_style(/*is_foreground=*/true, foreground);
|
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
|
||||||
|
return text_style(true, foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT {
|
/** Creates a text style from the background color. */
|
||||||
return text_style(/*is_foreground=*/false, background);
|
FMT_CONSTEXPR inline text_style bg(detail::color_type background) FMT_NOEXCEPT {
|
||||||
|
return text_style(false, background);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
|
FMT_CONSTEXPR inline text_style operator|(emphasis lhs,
|
||||||
|
emphasis rhs) FMT_NOEXCEPT {
|
||||||
return text_style(lhs) | rhs;
|
return text_style(lhs) | rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail {
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
|
|
||||||
template <typename Char> struct ansi_color_escape {
|
template <typename Char> struct ansi_color_escape {
|
||||||
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
|
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
|
||||||
|
@ -358,7 +381,7 @@ template <typename Char> struct ansi_color_escape {
|
||||||
// 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 == detail::data::background_color;
|
bool is_background = esc == string_view("\x1b[48;2;");
|
||||||
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.
|
||||||
|
@ -390,16 +413,18 @@ template <typename Char> struct ansi_color_escape {
|
||||||
buffer[19] = static_cast<Char>(0);
|
buffer[19] = static_cast<Char>(0);
|
||||||
}
|
}
|
||||||
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[num_emphases] = {};
|
||||||
uint8_t em_bits = static_cast<uint8_t>(em);
|
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
|
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
|
if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
|
if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
|
||||||
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
|
||||||
em_codes[3] = 9;
|
if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
|
||||||
|
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
|
||||||
|
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
|
||||||
|
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (size_t i = 0; i < num_emphases; ++i) {
|
||||||
if (!em_codes[i]) continue;
|
if (!em_codes[i]) continue;
|
||||||
buffer[index++] = static_cast<Char>('\x1b');
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
buffer[index++] = static_cast<Char>('[');
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
@ -411,12 +436,13 @@ template <typename Char> struct ansi_color_escape {
|
||||||
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
|
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
||||||
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
|
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT {
|
||||||
return buffer + std::char_traits<Char>::length(buffer);
|
return buffer + std::char_traits<Char>::length(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Char buffer[7u + 3u * 4u + 1u];
|
static constexpr size_t num_emphases = 8;
|
||||||
|
Char buffer[7u + 3u * num_emphases + 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 {
|
||||||
|
@ -425,18 +451,22 @@ template <typename Char> struct ansi_color_escape {
|
||||||
out[2] = static_cast<Char>('0' + c % 10);
|
out[2] = static_cast<Char>('0' + c % 10);
|
||||||
out[3] = static_cast<Char>(delimiter);
|
out[3] = static_cast<Char>(delimiter);
|
||||||
}
|
}
|
||||||
|
static FMT_CONSTEXPR bool has_emphasis(emphasis em,
|
||||||
|
emphasis mask) FMT_NOEXCEPT {
|
||||||
|
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
||||||
detail::color_type foreground) FMT_NOEXCEPT {
|
detail::color_type foreground) FMT_NOEXCEPT {
|
||||||
return ansi_color_escape<Char>(foreground, detail::data::foreground_color);
|
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
||||||
detail::color_type background) FMT_NOEXCEPT {
|
detail::color_type background) FMT_NOEXCEPT {
|
||||||
return ansi_color_escape<Char>(background, detail::data::background_color);
|
return ansi_color_escape<Char>(background, "\x1b[48;2;");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
|
@ -455,18 +485,17 @@ inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
||||||
fputs(detail::data::reset_color, stream);
|
fputs("\x1b[0m", stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
||||||
fputs(detail::data::wreset_color, stream);
|
fputs(L"\x1b[0m", stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
|
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||||
const char* begin = data::reset_color;
|
auto reset_color = string_view("\x1b[0m");
|
||||||
const char* end = begin + sizeof(data::reset_color) - 1;
|
buffer.append(reset_color.begin(), reset_color.end());
|
||||||
buffer.append(begin, end);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
|
@ -489,10 +518,11 @@ void vformat_to(buffer<Char>& buf, const text_style& ts,
|
||||||
auto background = detail::make_background_color<Char>(ts.get_background());
|
auto background = detail::make_background_color<Char>(ts.get_background());
|
||||||
buf.append(background.begin(), background.end());
|
buf.append(background.begin(), background.end());
|
||||||
}
|
}
|
||||||
detail::vformat_to(buf, format_str, args);
|
detail::vformat_to(buf, format_str, args, {});
|
||||||
if (has_style) detail::reset_color<Char>(buf);
|
if (has_style) detail::reset_color<Char>(buf);
|
||||||
}
|
}
|
||||||
} // namespace detail
|
|
||||||
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||||
|
@ -523,11 +553,15 @@ void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
\rst
|
||||||
Formats a string and prints it to stdout using ANSI escape sequences to
|
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||||
specify text formatting.
|
specify text formatting.
|
||||||
Example:
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
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);
|
||||||
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||||
|
@ -559,8 +593,8 @@ inline std::basic_string<Char> vformat(
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return vformat(ts, to_string_view(format_str),
|
return fmt::vformat(ts, to_string_view(format_str),
|
||||||
fmt::make_args_checked<Args...>(format_str, args...));
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -571,7 +605,7 @@ template <typename OutputIt, typename Char,
|
||||||
OutputIt vformat_to(
|
OutputIt vformat_to(
|
||||||
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
|
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
auto&& buf = detail::get_buffer<Char>(out);
|
||||||
detail::vformat_to(buf, ts, format_str, args);
|
detail::vformat_to(buf, ts, format_str, args);
|
||||||
return detail::get_iterator(buf);
|
return detail::get_iterator(buf);
|
||||||
}
|
}
|
||||||
|
@ -589,14 +623,16 @@ OutputIt vformat_to(
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char_t<S>>::value&&
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
|
||||||
detail::is_string<S>::value)>
|
detail::is_string<S>::value>
|
||||||
inline OutputIt format_to(OutputIt out, const text_style& ts,
|
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
|
||||||
const S& format_str, Args&&... args) {
|
Args&&... args) ->
|
||||||
|
typename std::enable_if<enable, OutputIt>::type {
|
||||||
return vformat_to(out, ts, to_string_view(format_str),
|
return vformat_to(out, ts, to_string_view(format_str),
|
||||||
fmt::make_args_checked<Args...>(format_str, args...));
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMT_MODULE_EXPORT_END
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_COLOR_H_
|
#endif // FMT_COLOR_H_
|
||||||
|
|
|
@ -8,13 +8,135 @@
|
||||||
#ifndef FMT_COMPILE_H_
|
#ifndef FMT_COMPILE_H_
|
||||||
#define FMT_COMPILE_H_
|
#define FMT_COMPILE_H_
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
// An output iterator that counts the number of objects written to it and
|
||||||
|
// discards them.
|
||||||
|
class counting_iterator {
|
||||||
|
private:
|
||||||
|
size_t count_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using iterator_category = std::output_iterator_tag;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = void;
|
||||||
|
using reference = void;
|
||||||
|
using _Unchecked_type = counting_iterator; // Mark iterator as checked.
|
||||||
|
|
||||||
|
struct value_type {
|
||||||
|
template <typename T> void operator=(const T&) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
counting_iterator() : count_(0) {}
|
||||||
|
|
||||||
|
size_t count() const { return count_; }
|
||||||
|
|
||||||
|
counting_iterator& operator++() {
|
||||||
|
++count_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
counting_iterator operator++(int) {
|
||||||
|
auto it = *this;
|
||||||
|
++*this;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend counting_iterator operator+(counting_iterator it, difference_type n) {
|
||||||
|
it.count_ += static_cast<size_t>(n);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type operator*() const { return {}; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename InputIt>
|
||||||
|
inline counting_iterator copy_str(InputIt begin, InputIt end,
|
||||||
|
counting_iterator it) {
|
||||||
|
return it + (end - begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt> class truncating_iterator_base {
|
||||||
|
protected:
|
||||||
|
OutputIt out_;
|
||||||
|
size_t limit_;
|
||||||
|
size_t count_ = 0;
|
||||||
|
|
||||||
|
truncating_iterator_base() : out_(), limit_(0) {}
|
||||||
|
|
||||||
|
truncating_iterator_base(OutputIt out, size_t limit)
|
||||||
|
: out_(out), limit_(limit) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using iterator_category = std::output_iterator_tag;
|
||||||
|
using value_type = typename std::iterator_traits<OutputIt>::value_type;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = void;
|
||||||
|
using reference = void;
|
||||||
|
using _Unchecked_type =
|
||||||
|
truncating_iterator_base; // Mark iterator as checked.
|
||||||
|
|
||||||
|
OutputIt base() const { return out_; }
|
||||||
|
size_t count() const { return count_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// An output iterator that truncates the output and counts the number of objects
|
||||||
|
// written to it.
|
||||||
|
template <typename OutputIt,
|
||||||
|
typename Enable = typename std::is_void<
|
||||||
|
typename std::iterator_traits<OutputIt>::value_type>::type>
|
||||||
|
class truncating_iterator;
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
class truncating_iterator<OutputIt, std::false_type>
|
||||||
|
: public truncating_iterator_base<OutputIt> {
|
||||||
|
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
|
||||||
|
|
||||||
|
truncating_iterator() = default;
|
||||||
|
|
||||||
|
truncating_iterator(OutputIt out, size_t limit)
|
||||||
|
: truncating_iterator_base<OutputIt>(out, limit) {}
|
||||||
|
|
||||||
|
truncating_iterator& operator++() {
|
||||||
|
if (this->count_++ < this->limit_) ++this->out_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
truncating_iterator operator++(int) {
|
||||||
|
auto it = *this;
|
||||||
|
++*this;
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_type& operator*() const {
|
||||||
|
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename OutputIt>
|
||||||
|
class truncating_iterator<OutputIt, std::true_type>
|
||||||
|
: public truncating_iterator_base<OutputIt> {
|
||||||
|
public:
|
||||||
|
truncating_iterator() = default;
|
||||||
|
|
||||||
|
truncating_iterator(OutputIt out, size_t limit)
|
||||||
|
: truncating_iterator_base<OutputIt>(out, limit) {}
|
||||||
|
|
||||||
|
template <typename T> truncating_iterator& operator=(T val) {
|
||||||
|
if (this->count_++ < this->limit_) *this->out_++ = val;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
truncating_iterator& operator++() { return *this; }
|
||||||
|
truncating_iterator& operator++(int) { return *this; }
|
||||||
|
truncating_iterator& operator*() { return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
// A compile-time string which is compiled into fast formatting code.
|
// A compile-time string which is compiled into fast formatting code.
|
||||||
class compiled_string {};
|
class compiled_string {};
|
||||||
|
|
||||||
|
@ -34,336 +156,30 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
||||||
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
|
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
|
# define FMT_COMPILE(s) \
|
||||||
|
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
|
||||||
|
#else
|
||||||
|
# define FMT_COMPILE(s) FMT_STRING(s)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||||
|
template <typename Char, size_t N,
|
||||||
|
fmt::detail_exported::fixed_string<Char, N> Str>
|
||||||
|
struct udl_compiled_string : compiled_string {
|
||||||
|
using char_type = Char;
|
||||||
|
constexpr operator basic_string_view<char_type>() const {
|
||||||
|
return {Str.data, N - 1};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
template <typename T, typename... Tail>
|
template <typename T, typename... Tail>
|
||||||
const T& first(const T& value, const Tail&...) {
|
const T& first(const T& value, const Tail&...) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Part of a compiled format string. It can be either literal text or a
|
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
// 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 int on_arg_id() { return ++num_parts, 0; }
|
|
||||||
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
|
|
||||||
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
|
|
||||||
return ++num_parts, 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* on_format_specs(int, 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 int on_arg_id() {
|
|
||||||
part_ = part::make_arg_index(parse_context_.next_arg_id());
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR int on_arg_id(int id) {
|
|
||||||
parse_context_.check_arg_id(id);
|
|
||||||
part_ = part::make_arg_index(id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
|
|
||||||
part_ = part::make_arg_name(id);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
|
|
||||||
part_.arg_id_end = ptr;
|
|
||||||
handler_(part_);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
|
|
||||||
const Char* end) {
|
|
||||||
auto repl = typename part::replacement();
|
|
||||||
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
|
|
||||||
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 OutputIt, typename Context, typename Id>
|
|
||||||
void format_arg(
|
|
||||||
basic_format_parse_context<typename Context::char_type>& parse_ctx,
|
|
||||||
Context& ctx, Id arg_id) {
|
|
||||||
ctx.advance_to(visit_format_arg(
|
|
||||||
arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx),
|
|
||||||
ctx.arg(arg_id)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// vformat_to is defined in a subnamespace to prevent ADL.
|
|
||||||
namespace cf {
|
|
||||||
template <typename Context, typename OutputIt, typename CompiledFormat>
|
|
||||||
auto vformat_to(OutputIt out, CompiledFormat& cf,
|
|
||||||
basic_format_args<Context> args) -> typename Context::iterator {
|
|
||||||
using char_type = typename Context::char_type;
|
|
||||||
basic_format_parse_context<char_type> parse_ctx(
|
|
||||||
to_string_view(cf.format_str_));
|
|
||||||
Context ctx(out, 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);
|
|
||||||
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case format_part_t::kind::arg_name:
|
|
||||||
advance_to(parse_ctx, part.arg_id_end);
|
|
||||||
detail::format_arg<OutputIt>(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<OutputIt, typename Context::char_type>(
|
|
||||||
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<detail::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>(
|
|
||||||
detail::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 OutputIt, typename CompiledFormat>
|
|
||||||
friend auto cf::vformat_to(OutputIt 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 {};
|
template <typename... Args> struct type_list {};
|
||||||
|
|
||||||
// Returns a reference to the argument at index N from [first, rest...].
|
// Returns a reference to the argument at index N from [first, rest...].
|
||||||
|
@ -374,13 +190,20 @@ constexpr const auto& get([[maybe_unused]] const T& first,
|
||||||
if constexpr (N == 0)
|
if constexpr (N == 0)
|
||||||
return first;
|
return first;
|
||||||
else
|
else
|
||||||
return get<N - 1>(rest...);
|
return detail::get<N - 1>(rest...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename... Args>
|
||||||
|
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
|
||||||
|
type_list<Args...>) {
|
||||||
|
return get_arg_index_by_name<Args...>(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <int N, typename> struct get_type_impl;
|
template <int N, typename> struct get_type_impl;
|
||||||
|
|
||||||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
||||||
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>;
|
using type =
|
||||||
|
remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <int N, typename T>
|
template <int N, typename T>
|
||||||
|
@ -393,7 +216,7 @@ template <typename Char> struct text {
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... Args>
|
||||||
OutputIt format(OutputIt out, const Args&...) const {
|
constexpr OutputIt format(OutputIt out, const Args&...) const {
|
||||||
return write<Char>(out, data);
|
return write<Char>(out, data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -412,11 +235,22 @@ template <typename Char> struct code_unit {
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... Args>
|
||||||
OutputIt format(OutputIt out, const Args&...) const {
|
constexpr OutputIt format(OutputIt out, const Args&...) const {
|
||||||
return write<Char>(out, value);
|
return write<Char>(out, value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This ensures that the argument type is convertible to `const T&`.
|
||||||
|
template <typename T, int N, typename... Args>
|
||||||
|
constexpr const T& get_arg_checked(const Args&... args) {
|
||||||
|
const auto& arg = detail::get<N>(args...);
|
||||||
|
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
|
||||||
|
return arg.value;
|
||||||
|
} else {
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct is_compiled_format<code_unit<Char>> : std::true_type {};
|
struct is_compiled_format<code_unit<Char>> : std::true_type {};
|
||||||
|
|
||||||
|
@ -425,29 +259,58 @@ template <typename Char, typename T, int N> struct field {
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... Args>
|
||||||
OutputIt format(OutputIt out, const Args&... args) const {
|
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
// This ensures that the argument type is convertile to `const T&`.
|
return write<Char>(out, get_arg_checked<T, N>(args...));
|
||||||
const T& arg = get<N>(args...);
|
|
||||||
return write<Char>(out, arg);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename T, int N>
|
template <typename Char, typename T, int N>
|
||||||
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
||||||
|
|
||||||
|
// A replacement field that refers to argument with name.
|
||||||
|
template <typename Char> struct runtime_named_field {
|
||||||
|
using char_type = Char;
|
||||||
|
basic_string_view<Char> name;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename T>
|
||||||
|
constexpr static bool try_format_argument(
|
||||||
|
OutputIt& out,
|
||||||
|
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
|
||||||
|
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
|
||||||
|
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
|
||||||
|
if (arg_name == arg.name) {
|
||||||
|
out = write<Char>(out, arg.value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename... Args>
|
||||||
|
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
|
bool found = (try_format_argument(out, name, args) || ...);
|
||||||
|
if (!found) {
|
||||||
|
FMT_THROW(format_error("argument with specified name is not found"));
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
|
||||||
|
|
||||||
// A replacement field that refers to argument N and has format specifiers.
|
// A replacement field that refers to argument N and has format specifiers.
|
||||||
template <typename Char, typename T, int N> struct spec_field {
|
template <typename Char, typename T, int N> struct spec_field {
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
mutable formatter<T, Char> fmt;
|
formatter<T, Char> fmt;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... Args>
|
||||||
OutputIt format(OutputIt out, const Args&... args) const {
|
constexpr FMT_INLINE OutputIt format(OutputIt out,
|
||||||
// This ensures that the argument type is convertile to `const T&`.
|
const Args&... args) const {
|
||||||
const T& arg = get<N>(args...);
|
|
||||||
const auto& vargs =
|
const auto& vargs =
|
||||||
make_format_args<basic_format_context<OutputIt, Char>>(args...);
|
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
|
||||||
basic_format_context<OutputIt, Char> ctx(out, vargs);
|
basic_format_context<OutputIt, Char> ctx(out, vargs);
|
||||||
return fmt.format(arg, ctx);
|
return fmt.format(get_arg_checked<T, N>(args...), ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -460,7 +323,7 @@ template <typename L, typename R> struct concat {
|
||||||
using char_type = typename L::char_type;
|
using char_type = typename L::char_type;
|
||||||
|
|
||||||
template <typename OutputIt, typename... Args>
|
template <typename OutputIt, typename... Args>
|
||||||
OutputIt format(OutputIt out, const Args&... args) const {
|
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
out = lhs.format(out, args...);
|
out = lhs.format(out, args...);
|
||||||
return rhs.format(out, args...);
|
return rhs.format(out, args...);
|
||||||
}
|
}
|
||||||
|
@ -508,14 +371,79 @@ template <typename T, typename Char> struct parse_specs_result {
|
||||||
int next_arg_id;
|
int next_arg_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr int manual_indexing_id = -1;
|
||||||
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||||
size_t pos, int arg_id) {
|
size_t pos, int next_arg_id) {
|
||||||
str.remove_prefix(pos);
|
str.remove_prefix(pos);
|
||||||
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1);
|
auto ctx = basic_format_parse_context<Char>(str, {}, next_arg_id);
|
||||||
auto f = formatter<T, Char>();
|
auto f = formatter<T, Char>();
|
||||||
auto end = f.parse(ctx);
|
auto end = f.parse(ctx);
|
||||||
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()};
|
return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1,
|
||||||
|
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char> struct arg_id_handler {
|
||||||
|
arg_ref<Char> arg_id;
|
||||||
|
|
||||||
|
constexpr int operator()() {
|
||||||
|
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
constexpr int operator()(int id) {
|
||||||
|
arg_id = arg_ref<Char>(id);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
constexpr int operator()(basic_string_view<Char> id) {
|
||||||
|
arg_id = arg_ref<Char>(id);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void on_error(const char* message) {
|
||||||
|
FMT_THROW(format_error(message));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char> struct parse_arg_id_result {
|
||||||
|
arg_ref<Char> arg_id;
|
||||||
|
const Char* arg_id_end;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <int ID, typename Char>
|
||||||
|
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
|
||||||
|
auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
|
||||||
|
auto arg_id_end = parse_arg_id(begin, end, handler);
|
||||||
|
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void> struct field_type {
|
||||||
|
using type = remove_cvref_t<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
|
||||||
|
using type = remove_cvref_t<decltype(T::value)>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
|
||||||
|
typename S>
|
||||||
|
constexpr auto parse_replacement_field_then_tail(S format_str) {
|
||||||
|
using char_type = typename S::char_type;
|
||||||
|
constexpr auto str = basic_string_view<char_type>(format_str);
|
||||||
|
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
|
||||||
|
if constexpr (c == '}') {
|
||||||
|
return parse_tail<Args, END_POS + 1, NEXT_ID>(
|
||||||
|
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
|
||||||
|
format_str);
|
||||||
|
} else if constexpr (c == ':') {
|
||||||
|
constexpr auto result = parse_specs<typename field_type<T>::type>(
|
||||||
|
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
|
||||||
|
return parse_tail<Args, result.end, result.next_arg_id>(
|
||||||
|
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
|
||||||
|
result.fmt},
|
||||||
|
format_str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compiles a non-empty format string and returns the compiled representation
|
// Compiles a non-empty format string and returns the compiled representation
|
||||||
|
@ -523,27 +451,59 @@ constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||||
template <typename Args, size_t POS, int ID, typename S>
|
template <typename Args, size_t POS, int ID, typename S>
|
||||||
constexpr auto compile_format_string(S format_str) {
|
constexpr auto compile_format_string(S format_str) {
|
||||||
using char_type = typename S::char_type;
|
using char_type = typename S::char_type;
|
||||||
constexpr basic_string_view<char_type> str = format_str;
|
constexpr auto str = basic_string_view<char_type>(format_str);
|
||||||
if constexpr (str[POS] == '{') {
|
if constexpr (str[POS] == '{') {
|
||||||
if (POS + 1 == str.size())
|
if constexpr (POS + 1 == str.size())
|
||||||
throw format_error("unmatched '{' in format string");
|
FMT_THROW(format_error("unmatched '{' in format string"));
|
||||||
if constexpr (str[POS + 1] == '{') {
|
if constexpr (str[POS + 1] == '{') {
|
||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
} else if constexpr (str[POS + 1] == '}') {
|
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
|
||||||
using type = get_type<ID, Args>;
|
static_assert(ID != manual_indexing_id,
|
||||||
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
|
"cannot switch from manual to automatic argument indexing");
|
||||||
format_str);
|
constexpr auto next_id =
|
||||||
} else if constexpr (str[POS + 1] == ':') {
|
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
|
||||||
using type = get_type<ID, Args>;
|
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
|
||||||
constexpr auto result = parse_specs<type>(str, POS + 2, ID);
|
POS + 1, ID, next_id>(
|
||||||
return parse_tail<Args, result.end, result.next_arg_id>(
|
format_str);
|
||||||
spec_field<char_type, type, ID>{result.fmt}, format_str);
|
|
||||||
} else {
|
} else {
|
||||||
return unknown_format();
|
constexpr auto arg_id_result =
|
||||||
|
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
|
||||||
|
constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
|
||||||
|
constexpr char_type c =
|
||||||
|
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
|
||||||
|
static_assert(c == '}' || c == ':', "missing '}' in format string");
|
||||||
|
if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
|
||||||
|
static_assert(
|
||||||
|
ID == manual_indexing_id || ID == 0,
|
||||||
|
"cannot switch from automatic to manual argument indexing");
|
||||||
|
constexpr auto arg_index = arg_id_result.arg_id.val.index;
|
||||||
|
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
|
||||||
|
Args, arg_id_end_pos,
|
||||||
|
arg_index, manual_indexing_id>(
|
||||||
|
format_str);
|
||||||
|
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
|
||||||
|
constexpr auto arg_index =
|
||||||
|
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
|
||||||
|
if constexpr (arg_index != invalid_arg_index) {
|
||||||
|
constexpr auto next_id =
|
||||||
|
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
|
||||||
|
return parse_replacement_field_then_tail<
|
||||||
|
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
|
||||||
|
arg_index, next_id>(format_str);
|
||||||
|
} else {
|
||||||
|
if constexpr (c == '}') {
|
||||||
|
return parse_tail<Args, arg_id_end_pos + 1, ID>(
|
||||||
|
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
|
||||||
|
format_str);
|
||||||
|
} else if constexpr (c == ':') {
|
||||||
|
return unknown_format(); // no type info for specs parsing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if constexpr (str[POS] == '}') {
|
} else if constexpr (str[POS] == '}') {
|
||||||
if (POS + 1 == str.size())
|
if constexpr (POS + 1 == str.size())
|
||||||
throw format_error("unmatched '}' in format string");
|
FMT_THROW(format_error("unmatched '}' in format string"));
|
||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
} else {
|
} else {
|
||||||
constexpr auto end = parse_text(str, POS + 1);
|
constexpr auto end = parse_text(str, POS + 1);
|
||||||
|
@ -558,143 +518,125 @@ constexpr auto compile_format_string(S format_str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args, typename S,
|
template <typename... Args, typename S,
|
||||||
FMT_ENABLE_IF(is_compile_string<S>::value ||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
detail::is_compiled_string<S>::value)>
|
|
||||||
constexpr auto compile(S format_str) {
|
constexpr auto compile(S format_str) {
|
||||||
constexpr basic_string_view<typename S::char_type> str = format_str;
|
constexpr auto str = basic_string_view<typename S::char_type>(format_str);
|
||||||
if constexpr (str.size() == 0) {
|
if constexpr (str.size() == 0) {
|
||||||
return detail::make_text(str, 0, 0);
|
return detail::make_text(str, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
constexpr auto result =
|
constexpr auto result =
|
||||||
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
|
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
|
||||||
format_str);
|
format_str);
|
||||||
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
|
return result;
|
||||||
detail::unknown_format>()) {
|
|
||||||
return detail::compiled_format<S, Args...>(to_string_view(format_str));
|
|
||||||
} else {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
template <typename... Args, typename S,
|
|
||||||
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
|
||||||
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
|
|
||||||
return detail::compiled_format<S, Args...>(to_string_view(format_str));
|
|
||||||
}
|
|
||||||
#endif // __cpp_if_constexpr
|
|
||||||
|
|
||||||
// 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])
|
|
||||||
-> detail::compiled_format<const Char*, Args...> {
|
|
||||||
return detail::compiled_format<const Char*, Args...>(
|
|
||||||
basic_string_view<Char>(format_str, N - 1));
|
|
||||||
}
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
// DEPRECATED! use FMT_COMPILE instead.
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
template <typename... Args>
|
|
||||||
FMT_DEPRECATED auto compile(const Args&... args)
|
|
||||||
-> decltype(detail::compile(args...)) {
|
|
||||||
return detail::compile(args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FMT_USE_CONSTEXPR
|
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||||
# ifdef __cpp_if_constexpr
|
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args,
|
template <typename CompiledFormat, typename... Args,
|
||||||
typename Char = typename CompiledFormat::char_type,
|
typename Char = typename CompiledFormat::char_type,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||||
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
auto s = std::basic_string<Char>();
|
||||||
cf.format(detail::buffer_appender<Char>(buffer), args...);
|
cf.format(std::back_inserter(s), args...);
|
||||||
return to_string(buffer);
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return cf.format(out, args...);
|
return cf.format(out, args...);
|
||||||
}
|
}
|
||||||
# endif // __cpp_if_constexpr
|
|
||||||
#endif // FMT_USE_CONSTEXPR
|
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args,
|
|
||||||
typename Char = typename CompiledFormat::char_type,
|
|
||||||
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
|
|
||||||
CompiledFormat>::value)>
|
|
||||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
using context = buffer_context<Char>;
|
|
||||||
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
|
|
||||||
make_format_args<context>(args...));
|
|
||||||
return to_string(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||||
Args&&... args) {
|
Args&&... args) {
|
||||||
#ifdef __cpp_if_constexpr
|
|
||||||
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||||
constexpr basic_string_view<typename S::char_type> str = S();
|
constexpr auto str = basic_string_view<typename S::char_type>(S());
|
||||||
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
|
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
|
||||||
return fmt::to_string(detail::first(args...));
|
const auto& first = detail::first(args...);
|
||||||
|
if constexpr (detail::is_named_arg<
|
||||||
|
remove_cvref_t<decltype(first)>>::value) {
|
||||||
|
return fmt::to_string(first.value);
|
||||||
|
} else {
|
||||||
|
return fmt::to_string(first);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
|
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||||
|
detail::unknown_format>()) {
|
||||||
|
return format(static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
} else {
|
||||||
|
return format(compiled, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
|
||||||
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
|
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||||
|
detail::unknown_format>()) {
|
||||||
|
return format_to(out,
|
||||||
|
static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
} else {
|
||||||
|
return format_to(out, compiled, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
constexpr auto compiled = detail::compile<Args...>(S());
|
|
||||||
return format(compiled, std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
|
||||||
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
|
|
||||||
CompiledFormat>::value)>
|
|
||||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
|
||||||
const Args&... args) {
|
|
||||||
using char_type = typename CompiledFormat::char_type;
|
|
||||||
using context = format_context_t<OutputIt, char_type>;
|
|
||||||
return detail::cf::vformat_to<context>(out, cf,
|
|
||||||
make_format_args<context>(args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
OutputIt format_to(OutputIt out, const S&, const Args&... args) {
|
|
||||||
constexpr auto compiled = detail::compile<Args...>(S());
|
|
||||||
return format_to(out, compiled, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<
|
|
||||||
OutputIt, typename CompiledFormat::char_type>::value&&
|
|
||||||
std::is_base_of<detail::basic_compiled_format,
|
|
||||||
CompiledFormat>::value)>
|
|
||||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||||
const CompiledFormat& cf,
|
const S& format_str, Args&&... args) {
|
||||||
const Args&... args) {
|
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str,
|
||||||
auto it =
|
std::forward<Args>(args)...);
|
||||||
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
|
|
||||||
return {it.base(), it.count()};
|
return {it.base(), it.count()};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&,
|
size_t formatted_size(const S& format_str, const Args&... args) {
|
||||||
const Args&... args) {
|
return format_to(detail::counting_iterator(), format_str, args...).count();
|
||||||
constexpr auto compiled = detail::compile<Args...>(S());
|
|
||||||
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled,
|
|
||||||
args...);
|
|
||||||
return {it.base(), it.count()};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args>
|
template <typename S, typename... Args,
|
||||||
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
return format_to(detail::counting_iterator(), cf, args...).count();
|
void print(std::FILE* f, const S& format_str, const Args&... args) {
|
||||||
|
memory_buffer buffer;
|
||||||
|
format_to(std::back_inserter(buffer), format_str, args...);
|
||||||
|
detail::print(f, {buffer.data(), buffer.size()});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
void print(const S& format_str, const Args&... args) {
|
||||||
|
print(stdout, format_str, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||||
|
inline namespace literals {
|
||||||
|
template <detail_exported::fixed_string Str>
|
||||||
|
constexpr detail::udl_compiled_string<
|
||||||
|
remove_cvref_t<decltype(Str.data[0])>,
|
||||||
|
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str>
|
||||||
|
operator""_cf() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
} // namespace literals
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_MODULE_EXPORT_END
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_COMPILE_H_
|
#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
|
@ -1,64 +1,2 @@
|
||||||
// Formatting library for C++ - std::locale support
|
#include "xchar.h"
|
||||||
//
|
#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead
|
||||||
// Copyright (c) 2012 - present, Victor Zverovich
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
|
|
||||||
#ifndef FMT_LOCALE_H_
|
|
||||||
#define FMT_LOCALE_H_
|
|
||||||
|
|
||||||
#include <locale>
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
template <typename Char>
|
|
||||||
std::basic_string<Char> vformat(
|
|
||||||
const std::locale& loc, basic_string_view<Char> format_str,
|
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
|
|
||||||
return fmt::to_string(buffer);
|
|
||||||
}
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
|
||||||
inline std::basic_string<Char> vformat(
|
|
||||||
const std::locale& loc, const S& format_str,
|
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
||||||
return detail::vformat(loc, to_string_view(format_str), args);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
|
||||||
inline std::basic_string<Char> format(const std::locale& loc,
|
|
||||||
const S& format_str, Args&&... args) {
|
|
||||||
return detail::vformat(loc, to_string_view(format_str),
|
|
||||||
fmt::make_args_checked<Args...>(format_str, args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename S, typename OutputIt, typename... Args,
|
|
||||||
typename Char = char_t<S>,
|
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
|
||||||
inline OutputIt vformat_to(
|
|
||||||
OutputIt out, const std::locale& loc, const S& format_str,
|
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
||||||
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
|
||||||
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
|
|
||||||
return detail::get_iterator(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
|
||||||
typename Char = char_t<S>,
|
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
|
||||||
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
|
||||||
const S& format_str, Args&&... args) {
|
|
||||||
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
|
||||||
return vformat_to(out, loc, to_string_view(format_str), vargs);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
|
|
||||||
#endif // FMT_LOCALE_H_
|
|
||||||
|
|
|
@ -8,16 +8,12 @@
|
||||||
#ifndef FMT_OS_H_
|
#ifndef FMT_OS_H_
|
||||||
#define FMT_OS_H_
|
#define FMT_OS_H_
|
||||||
|
|
||||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
|
||||||
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
|
|
||||||
# undef __STRICT_ANSI__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <clocale> // for locale_t
|
#include <clocale> // locale_t
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstdlib> // for strtod_l
|
#include <cstdlib> // strtod_l
|
||||||
|
#include <system_error> // std::system_error
|
||||||
|
|
||||||
#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
|
||||||
|
@ -25,17 +21,20 @@
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
|
#ifndef FMT_USE_FCNTL
|
||||||
// UWP doesn't provide _pipe.
|
// UWP doesn't provide _pipe.
|
||||||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
# if FMT_HAS_INCLUDE("winapifamily.h")
|
||||||
# include <winapifamily.h>
|
# include <winapifamily.h>
|
||||||
#endif
|
# endif
|
||||||
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||||
defined(__linux__)) && \
|
defined(__linux__)) && \
|
||||||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
(!defined(WINAPI_FAMILY) || \
|
||||||
# include <fcntl.h> // for O_RDONLY
|
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||||
# define FMT_USE_FCNTL 1
|
# include <fcntl.h> // for O_RDONLY
|
||||||
#else
|
# define FMT_USE_FCNTL 1
|
||||||
# define FMT_USE_FCNTL 0
|
# else
|
||||||
|
# define FMT_USE_FCNTL 0
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_POSIX
|
#ifndef FMT_POSIX
|
||||||
|
@ -74,6 +73,7 @@
|
||||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
|
@ -122,19 +122,28 @@ template <typename Char> class basic_cstring_view {
|
||||||
using cstring_view = basic_cstring_view<char>;
|
using cstring_view = basic_cstring_view<char>;
|
||||||
using wcstring_view = basic_cstring_view<wchar_t>;
|
using wcstring_view = basic_cstring_view<wchar_t>;
|
||||||
|
|
||||||
// An error code.
|
template <typename Char> struct formatter<std::error_code, Char> {
|
||||||
class error_code {
|
template <typename ParseContext>
|
||||||
private:
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
int value_;
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
template <typename FormatContext>
|
||||||
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
int get() const FMT_NOEXCEPT { return value_; }
|
auto out = ctx.out();
|
||||||
|
out = detail::write_bytes(out, ec.category().name(),
|
||||||
|
basic_format_specs<Char>());
|
||||||
|
out = detail::write<Char>(out, Char(':'));
|
||||||
|
out = detail::write<Char>(out, ec.value());
|
||||||
|
return out;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
namespace detail {
|
FMT_API const std::error_category& system_category() FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
// A converter from UTF-16 to UTF-8.
|
// A converter from UTF-16 to UTF-8.
|
||||||
// It is only provided for Windows since other systems support UTF-8 natively.
|
// It is only provided for Windows since other systems support UTF-8 natively.
|
||||||
class utf16_to_utf8 {
|
class utf16_to_utf8 {
|
||||||
|
@ -143,7 +152,7 @@ class utf16_to_utf8 {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
utf16_to_utf8() {}
|
utf16_to_utf8() {}
|
||||||
FMT_API explicit utf16_to_utf8(wstring_view s);
|
FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
|
||||||
operator string_view() const { return string_view(&buffer_[0], size()); }
|
operator string_view() const { return string_view(&buffer_[0], size()); }
|
||||||
size_t size() const { return buffer_.size() - 1; }
|
size_t size() const { return buffer_.size() - 1; }
|
||||||
const char* c_str() const { return &buffer_[0]; }
|
const char* c_str() const { return &buffer_[0]; }
|
||||||
|
@ -152,59 +161,68 @@ class utf16_to_utf8 {
|
||||||
// Performs conversion returning a system error code instead of
|
// Performs conversion returning a system error code instead of
|
||||||
// throwing exception on conversion error. This method may still throw
|
// throwing exception on conversion error. This method may still throw
|
||||||
// in case of memory allocation error.
|
// in case of memory allocation error.
|
||||||
FMT_API int convert(wstring_view s);
|
FMT_API int convert(basic_string_view<wchar_t> s);
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_API void format_windows_error(buffer<char>& out, int error_code,
|
FMT_API void format_windows_error(buffer<char>& out, int error_code,
|
||||||
string_view message) FMT_NOEXCEPT;
|
const char* message) FMT_NOEXCEPT;
|
||||||
} // namespace detail
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
/** A Windows error. */
|
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
|
||||||
class windows_error : public system_error {
|
format_args args);
|
||||||
private:
|
|
||||||
FMT_API void init(int error_code, string_view format_str, format_args args);
|
|
||||||
|
|
||||||
public:
|
/**
|
||||||
/**
|
\rst
|
||||||
\rst
|
Constructs a :class:`std::system_error` object with the description
|
||||||
Constructs a :class:`fmt::windows_error` object with the description
|
of the form
|
||||||
of the form
|
|
||||||
|
|
||||||
.. parsed-literal::
|
.. parsed-literal::
|
||||||
*<message>*: *<system-message>*
|
*<message>*: *<system-message>*
|
||||||
|
|
||||||
where *<message>* is the formatted message and *<system-message>* is the
|
where *<message>* is the formatted message and *<system-message>* is the
|
||||||
system message corresponding to the error code.
|
system message corresponding to the error code.
|
||||||
*error_code* is a Windows error code as given by ``GetLastError``.
|
*error_code* is a Windows error code as given by ``GetLastError``.
|
||||||
If *error_code* is not a valid error code such as -1, the system message
|
If *error_code* is not a valid error code such as -1, the system message
|
||||||
will look like "error -1".
|
will look like "error -1".
|
||||||
|
|
||||||
**Example**::
|
**Example**::
|
||||||
|
|
||||||
// This throws a windows_error with the description
|
// This throws a system_error with the description
|
||||||
// cannot open file 'madeup': The system cannot find the file specified.
|
// cannot open file 'madeup': The system cannot find the file specified.
|
||||||
// or similar (system message may vary).
|
// or similar (system message may vary).
|
||||||
const char *filename = "madeup";
|
const char *filename = "madeup";
|
||||||
LPOFSTRUCT of = LPOFSTRUCT();
|
LPOFSTRUCT of = LPOFSTRUCT();
|
||||||
HFILE file = OpenFile(filename, &of, OF_READ);
|
HFILE file = OpenFile(filename, &of, OF_READ);
|
||||||
if (file == HFILE_ERROR) {
|
if (file == HFILE_ERROR) {
|
||||||
throw fmt::windows_error(GetLastError(),
|
throw fmt::windows_error(GetLastError(),
|
||||||
"cannot open file '{}'", filename);
|
"cannot open file '{}'", filename);
|
||||||
}
|
}
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
windows_error(int error_code, string_view message, const Args&... args) {
|
std::system_error windows_error(int error_code, string_view message,
|
||||||
init(error_code, message, make_format_args(args...));
|
const Args&... args) {
|
||||||
}
|
return vwindows_error(error_code, message, fmt::make_format_args(args...));
|
||||||
};
|
}
|
||||||
|
|
||||||
// Reports a Windows error without throwing an exception.
|
// Reports a Windows error without throwing an exception.
|
||||||
// Can be used to report errors from destructors.
|
// Can be used to report errors from destructors.
|
||||||
FMT_API void report_windows_error(int error_code,
|
FMT_API void report_windows_error(int error_code,
|
||||||
string_view message) FMT_NOEXCEPT;
|
const char* message) FMT_NOEXCEPT;
|
||||||
|
#else
|
||||||
|
inline const std::error_category& system_category() FMT_NOEXCEPT {
|
||||||
|
return std::system_category();
|
||||||
|
}
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
|
// std::system is not available on some platforms such as iOS (#2248).
|
||||||
|
#ifdef __OSX__
|
||||||
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
|
void say(const S& format_str, Args&&... args) {
|
||||||
|
std::system(format("say \"{}\"", format(format_str, args...)).c_str());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// A buffered file.
|
// A buffered file.
|
||||||
class buffered_file {
|
class buffered_file {
|
||||||
private:
|
private:
|
||||||
|
@ -255,7 +273,7 @@ class buffered_file {
|
||||||
|
|
||||||
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, fmt::make_format_args(args...));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -280,7 +298,8 @@ class file {
|
||||||
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.
|
||||||
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
|
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
|
||||||
APPEND = FMT_POSIX(O_APPEND) // Open in append mode.
|
APPEND = FMT_POSIX(O_APPEND), // Open in append mode.
|
||||||
|
TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file.
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constructs a file object which doesn't represent any file.
|
// Constructs a file object which doesn't represent any file.
|
||||||
|
@ -295,7 +314,8 @@ class file {
|
||||||
|
|
||||||
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
|
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
|
||||||
|
|
||||||
file& operator=(file&& other) FMT_NOEXCEPT {
|
// Move assignment is not noexcept because close may throw.
|
||||||
|
file& operator=(file&& other) {
|
||||||
close();
|
close();
|
||||||
fd_ = other.fd_;
|
fd_ = other.fd_;
|
||||||
other.fd_ = -1;
|
other.fd_ = -1;
|
||||||
|
@ -331,7 +351,7 @@ 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, std::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.
|
||||||
|
@ -345,9 +365,10 @@ class file {
|
||||||
// Returns the memory page size.
|
// Returns the memory page size.
|
||||||
long getpagesize();
|
long getpagesize();
|
||||||
|
|
||||||
namespace detail {
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
|
|
||||||
struct buffer_size {
|
struct buffer_size {
|
||||||
|
buffer_size() = default;
|
||||||
size_t value = 0;
|
size_t value = 0;
|
||||||
buffer_size operator=(size_t val) const {
|
buffer_size operator=(size_t val) const {
|
||||||
auto bs = buffer_size();
|
auto bs = buffer_size();
|
||||||
|
@ -357,14 +378,14 @@ struct buffer_size {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ostream_params {
|
struct ostream_params {
|
||||||
int oflag = file::WRONLY | file::CREATE;
|
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
|
||||||
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
|
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
|
||||||
|
|
||||||
ostream_params() {}
|
ostream_params() {}
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
ostream_params(T... params, int oflag) : ostream_params(params...) {
|
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
|
||||||
this->oflag = oflag;
|
oflag = new_oflag;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
|
@ -372,23 +393,27 @@ struct ostream_params {
|
||||||
: ostream_params(params...) {
|
: ostream_params(params...) {
|
||||||
this->buffer_size = bs.value;
|
this->buffer_size = bs.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Intel has a bug that results in failure to deduce a constructor
|
||||||
|
// for empty parameter packs.
|
||||||
|
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
|
||||||
|
ostream_params(int new_oflag) : oflag(new_oflag) {}
|
||||||
|
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
|
||||||
|
# endif
|
||||||
};
|
};
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
static constexpr detail::buffer_size buffer_size;
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
// A fast output stream which is not thread-safe.
|
// Added {} below to work around default constructor error known to
|
||||||
class ostream final : private detail::buffer<char> {
|
// occur in Xcode versions 7.2.1 and 8.2.1.
|
||||||
|
constexpr detail::buffer_size buffer_size{};
|
||||||
|
|
||||||
|
/** A fast output stream which is not thread-safe. */
|
||||||
|
class FMT_API ostream final : private detail::buffer<char> {
|
||||||
private:
|
private:
|
||||||
file file_;
|
file file_;
|
||||||
|
|
||||||
void flush() {
|
void grow(size_t) override;
|
||||||
if (size() == 0) return;
|
|
||||||
file_.write(data(), size());
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void grow(size_t) final;
|
|
||||||
|
|
||||||
ostream(cstring_view path, const detail::ostream_params& params)
|
ostream(cstring_view path, const detail::ostream_params& params)
|
||||||
: file_(path, params.oflag) {
|
: file_(path, params.oflag) {
|
||||||
|
@ -399,6 +424,7 @@ class ostream final : private detail::buffer<char> {
|
||||||
ostream(ostream&& other)
|
ostream(ostream&& other)
|
||||||
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
|
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
|
||||||
file_(std::move(other.file_)) {
|
file_(std::move(other.file_)) {
|
||||||
|
other.clear();
|
||||||
other.set(nullptr, 0);
|
other.set(nullptr, 0);
|
||||||
}
|
}
|
||||||
~ostream() {
|
~ostream() {
|
||||||
|
@ -406,6 +432,12 @@ class ostream final : private detail::buffer<char> {
|
||||||
delete[] data();
|
delete[] data();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void flush() {
|
||||||
|
if (size() == 0) return;
|
||||||
|
file_.write(data(), size());
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
friend ostream output_file(cstring_view path, T... params);
|
friend ostream output_file(cstring_view path, T... params);
|
||||||
|
|
||||||
|
@ -414,16 +446,30 @@ class ostream final : private detail::buffer<char> {
|
||||||
file_.close();
|
file_.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename... Args>
|
/**
|
||||||
void print(const S& format_str, const Args&... args) {
|
Formats ``args`` according to specifications in ``fmt`` and writes the
|
||||||
format_to(detail::buffer_appender<char>(*this), format_str, args...);
|
output to the file.
|
||||||
|
*/
|
||||||
|
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
|
||||||
|
vformat_to(detail::buffer_appender<char>(*this), fmt,
|
||||||
|
fmt::make_format_args(args...));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Opens a file for writing. Supported parameters passed in `params`:
|
\rst
|
||||||
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
|
Opens a file for writing. Supported parameters passed in *params*:
|
||||||
|
|
||||||
|
* ``<integer>``: Flags passed to `open
|
||||||
|
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
|
||||||
|
(``file::WRONLY | file::CREATE`` by default)
|
||||||
* ``buffer_size=<integer>``: Output buffer size
|
* ``buffer_size=<integer>``: Output buffer size
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
auto out = fmt::output_file("guide.txt");
|
||||||
|
out.print("Don't {}", "Panic");
|
||||||
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
inline ostream output_file(cstring_view path, T... params) {
|
inline ostream output_file(cstring_view path, T... params) {
|
||||||
|
@ -466,7 +512,7 @@ class 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 {
|
FMT_DEPRECATED double strtod(const char*& str) const {
|
||||||
char* end = nullptr;
|
char* end = nullptr;
|
||||||
double result = strtod_l(str, &end, locale_);
|
double result = strtod_l(str, &end, locale_);
|
||||||
str = end;
|
str = end;
|
||||||
|
@ -475,6 +521,7 @@ class locale {
|
||||||
};
|
};
|
||||||
using Locale FMT_DEPRECATED_ALIAS = locale;
|
using Locale FMT_DEPRECATED_ALIAS = locale;
|
||||||
#endif // FMT_LOCALE
|
#endif // FMT_LOCALE
|
||||||
|
FMT_MODULE_EXPORT_END
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_OS_H_
|
#endif // FMT_OS_H_
|
||||||
|
|
|
@ -14,81 +14,44 @@
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
template <typename Char> class basic_printf_parse_context;
|
|
||||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
// Checks if T has a user-defined operator<<.
|
||||||
private:
|
template <typename T, typename Char, typename Enable = void>
|
||||||
using int_type = typename std::basic_streambuf<Char>::int_type;
|
class is_streamable {
|
||||||
using traits_type = typename std::basic_streambuf<Char>::traits_type;
|
|
||||||
|
|
||||||
buffer<Char>& buffer_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The put-area is actually always empty. This makes the implementation
|
|
||||||
// simpler and has the advantage that the streambuf and the buffer are always
|
|
||||||
// in sync and sputc never writes into uninitialized memory. The obvious
|
|
||||||
// disadvantage is that each call to sputc always results in a (virtual) call
|
|
||||||
// to overflow. There is no disadvantage here for sputn since this always
|
|
||||||
// results in a call to xsputn.
|
|
||||||
|
|
||||||
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
|
||||||
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
|
||||||
buffer_.push_back(static_cast<Char>(ch));
|
|
||||||
return ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
|
|
||||||
buffer_.append(s, s + count);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct converter {
|
|
||||||
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
|
||||||
private:
|
|
||||||
void_t<> operator<<(converter);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Hide insertion operators for built-in types.
|
|
||||||
template <typename Char, typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
|
|
||||||
template <typename Char, typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
|
|
||||||
template <typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
|
|
||||||
template <typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
|
|
||||||
template <typename Traits>
|
|
||||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
|
|
||||||
|
|
||||||
// Checks if T has a user-defined operator<< (e.g. not a member of
|
|
||||||
// std::ostream).
|
|
||||||
template <typename T, typename Char> class is_streamable {
|
|
||||||
private:
|
private:
|
||||||
template <typename U>
|
template <typename U>
|
||||||
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
|
static auto test(int)
|
||||||
<< std::declval<U>()),
|
-> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
|
||||||
void_t<>>::value>
|
<< std::declval<U>()) != 0>;
|
||||||
test(int);
|
|
||||||
|
|
||||||
template <typename> static std::false_type test(...);
|
template <typename> static auto test(...) -> std::false_type;
|
||||||
|
|
||||||
using result = decltype(test<T>(0));
|
using result = decltype(test<T>(0));
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
is_streamable() = default;
|
||||||
|
|
||||||
static const bool value = result::value;
|
static const bool value = result::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Formatting of built-in types and arrays is intentionally disabled because
|
||||||
|
// it's handled by standard (non-ostream) formatters.
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct is_streamable<
|
||||||
|
T, Char,
|
||||||
|
enable_if_t<
|
||||||
|
std::is_arithmetic<T>::value || std::is_array<T>::value ||
|
||||||
|
std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
|
||||||
|
std::is_same<T, std::basic_string<Char>>::value ||
|
||||||
|
std::is_same<T, std_string_view<Char>>::value ||
|
||||||
|
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
|
||||||
|
: std::false_type {};
|
||||||
|
|
||||||
// Write the content of buf to os.
|
// Write the content of buf to os.
|
||||||
|
// It is a separate function rather than a part of vprint to simplify testing.
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||||
const Char* buf_data = buf.data();
|
const Char* buf_data = buf.data();
|
||||||
|
@ -106,8 +69,8 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||||
template <typename Char, typename T>
|
template <typename Char, typename T>
|
||||||
void format_value(buffer<Char>& buf, const T& value,
|
void format_value(buffer<Char>& buf, const T& value,
|
||||||
locale_ref loc = locale_ref()) {
|
locale_ref loc = locale_ref()) {
|
||||||
formatbuf<Char> format_buf(buf);
|
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
|
||||||
std::basic_ostream<Char> output(&format_buf);
|
auto&& output = std::basic_ostream<Char>(&format_buf);
|
||||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
if (loc) output.imbue(loc.get<std::locale>());
|
if (loc) output.imbue(loc.get<std::locale>());
|
||||||
#endif
|
#endif
|
||||||
|
@ -120,39 +83,33 @@ void format_value(buffer<Char>& buf, const T& value,
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||||
: private formatter<basic_string_view<Char>, Char> {
|
: private formatter<basic_string_view<Char>, Char> {
|
||||||
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
using formatter<basic_string_view<Char>, Char>::parse;
|
||||||
-> decltype(ctx.begin()) {
|
|
||||||
return formatter<basic_string_view<Char>, Char>::parse(ctx);
|
|
||||||
}
|
|
||||||
template <typename ParseCtx,
|
|
||||||
FMT_ENABLE_IF(std::is_same<
|
|
||||||
ParseCtx, basic_printf_parse_context<Char>>::value)>
|
|
||||||
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt>
|
template <typename OutputIt>
|
||||||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
|
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
|
||||||
-> OutputIt {
|
-> OutputIt {
|
||||||
basic_memory_buffer<Char> buffer;
|
auto buffer = basic_memory_buffer<Char>();
|
||||||
format_value(buffer, value, ctx.locale());
|
format_value(buffer, value, ctx.locale());
|
||||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
return formatter<basic_string_view<Char>, Char>::format(
|
||||||
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
{buffer.data(), buffer.size()}, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEPRECATED!
|
||||||
template <typename OutputIt>
|
template <typename OutputIt>
|
||||||
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
|
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
|
||||||
-> OutputIt {
|
-> OutputIt {
|
||||||
basic_memory_buffer<Char> buffer;
|
auto buffer = basic_memory_buffer<Char>();
|
||||||
format_value(buffer, value, ctx.locale());
|
format_value(buffer, value, ctx.locale());
|
||||||
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
FMT_MODULE_EXPORT
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
auto buffer = basic_memory_buffer<Char>();
|
||||||
detail::vformat_to(buffer, format_str, args);
|
detail::vformat_to(buffer, format_str, args);
|
||||||
detail::write_buffer(os, buffer);
|
detail::write_buffer(os, buffer);
|
||||||
}
|
}
|
||||||
|
@ -166,6 +123,7 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||||
fmt::print(cerr, "Don't {}!", "panic");
|
fmt::print(cerr, "Don't {}!", "panic");
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
|
FMT_MODULE_EXPORT
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
#include "os.h"
|
|
||||||
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"
|
|
|
@ -10,11 +10,54 @@
|
||||||
|
|
||||||
#include <algorithm> // std::max
|
#include <algorithm> // std::max
|
||||||
#include <limits> // std::numeric_limits
|
#include <limits> // std::numeric_limits
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
#include "ostream.h"
|
#include "format.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
|
|
||||||
|
template <typename T> struct printf_formatter { printf_formatter() = delete; };
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
class basic_printf_parse_context : public basic_format_parse_context<Char> {
|
||||||
|
using basic_format_parse_context<Char>::basic_format_parse_context;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char> class basic_printf_context {
|
||||||
|
private:
|
||||||
|
OutputIt out_;
|
||||||
|
basic_format_args<basic_printf_context> args_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using char_type = Char;
|
||||||
|
using format_arg = basic_format_arg<basic_printf_context>;
|
||||||
|
using parse_context_type = basic_printf_parse_context<Char>;
|
||||||
|
template <typename T> using formatter_type = printf_formatter<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a ``printf_context`` object. References to the arguments are
|
||||||
|
stored in the context object so make sure they have appropriate lifetimes.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
basic_printf_context(OutputIt out,
|
||||||
|
basic_format_args<basic_printf_context> args)
|
||||||
|
: out_(out), args_(args) {}
|
||||||
|
|
||||||
|
OutputIt out() { return out_; }
|
||||||
|
void advance_to(OutputIt it) { out_ = it; }
|
||||||
|
|
||||||
|
detail::locale_ref locale() { return {}; }
|
||||||
|
|
||||||
|
format_arg arg(int id) const { return args_.get(id); }
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_error(const char* message) {
|
||||||
|
detail::error_handler().on_error(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_BEGIN_DETAIL_NAMESPACE
|
||||||
|
|
||||||
// Checks if a value fits in int - used to avoid warnings about comparing
|
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||||
// signed and unsigned integers.
|
// signed and unsigned integers.
|
||||||
|
@ -178,81 +221,38 @@ template <typename Char> class printf_width_handler {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char, typename Context>
|
// The ``printf`` argument formatter.
|
||||||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
|
||||||
basic_format_args<Context> args) {
|
|
||||||
Context(buffer_appender<Char>(buf), format, args).format();
|
|
||||||
}
|
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
// For printing into memory_buffer.
|
|
||||||
template <typename Char, typename Context>
|
|
||||||
FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
|
|
||||||
basic_string_view<Char> format,
|
|
||||||
basic_format_args<Context> args) {
|
|
||||||
return detail::vprintf(buf, format, args);
|
|
||||||
}
|
|
||||||
using detail::vprintf;
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
class basic_printf_parse_context : public basic_format_parse_context<Char> {
|
|
||||||
using basic_format_parse_context<Char>::basic_format_parse_context;
|
|
||||||
};
|
|
||||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
The ``printf`` argument formatter.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename OutputIt, typename Char>
|
template <typename OutputIt, typename Char>
|
||||||
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
|
class printf_arg_formatter : public arg_formatter<Char> {
|
||||||
public:
|
|
||||||
using iterator = OutputIt;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using char_type = Char;
|
using base = arg_formatter<Char>;
|
||||||
using base = detail::arg_formatter_base<OutputIt, Char>;
|
|
||||||
using context_type = basic_printf_context<OutputIt, Char>;
|
using context_type = basic_printf_context<OutputIt, Char>;
|
||||||
|
using format_specs = basic_format_specs<Char>;
|
||||||
|
|
||||||
context_type& context_;
|
context_type& context_;
|
||||||
|
|
||||||
void write_null_pointer(char) {
|
OutputIt write_null_pointer(bool is_string = false) {
|
||||||
this->specs()->type = 0;
|
auto s = this->specs;
|
||||||
this->write("(nil)");
|
s.type = presentation_type::none;
|
||||||
}
|
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
|
||||||
|
|
||||||
void write_null_pointer(wchar_t) {
|
|
||||||
this->specs()->type = 0;
|
|
||||||
this->write(L"(nil)");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using format_specs = typename base::format_specs;
|
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
|
||||||
|
: base{iter, s, locale_ref()}, context_(ctx) {}
|
||||||
|
|
||||||
/**
|
OutputIt operator()(monostate value) { return base::operator()(value); }
|
||||||
\rst
|
|
||||||
Constructs an argument formatter object.
|
|
||||||
*buffer* is a reference to the output buffer and *specs* contains format
|
|
||||||
specifier information for standard argument types.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
|
|
||||||
: base(iter, &specs, detail::locale_ref()), context_(ctx) {}
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
|
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
|
||||||
iterator operator()(T value) {
|
OutputIt operator()(T value) {
|
||||||
// MSVC2013 fails to compile separate overloads for bool and char_type so
|
// MSVC2013 fails to compile separate overloads for bool and Char so use
|
||||||
// use std::is_same instead.
|
// std::is_same instead.
|
||||||
if (std::is_same<T, bool>::value) {
|
if (std::is_same<T, Char>::value) {
|
||||||
format_specs& fmt_specs = *this->specs();
|
format_specs fmt_specs = this->specs;
|
||||||
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0);
|
if (fmt_specs.type != presentation_type::none &&
|
||||||
fmt_specs.type = 0;
|
fmt_specs.type != presentation_type::chr) {
|
||||||
this->write(value != 0);
|
|
||||||
} else if (std::is_same<T, char_type>::value) {
|
|
||||||
format_specs& fmt_specs = *this->specs();
|
|
||||||
if (fmt_specs.type && fmt_specs.type != 'c')
|
|
||||||
return (*this)(static_cast<int>(value));
|
return (*this)(static_cast<int>(value));
|
||||||
|
}
|
||||||
fmt_specs.sign = sign::none;
|
fmt_specs.sign = sign::none;
|
||||||
fmt_specs.alt = false;
|
fmt_specs.alt = false;
|
||||||
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
||||||
|
@ -260,138 +260,49 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
|
||||||
// ignored for non-numeric types
|
// ignored for non-numeric types
|
||||||
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
||||||
fmt_specs.align = align::right;
|
fmt_specs.align = align::right;
|
||||||
return base::operator()(value);
|
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
|
||||||
} else {
|
|
||||||
return base::operator()(value);
|
|
||||||
}
|
}
|
||||||
return this->out();
|
return base::operator()(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||||
iterator operator()(T value) {
|
OutputIt operator()(T value) {
|
||||||
return base::operator()(value);
|
return base::operator()(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Formats a null-terminated C string. */
|
/** Formats a null-terminated C string. */
|
||||||
iterator operator()(const char* value) {
|
OutputIt operator()(const char* value) {
|
||||||
if (value)
|
if (value) return base::operator()(value);
|
||||||
base::operator()(value);
|
return write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||||
else if (this->specs()->type == 'p')
|
|
||||||
write_null_pointer(char_type());
|
|
||||||
else
|
|
||||||
this->write("(null)");
|
|
||||||
return this->out();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Formats a null-terminated wide C string. */
|
/** Formats a null-terminated wide C string. */
|
||||||
iterator operator()(const wchar_t* value) {
|
OutputIt operator()(const wchar_t* value) {
|
||||||
if (value)
|
if (value) return base::operator()(value);
|
||||||
base::operator()(value);
|
return write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||||
else if (this->specs()->type == 'p')
|
|
||||||
write_null_pointer(char_type());
|
|
||||||
else
|
|
||||||
this->write(L"(null)");
|
|
||||||
return this->out();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator operator()(basic_string_view<char_type> value) {
|
OutputIt operator()(basic_string_view<Char> value) {
|
||||||
return base::operator()(value);
|
return base::operator()(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator operator()(monostate value) { return base::operator()(value); }
|
|
||||||
|
|
||||||
/** Formats a pointer. */
|
/** Formats a pointer. */
|
||||||
iterator operator()(const void* value) {
|
OutputIt operator()(const void* value) {
|
||||||
if (value) return base::operator()(value);
|
return value ? base::operator()(value) : write_null_pointer();
|
||||||
this->specs()->type = 0;
|
|
||||||
write_null_pointer(char_type());
|
|
||||||
return this->out();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Formats an argument of a custom (user-defined) type. */
|
/** Formats an argument of a custom (user-defined) type. */
|
||||||
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
|
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||||
handle.format(context_.parse_context(), context_);
|
auto parse_ctx =
|
||||||
return this->out();
|
basic_printf_parse_context<Char>(basic_string_view<Char>());
|
||||||
|
handle.format(parse_ctx, context_);
|
||||||
|
return this->out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T> struct printf_formatter {
|
template <typename Char>
|
||||||
printf_formatter() = delete;
|
void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
|
||||||
|
const Char* end) {
|
||||||
template <typename ParseContext>
|
|
||||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
|
|
||||||
detail::format_value(detail::get_container(ctx.out()), value);
|
|
||||||
return ctx.out();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
This template formats data and writes the output through an output iterator.
|
|
||||||
*/
|
|
||||||
template <typename OutputIt, typename Char> class basic_printf_context {
|
|
||||||
public:
|
|
||||||
/** The character type for the output. */
|
|
||||||
using char_type = Char;
|
|
||||||
using iterator = OutputIt;
|
|
||||||
using format_arg = basic_format_arg<basic_printf_context>;
|
|
||||||
using parse_context_type = basic_printf_parse_context<Char>;
|
|
||||||
template <typename T> using formatter_type = printf_formatter<T>;
|
|
||||||
|
|
||||||
private:
|
|
||||||
using format_specs = basic_format_specs<char_type>;
|
|
||||||
|
|
||||||
OutputIt out_;
|
|
||||||
basic_format_args<basic_printf_context> args_;
|
|
||||||
parse_context_type parse_ctx_;
|
|
||||||
|
|
||||||
static void parse_flags(format_specs& specs, const Char*& it,
|
|
||||||
const Char* end);
|
|
||||||
|
|
||||||
// Returns the argument with specified index or, if arg_index is -1, the next
|
|
||||||
// argument.
|
|
||||||
format_arg get_arg(int arg_index = -1);
|
|
||||||
|
|
||||||
// Parses argument index, flags and width and returns the argument index.
|
|
||||||
int parse_header(const Char*& it, const Char* end, format_specs& specs);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Constructs a ``printf_context`` object. References to the arguments are
|
|
||||||
stored in the context object so make sure they have appropriate lifetimes.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
|
|
||||||
basic_format_args<basic_printf_context> args)
|
|
||||||
: out_(out), args_(args), parse_ctx_(format_str) {}
|
|
||||||
|
|
||||||
OutputIt out() { return out_; }
|
|
||||||
void advance_to(OutputIt it) { out_ = it; }
|
|
||||||
|
|
||||||
detail::locale_ref locale() { return {}; }
|
|
||||||
|
|
||||||
format_arg arg(int id) const { return args_.get(id); }
|
|
||||||
|
|
||||||
parse_context_type& parse_context() { return parse_ctx_; }
|
|
||||||
|
|
||||||
FMT_CONSTEXPR void on_error(const char* message) {
|
|
||||||
parse_ctx_.on_error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats stored arguments and writes the output to the range. */
|
|
||||||
template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
|
|
||||||
OutputIt format();
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
|
||||||
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
|
|
||||||
const Char*& it,
|
|
||||||
const Char* end) {
|
|
||||||
for (; it != end; ++it) {
|
for (; it != end; ++it) {
|
||||||
switch (*it) {
|
switch (*it) {
|
||||||
case '-':
|
case '-':
|
||||||
|
@ -417,35 +328,24 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
template <typename Char, typename GetArg>
|
||||||
typename basic_printf_context<OutputIt, Char>::format_arg
|
int parse_header(const Char*& it, const Char* end,
|
||||||
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
|
basic_format_specs<Char>& specs, GetArg get_arg) {
|
||||||
if (arg_index < 0)
|
|
||||||
arg_index = parse_ctx_.next_arg_id();
|
|
||||||
else
|
|
||||||
parse_ctx_.check_arg_id(--arg_index);
|
|
||||||
return detail::get_arg(*this, arg_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
|
||||||
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
|
|
||||||
const Char* end,
|
|
||||||
format_specs& specs) {
|
|
||||||
int arg_index = -1;
|
int arg_index = -1;
|
||||||
char_type c = *it;
|
Char c = *it;
|
||||||
if (c >= '0' && c <= '9') {
|
if (c >= '0' && c <= '9') {
|
||||||
// Parse an argument index (if followed by '$') or a width possibly
|
// Parse an argument index (if followed by '$') or a width possibly
|
||||||
// preceded with '0' flag(s).
|
// preceded with '0' flag(s).
|
||||||
detail::error_handler eh;
|
int value = parse_nonnegative_int(it, end, -1);
|
||||||
int value = parse_nonnegative_int(it, end, eh);
|
|
||||||
if (it != end && *it == '$') { // value is an argument index
|
if (it != end && *it == '$') { // value is an argument index
|
||||||
++it;
|
++it;
|
||||||
arg_index = value;
|
arg_index = value != -1 ? value : max_value<int>();
|
||||||
} else {
|
} else {
|
||||||
if (c == '0') specs.fill[0] = '0';
|
if (c == '0') specs.fill[0] = '0';
|
||||||
if (value != 0) {
|
if (value != 0) {
|
||||||
// Nonzero value means that we parsed width and don't need to
|
// Nonzero value means that we parsed width and don't need to
|
||||||
// parse it or flags again, so return now.
|
// parse it or flags again, so return now.
|
||||||
|
if (value == -1) FMT_THROW(format_error("number is too big"));
|
||||||
specs.width = value;
|
specs.width = value;
|
||||||
return arg_index;
|
return arg_index;
|
||||||
}
|
}
|
||||||
|
@ -455,58 +355,76 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
|
||||||
// Parse width.
|
// Parse width.
|
||||||
if (it != end) {
|
if (it != end) {
|
||||||
if (*it >= '0' && *it <= '9') {
|
if (*it >= '0' && *it <= '9') {
|
||||||
detail::error_handler eh;
|
specs.width = parse_nonnegative_int(it, end, -1);
|
||||||
specs.width = parse_nonnegative_int(it, end, eh);
|
if (specs.width == -1) FMT_THROW(format_error("number is too big"));
|
||||||
} else if (*it == '*') {
|
} else if (*it == '*') {
|
||||||
++it;
|
++it;
|
||||||
specs.width = static_cast<int>(visit_format_arg(
|
specs.width = static_cast<int>(visit_format_arg(
|
||||||
detail::printf_width_handler<char_type>(specs), get_arg()));
|
detail::printf_width_handler<Char>(specs), get_arg(-1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return arg_index;
|
return arg_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
template <typename Char, typename Context>
|
||||||
template <typename ArgFormatter>
|
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||||
OutputIt basic_printf_context<OutputIt, Char>::format() {
|
basic_format_args<Context> args) {
|
||||||
auto out = this->out();
|
using OutputIt = buffer_appender<Char>;
|
||||||
const Char* start = parse_ctx_.begin();
|
auto out = OutputIt(buf);
|
||||||
const Char* end = parse_ctx_.end();
|
auto context = basic_printf_context<OutputIt, Char>(out, args);
|
||||||
|
auto parse_ctx = basic_printf_parse_context<Char>(format);
|
||||||
|
|
||||||
|
// Returns the argument with specified index or, if arg_index is -1, the next
|
||||||
|
// argument.
|
||||||
|
auto get_arg = [&](int arg_index) {
|
||||||
|
if (arg_index < 0)
|
||||||
|
arg_index = parse_ctx.next_arg_id();
|
||||||
|
else
|
||||||
|
parse_ctx.check_arg_id(--arg_index);
|
||||||
|
return detail::get_arg(context, arg_index);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Char* start = parse_ctx.begin();
|
||||||
|
const Char* end = parse_ctx.end();
|
||||||
auto it = start;
|
auto it = start;
|
||||||
while (it != end) {
|
while (it != end) {
|
||||||
char_type c = *it++;
|
if (!detail::find<false, Char>(it, end, '%', it)) {
|
||||||
if (c != '%') continue;
|
it = end; // detail::find leaves it == nullptr if it doesn't find '%'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Char c = *it++;
|
||||||
if (it != end && *it == c) {
|
if (it != end && *it == c) {
|
||||||
out = std::copy(start, it, out);
|
out = detail::write(
|
||||||
|
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
|
||||||
start = ++it;
|
start = ++it;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
out = std::copy(start, it - 1, out);
|
out = detail::write(out, basic_string_view<Char>(
|
||||||
|
start, detail::to_unsigned(it - 1 - start)));
|
||||||
|
|
||||||
format_specs specs;
|
basic_format_specs<Char> specs;
|
||||||
specs.align = align::right;
|
specs.align = align::right;
|
||||||
|
|
||||||
// Parse argument index, flags and width.
|
// Parse argument index, flags and width.
|
||||||
int arg_index = parse_header(it, end, specs);
|
int arg_index = parse_header(it, end, specs, get_arg);
|
||||||
if (arg_index == 0) on_error("argument not found");
|
if (arg_index == 0) parse_ctx.on_error("argument not found");
|
||||||
|
|
||||||
// Parse precision.
|
// Parse precision.
|
||||||
if (it != end && *it == '.') {
|
if (it != end && *it == '.') {
|
||||||
++it;
|
++it;
|
||||||
c = it != end ? *it : 0;
|
c = it != end ? *it : 0;
|
||||||
if ('0' <= c && c <= '9') {
|
if ('0' <= c && c <= '9') {
|
||||||
detail::error_handler eh;
|
specs.precision = parse_nonnegative_int(it, end, 0);
|
||||||
specs.precision = parse_nonnegative_int(it, end, eh);
|
|
||||||
} else if (c == '*') {
|
} else if (c == '*') {
|
||||||
++it;
|
++it;
|
||||||
specs.precision = static_cast<int>(
|
specs.precision = static_cast<int>(
|
||||||
visit_format_arg(detail::printf_precision_handler(), get_arg()));
|
visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
|
||||||
} else {
|
} else {
|
||||||
specs.precision = 0;
|
specs.precision = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
format_arg arg = get_arg(arg_index);
|
auto arg = get_arg(arg_index);
|
||||||
// For d, i, o, u, x, and X conversion specifiers, if a precision is
|
// For d, i, o, u, x, and X conversion specifiers, if a precision is
|
||||||
// specified, the '0' flag is ignored
|
// specified, the '0' flag is ignored
|
||||||
if (specs.precision >= 0 && arg.is_integral())
|
if (specs.precision >= 0 && arg.is_integral())
|
||||||
|
@ -516,9 +434,10 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||||
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
|
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
|
||||||
auto str_end = str + specs.precision;
|
auto str_end = str + specs.precision;
|
||||||
auto nul = std::find(str, str_end, Char());
|
auto nul = std::find(str, str_end, Char());
|
||||||
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>(
|
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
|
||||||
str,
|
basic_string_view<Char>(
|
||||||
detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
|
str, detail::to_unsigned(nul != str_end ? nul - str
|
||||||
|
: specs.precision)));
|
||||||
}
|
}
|
||||||
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
|
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
|
||||||
specs.alt = false;
|
specs.alt = false;
|
||||||
|
@ -532,7 +451,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||||
|
|
||||||
// Parse length and convert the argument to the required type.
|
// Parse length and convert the argument to the required type.
|
||||||
c = it != end ? *it++ : 0;
|
c = it != end ? *it++ : 0;
|
||||||
char_type t = it != end ? *it : 0;
|
Char t = it != end ? *it : 0;
|
||||||
using detail::convert_arg;
|
using detail::convert_arg;
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h':
|
case 'h':
|
||||||
|
@ -573,28 +492,34 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||||
|
|
||||||
// Parse type.
|
// Parse type.
|
||||||
if (it == end) FMT_THROW(format_error("invalid format string"));
|
if (it == end) FMT_THROW(format_error("invalid format string"));
|
||||||
specs.type = static_cast<char>(*it++);
|
char type = static_cast<char>(*it++);
|
||||||
if (arg.is_integral()) {
|
if (arg.is_integral()) {
|
||||||
// Normalize type.
|
// Normalize type.
|
||||||
switch (specs.type) {
|
switch (type) {
|
||||||
case 'i':
|
case 'i':
|
||||||
case 'u':
|
case 'u':
|
||||||
specs.type = 'd';
|
type = 'd';
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
visit_format_arg(detail::char_converter<basic_printf_context>(arg),
|
visit_format_arg(
|
||||||
arg);
|
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
|
||||||
|
arg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
specs.type = parse_presentation_type(type);
|
||||||
|
if (specs.type == presentation_type::none)
|
||||||
|
parse_ctx.on_error("invalid type specifier");
|
||||||
|
|
||||||
start = it;
|
start = it;
|
||||||
|
|
||||||
// Format argument.
|
// Format argument.
|
||||||
out = visit_format_arg(ArgFormatter(out, specs, *this), arg);
|
out = visit_format_arg(
|
||||||
|
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
|
||||||
}
|
}
|
||||||
return std::copy(start, it, out);
|
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
|
||||||
}
|
}
|
||||||
|
FMT_END_DETAIL_NAMESPACE
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
using basic_printf_context_t =
|
using basic_printf_context_t =
|
||||||
|
@ -612,9 +537,9 @@ using wprintf_args = basic_format_args<wprintf_context>;
|
||||||
arguments and can be implicitly converted to `~fmt::printf_args`.
|
arguments and can be implicitly converted to `~fmt::printf_args`.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename... Args>
|
template <typename... T>
|
||||||
inline format_arg_store<printf_context, Args...> make_printf_args(
|
inline auto make_printf_args(const T&... args)
|
||||||
const Args&... args) {
|
-> format_arg_store<printf_context, T...> {
|
||||||
return {args...};
|
return {args...};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -624,18 +549,19 @@ inline format_arg_store<printf_context, Args...> make_printf_args(
|
||||||
arguments and can be implicitly converted to `~fmt::wprintf_args`.
|
arguments and can be implicitly converted to `~fmt::wprintf_args`.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename... Args>
|
template <typename... T>
|
||||||
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
|
inline auto make_wprintf_args(const T&... args)
|
||||||
const Args&... args) {
|
-> format_arg_store<wprintf_context, T...> {
|
||||||
return {args...};
|
return {args...};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline std::basic_string<Char> vsprintf(
|
inline auto vsprintf(
|
||||||
const S& format,
|
const S& fmt,
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||||
|
-> std::basic_string<Char> {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
vprintf(buffer, to_string_view(format), args);
|
vprintf(buffer, to_string_view(fmt), args);
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,19 +574,20 @@ inline std::basic_string<Char> vsprintf(
|
||||||
std::string message = fmt::sprintf("The answer is %d", 42);
|
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... T,
|
||||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||||
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
|
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
|
||||||
using context = basic_printf_context_t<Char>;
|
using context = basic_printf_context_t<Char>;
|
||||||
return vsprintf(to_string_view(format), make_format_args<context>(args...));
|
return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline int vfprintf(
|
inline auto vfprintf(
|
||||||
std::FILE* f, const S& format,
|
std::FILE* f, const S& fmt,
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||||
|
-> int {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
vprintf(buffer, to_string_view(format), args);
|
vprintf(buffer, to_string_view(fmt), args);
|
||||||
size_t size = buffer.size();
|
size_t size = buffer.size();
|
||||||
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
||||||
? -1
|
? -1
|
||||||
|
@ -676,19 +603,19 @@ inline int vfprintf(
|
||||||
fmt::fprintf(stderr, "Don't %s!", "panic");
|
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... T, typename Char = char_t<S>>
|
||||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
|
||||||
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
|
|
||||||
using context = basic_printf_context_t<Char>;
|
using context = basic_printf_context_t<Char>;
|
||||||
return vfprintf(f, to_string_view(format),
|
return vfprintf(f, to_string_view(fmt),
|
||||||
make_format_args<context>(args...));
|
fmt::make_format_args<context>(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline int vprintf(
|
inline auto vprintf(
|
||||||
const S& format,
|
const S& fmt,
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||||
return vfprintf(stdout, to_string_view(format), args);
|
-> int {
|
||||||
|
return vfprintf(stdout, to_string_view(fmt), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -700,52 +627,31 @@ inline int vprintf(
|
||||||
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
inline auto printf(const S& fmt, const T&... args) -> int {
|
||||||
inline int printf(const S& format_str, const Args&... args) {
|
return vprintf(
|
||||||
using context = basic_printf_context_t<char_t<S>>;
|
to_string_view(fmt),
|
||||||
return vprintf(to_string_view(format_str),
|
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
|
||||||
make_format_args<context>(args...));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
inline int vfprintf(
|
FMT_DEPRECATED auto vfprintf(
|
||||||
std::basic_ostream<Char>& os, const S& format,
|
std::basic_ostream<Char>& os, const S& fmt,
|
||||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||||
|
-> int {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
vprintf(buffer, to_string_view(format), args);
|
vprintf(buffer, to_string_view(fmt), args);
|
||||||
detail::write_buffer(os, buffer);
|
os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
|
||||||
return static_cast<int>(buffer.size());
|
return static_cast<int>(buffer.size());
|
||||||
}
|
}
|
||||||
|
template <typename S, typename... T, typename Char = char_t<S>>
|
||||||
/** Formats arguments and writes the output to the range. */
|
FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
|
||||||
template <typename ArgFormatter, typename Char,
|
const T&... args) -> int {
|
||||||
typename Context =
|
return vfprintf(os, to_string_view(fmt),
|
||||||
basic_printf_context<typename ArgFormatter::iterator, Char>>
|
fmt::make_format_args<basic_printf_context_t<Char>>(args...));
|
||||||
typename ArgFormatter::iterator vprintf(
|
|
||||||
detail::buffer<Char>& out, basic_string_view<Char> format_str,
|
|
||||||
basic_format_args<type_identity_t<Context>> args) {
|
|
||||||
typename ArgFormatter::iterator iter(out);
|
|
||||||
Context(iter, format_str, args).template format<ArgFormatter>();
|
|
||||||
return iter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
FMT_MODULE_EXPORT_END
|
||||||
\rst
|
|
||||||
Prints formatted data to the stream *os*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
fmt::fprintf(cerr, "Don't %s!", "panic");
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
|
||||||
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
|
|
||||||
const Args&... args) {
|
|
||||||
using context = basic_printf_context_t<Char>;
|
|
||||||
return vfprintf(os, to_string_view(format_str),
|
|
||||||
make_format_args<context>(args...));
|
|
||||||
}
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_PRINTF_H_
|
#endif // FMT_PRINTF_H_
|
||||||
|
|
|
@ -13,47 +13,13 @@
|
||||||
#define FMT_RANGES_H_
|
#define FMT_RANGES_H_
|
||||||
|
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
|
#include <tuple>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
// output only up to N items from the range.
|
|
||||||
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
|
|
||||||
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
|
|
||||||
#endif
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
template <typename Char> struct formatting_base {
|
|
||||||
template <typename ParseContext>
|
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename Enable = void>
|
|
||||||
struct formatting_range : formatting_base<Char> {
|
|
||||||
static FMT_CONSTEXPR_DECL const size_t range_length_limit =
|
|
||||||
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
|
||||||
// range.
|
|
||||||
Char prefix;
|
|
||||||
Char delimiter;
|
|
||||||
Char postfix;
|
|
||||||
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename Enable = void>
|
|
||||||
struct formatting_tuple : formatting_base<Char> {
|
|
||||||
Char prefix;
|
|
||||||
Char delimiter;
|
|
||||||
Char postfix;
|
|
||||||
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <typename RangeT, typename OutputIterator>
|
template <typename RangeT, typename OutputIterator>
|
||||||
|
@ -75,8 +41,14 @@ OutputIterator copy(char ch, OutputIterator out) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true value if T has std::string interface, like std::string_view.
|
template <typename OutputIterator>
|
||||||
template <typename T> class is_like_std_string {
|
OutputIterator copy(wchar_t ch, OutputIterator out) {
|
||||||
|
*out++ = ch;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if T has a std::string-like interface, like std::string_view.
|
||||||
|
template <typename T> class is_std_string_like {
|
||||||
template <typename U>
|
template <typename U>
|
||||||
static auto check(U* p)
|
static auto check(U* p)
|
||||||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||||
|
@ -84,26 +56,118 @@ template <typename T> class is_like_std_string {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static FMT_CONSTEXPR_DECL const bool value =
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
|
is_string<T>::value ||
|
||||||
|
std::is_convertible<T, std_string_view<char>>::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_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T> class is_map {
|
||||||
|
template <typename U> static auto check(U*) -> typename U::mapped_type;
|
||||||
|
template <typename> static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef FMT_FORMAT_MAP_AS_LIST
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value = false;
|
||||||
|
#else
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class is_set {
|
||||||
|
template <typename U> static auto check(U*) -> typename U::key_type;
|
||||||
|
template <typename> static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef FMT_FORMAT_SET_AS_LIST
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value = false;
|
||||||
|
#else
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
template <typename... Ts> struct conditional_helper {};
|
template <typename... Ts> struct conditional_helper {};
|
||||||
|
|
||||||
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
||||||
|
|
||||||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
||||||
|
|
||||||
|
# define FMT_DECLTYPE_RETURN(val) \
|
||||||
|
->decltype(val) { return val; } \
|
||||||
|
static_assert( \
|
||||||
|
true, "") // This makes it so that a semicolon is required after the
|
||||||
|
// macro, which helps clang-format handle the formatting.
|
||||||
|
|
||||||
|
// C array overload
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
auto range_begin(const T (&arr)[N]) -> const T* {
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
auto range_end(const T (&arr)[N]) -> const T* {
|
||||||
|
return arr + N;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void>
|
||||||
|
struct has_member_fn_begin_end_t : std::false_type {};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct is_range_<
|
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
|
||||||
T, conditional_t<false,
|
decltype(std::declval<T>().end())>>
|
||||||
conditional_helper<decltype(std::declval<T>().begin()),
|
: std::true_type {};
|
||||||
decltype(std::declval<T>().end())>,
|
|
||||||
void>> : std::true_type {};
|
// Member function overload
|
||||||
|
template <typename T>
|
||||||
|
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
|
||||||
|
template <typename T>
|
||||||
|
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
|
||||||
|
|
||||||
|
// ADL overload. Only participates in overload resolution if member functions
|
||||||
|
// are not found.
|
||||||
|
template <typename T>
|
||||||
|
auto range_begin(T&& rng)
|
||||||
|
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
|
||||||
|
decltype(begin(static_cast<T&&>(rng)))> {
|
||||||
|
return begin(static_cast<T&&>(rng));
|
||||||
|
}
|
||||||
|
template <typename T>
|
||||||
|
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
|
||||||
|
decltype(end(static_cast<T&&>(rng)))> {
|
||||||
|
return end(static_cast<T&&>(rng));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void>
|
||||||
|
struct has_const_begin_end : std::false_type {};
|
||||||
|
template <typename T, typename Enable = void>
|
||||||
|
struct has_mutable_begin_end : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct has_const_begin_end<
|
||||||
|
T,
|
||||||
|
void_t<
|
||||||
|
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
|
||||||
|
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct has_mutable_begin_end<
|
||||||
|
T, void_t<decltype(detail::range_begin(std::declval<T>())),
|
||||||
|
decltype(detail::range_end(std::declval<T>())),
|
||||||
|
enable_if_t<std::is_copy_constructible<T>::value>>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_range_<T, void>
|
||||||
|
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
|
||||||
|
has_mutable_begin_end<T>::value)> {};
|
||||||
|
# undef FMT_DECLTYPE_RETURN
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// tuple_size and tuple_element check.
|
// tuple_size and tuple_element check.
|
||||||
template <typename T> class is_tuple_like_ {
|
template <typename T> class is_tuple_like_ {
|
||||||
template <typename U>
|
template <typename U>
|
||||||
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
||||||
|
@ -158,33 +222,321 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Range>
|
template <typename Range>
|
||||||
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
|
using value_type =
|
||||||
|
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>;
|
||||||
|
|
||||||
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
|
||||||
typename std::decay<Arg>::type>::value)>
|
*out++ = ',';
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
*out++ = ' ';
|
||||||
return add_space ? " {}" : "{}";
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
|
struct singleton {
|
||||||
typename std::decay<Arg>::type>::value)>
|
unsigned char upper;
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
unsigned char lower_count;
|
||||||
return add_space ? " \"{}\"" : "\"{}\"";
|
};
|
||||||
|
|
||||||
|
inline auto is_printable(uint16_t x, const singleton* singletons,
|
||||||
|
size_t singletons_size,
|
||||||
|
const unsigned char* singleton_lowers,
|
||||||
|
const unsigned char* normal, size_t normal_size)
|
||||||
|
-> bool {
|
||||||
|
auto upper = x >> 8;
|
||||||
|
auto lower_start = 0;
|
||||||
|
for (size_t i = 0; i < singletons_size; ++i) {
|
||||||
|
auto s = singletons[i];
|
||||||
|
auto lower_end = lower_start + s.lower_count;
|
||||||
|
if (upper < s.upper) break;
|
||||||
|
if (upper == s.upper) {
|
||||||
|
for (auto j = lower_start; j < lower_end; ++j) {
|
||||||
|
if (singleton_lowers[j] == (x & 0xff)) return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lower_start = lower_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto xsigned = static_cast<int>(x);
|
||||||
|
auto current = true;
|
||||||
|
for (size_t i = 0; i < normal_size; ++i) {
|
||||||
|
auto v = static_cast<int>(normal[i]);
|
||||||
|
auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
|
||||||
|
xsigned -= len;
|
||||||
|
if (xsigned < 0) break;
|
||||||
|
current = !current;
|
||||||
|
}
|
||||||
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
|
// Returns true iff the code point cp is printable.
|
||||||
return add_space ? " \"{}\"" : "\"{}\"";
|
// This code is generated by support/printable.py.
|
||||||
}
|
inline auto is_printable(uint32_t cp) -> bool {
|
||||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
|
static constexpr singleton singletons0[] = {
|
||||||
return add_space ? L" \"{}\"" : L"\"{}\"";
|
{0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8},
|
||||||
|
{0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13},
|
||||||
|
{0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5},
|
||||||
|
{0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22},
|
||||||
|
{0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3},
|
||||||
|
{0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8},
|
||||||
|
{0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9},
|
||||||
|
};
|
||||||
|
static constexpr unsigned char singletons0_lower[] = {
|
||||||
|
0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90,
|
||||||
|
0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f,
|
||||||
|
0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
|
||||||
|
0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04,
|
||||||
|
0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d,
|
||||||
|
0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
|
||||||
|
0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
|
||||||
|
0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d,
|
||||||
|
0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
|
||||||
|
0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d,
|
||||||
|
0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5,
|
||||||
|
0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
|
||||||
|
0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49,
|
||||||
|
0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7,
|
||||||
|
0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
|
||||||
|
0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e,
|
||||||
|
0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16,
|
||||||
|
0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
|
||||||
|
0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f,
|
||||||
|
0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf,
|
||||||
|
0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
|
||||||
|
0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27,
|
||||||
|
0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91,
|
||||||
|
0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
|
||||||
|
0xfe, 0xff,
|
||||||
|
};
|
||||||
|
static constexpr singleton singletons1[] = {
|
||||||
|
{0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2},
|
||||||
|
{0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5},
|
||||||
|
{0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5},
|
||||||
|
{0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2},
|
||||||
|
{0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5},
|
||||||
|
{0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2},
|
||||||
|
{0xfa, 2}, {0xfb, 1},
|
||||||
|
};
|
||||||
|
static constexpr unsigned char singletons1_lower[] = {
|
||||||
|
0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07,
|
||||||
|
0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36,
|
||||||
|
0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
|
||||||
|
0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
|
||||||
|
0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b,
|
||||||
|
0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
|
||||||
|
0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66,
|
||||||
|
0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27,
|
||||||
|
0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
|
||||||
|
0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7,
|
||||||
|
0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6,
|
||||||
|
0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
|
||||||
|
0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66,
|
||||||
|
0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0,
|
||||||
|
0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
|
||||||
|
};
|
||||||
|
static constexpr unsigned char normal0[] = {
|
||||||
|
0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04,
|
||||||
|
0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0,
|
||||||
|
0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
|
||||||
|
0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03,
|
||||||
|
0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03,
|
||||||
|
0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
|
||||||
|
0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15,
|
||||||
|
0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f,
|
||||||
|
0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
|
||||||
|
0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07,
|
||||||
|
0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06,
|
||||||
|
0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
|
||||||
|
0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac,
|
||||||
|
0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c,
|
||||||
|
0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
|
||||||
|
0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c,
|
||||||
|
0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b,
|
||||||
|
0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
|
||||||
|
0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03,
|
||||||
|
0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80,
|
||||||
|
0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
|
||||||
|
0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c,
|
||||||
|
0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17,
|
||||||
|
0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
|
||||||
|
0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80,
|
||||||
|
0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d,
|
||||||
|
};
|
||||||
|
static constexpr unsigned char normal1[] = {
|
||||||
|
0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f,
|
||||||
|
0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e,
|
||||||
|
0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
|
||||||
|
0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09,
|
||||||
|
0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16,
|
||||||
|
0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
|
||||||
|
0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36,
|
||||||
|
0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33,
|
||||||
|
0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
|
||||||
|
0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e,
|
||||||
|
0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41,
|
||||||
|
0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
|
||||||
|
0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22,
|
||||||
|
0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04,
|
||||||
|
0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
|
||||||
|
0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03,
|
||||||
|
0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81,
|
||||||
|
0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
|
||||||
|
0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1,
|
||||||
|
0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a,
|
||||||
|
0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
|
||||||
|
0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09,
|
||||||
|
0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89,
|
||||||
|
0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
|
||||||
|
0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09,
|
||||||
|
0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50,
|
||||||
|
0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
|
||||||
|
0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83,
|
||||||
|
0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05,
|
||||||
|
0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
|
||||||
|
0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80,
|
||||||
|
0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07,
|
||||||
|
0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
|
||||||
|
0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07,
|
||||||
|
0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06,
|
||||||
|
};
|
||||||
|
auto lower = static_cast<uint16_t>(cp);
|
||||||
|
if (cp < 0x10000) {
|
||||||
|
return is_printable(lower, singletons0,
|
||||||
|
sizeof(singletons0) / sizeof(*singletons0),
|
||||||
|
singletons0_lower, normal0, sizeof(normal0));
|
||||||
|
}
|
||||||
|
if (cp < 0x20000) {
|
||||||
|
return is_printable(lower, singletons1,
|
||||||
|
sizeof(singletons1) / sizeof(*singletons1),
|
||||||
|
singletons1_lower, normal1, sizeof(normal1));
|
||||||
|
}
|
||||||
|
if (0x2a6de <= cp && cp < 0x2a700) return false;
|
||||||
|
if (0x2b735 <= cp && cp < 0x2b740) return false;
|
||||||
|
if (0x2b81e <= cp && cp < 0x2b820) return false;
|
||||||
|
if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
|
||||||
|
if (0x2ebe1 <= cp && cp < 0x2f800) return false;
|
||||||
|
if (0x2fa1e <= cp && cp < 0x30000) return false;
|
||||||
|
if (0x3134b <= cp && cp < 0xe0100) return false;
|
||||||
|
if (0xe01f0 <= cp && cp < 0x110000) return false;
|
||||||
|
return cp < 0x110000;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
inline auto needs_escape(uint32_t cp) -> bool {
|
||||||
return add_space ? " '{}'" : "'{}'";
|
return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
|
||||||
|
!is_printable(cp);
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
|
||||||
return add_space ? L" '{}'" : L"'{}'";
|
template <typename Char> struct find_escape_result {
|
||||||
|
const Char* begin;
|
||||||
|
const Char* end;
|
||||||
|
uint32_t cp;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
auto find_escape(const Char* begin, const Char* end)
|
||||||
|
-> find_escape_result<Char> {
|
||||||
|
for (; begin != end; ++begin) {
|
||||||
|
auto cp = static_cast<typename std::make_unsigned<Char>::type>(*begin);
|
||||||
|
if (sizeof(Char) == 1 && cp >= 0x80) continue;
|
||||||
|
if (needs_escape(cp)) return {begin, begin + 1, cp};
|
||||||
|
}
|
||||||
|
return {begin, nullptr, 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline auto find_escape(const char* begin, const char* end)
|
||||||
|
-> find_escape_result<char> {
|
||||||
|
if (!is_utf8()) return find_escape<char>(begin, end);
|
||||||
|
auto result = find_escape_result<char>{end, nullptr, 0};
|
||||||
|
for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
|
||||||
|
[&](uint32_t cp, string_view sv) {
|
||||||
|
if (needs_escape(cp)) {
|
||||||
|
result = {sv.begin(), sv.end(), cp};
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename OutputIt>
|
||||||
|
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
|
||||||
|
*out++ = '"';
|
||||||
|
auto begin = str.begin(), end = str.end();
|
||||||
|
do {
|
||||||
|
auto escape = find_escape(begin, end);
|
||||||
|
out = copy_str<Char>(begin, escape.begin, out);
|
||||||
|
begin = escape.end;
|
||||||
|
if (!begin) break;
|
||||||
|
auto c = static_cast<Char>(escape.cp);
|
||||||
|
switch (escape.cp) {
|
||||||
|
case '\n':
|
||||||
|
*out++ = '\\';
|
||||||
|
c = 'n';
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
*out++ = '\\';
|
||||||
|
c = 'r';
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
*out++ = '\\';
|
||||||
|
c = 't';
|
||||||
|
break;
|
||||||
|
case '"':
|
||||||
|
FMT_FALLTHROUGH;
|
||||||
|
case '\\':
|
||||||
|
*out++ = '\\';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (is_utf8()) {
|
||||||
|
if (escape.cp < 0x100) {
|
||||||
|
out = format_to(out, "\\x{:02x}", escape.cp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (escape.cp < 0x10000) {
|
||||||
|
out = format_to(out, "\\u{:04x}", escape.cp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (escape.cp < 0x110000) {
|
||||||
|
out = format_to(out, "\\U{:08x}", escape.cp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Char escape_char : basic_string_view<Char>(
|
||||||
|
escape.begin, to_unsigned(escape.end - escape.begin))) {
|
||||||
|
out = format_to(
|
||||||
|
out, "\\x{:02x}",
|
||||||
|
static_cast<typename std::make_unsigned<Char>::type>(escape_char));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*out++ = c;
|
||||||
|
} while (begin != end);
|
||||||
|
*out++ = '"';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename OutputIt, typename T,
|
||||||
|
FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
|
||||||
|
inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
|
||||||
|
auto sv = std_string_view<Char>(str);
|
||||||
|
return write_range_entry<Char>(out, basic_string_view<Char>(sv));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename OutputIt, typename Arg,
|
||||||
|
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
|
||||||
|
OutputIt write_range_entry(OutputIt out, const Arg v) {
|
||||||
|
*out++ = '\'';
|
||||||
|
*out++ = v;
|
||||||
|
*out++ = '\'';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename Char, typename OutputIt, typename Arg,
|
||||||
|
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
|
||||||
|
!std::is_same<Arg, Char>::value)>
|
||||||
|
OutputIt write_range_entry(OutputIt out, const Arg& v) {
|
||||||
|
return write<Char>(out, v);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template <typename T> struct is_tuple_like {
|
template <typename T> struct is_tuple_like {
|
||||||
|
@ -195,55 +547,37 @@ template <typename T> struct is_tuple_like {
|
||||||
template <typename TupleT, typename Char>
|
template <typename TupleT, typename Char>
|
||||||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||||
private:
|
private:
|
||||||
// C++11 generic lambda for format()
|
// C++11 generic lambda for format().
|
||||||
template <typename FormatContext> struct format_each {
|
template <typename FormatContext> 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) out = detail::write_delimiter(out);
|
||||||
if (formatting.add_prepostfix_space) {
|
out = detail::write_range_entry<Char>(out, v);
|
||||||
*out++ = ' ';
|
|
||||||
}
|
|
||||||
out = detail::copy(formatting.delimiter, out);
|
|
||||||
}
|
|
||||||
out = format_to(out,
|
|
||||||
detail::format_str_quoted(
|
|
||||||
(formatting.add_delimiter_spaces && i > 0), v),
|
|
||||||
v);
|
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
int i;
|
||||||
formatting_tuple<Char>& formatting;
|
typename FormatContext::iterator& out;
|
||||||
size_t& i;
|
|
||||||
typename std::add_lvalue_reference<decltype(
|
|
||||||
std::declval<FormatContext>().out())>::type out;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
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 ctx.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
size_t i = 0;
|
*out++ = '(';
|
||||||
detail::copy(formatting.prefix, out);
|
detail::for_each(values, format_each<FormatContext>{0, out});
|
||||||
|
*out++ = ')';
|
||||||
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
|
return out;
|
||||||
if (formatting.add_prepostfix_space) {
|
|
||||||
*out++ = ' ';
|
|
||||||
}
|
|
||||||
detail::copy(formatting.postfix, out);
|
|
||||||
|
|
||||||
return ctx.out();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename Char> struct is_range {
|
template <typename T, typename Char> struct is_range {
|
||||||
static FMT_CONSTEXPR_DECL const bool value =
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value &&
|
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
|
||||||
|
!detail::is_map<T>::value &&
|
||||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||||
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
||||||
};
|
};
|
||||||
|
@ -251,97 +585,167 @@ template <typename T, typename Char> struct is_range {
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<
|
struct formatter<
|
||||||
T, Char,
|
T, Char,
|
||||||
enable_if_t<fmt::is_range<T, Char>::value
|
enable_if_t<
|
||||||
// Workaround a bug in MSVC 2017 and earlier.
|
fmt::is_range<T, Char>::value
|
||||||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
|
// Workaround a bug in MSVC 2019 and earlier.
|
||||||
&& has_formatter<detail::value_type<T>, format_context>::value
|
#if !FMT_MSC_VER
|
||||||
|
&& (is_formattable<detail::value_type<T>, Char>::value ||
|
||||||
|
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
|
||||||
#endif
|
#endif
|
||||||
>> {
|
>> {
|
||||||
formatting_range<Char> formatting;
|
|
||||||
|
|
||||||
template <typename ParseContext>
|
|
||||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
|
||||||
return formatting.parse(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FormatContext>
|
|
||||||
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
|
|
||||||
auto out = detail::copy(formatting.prefix, ctx.out());
|
|
||||||
size_t i = 0;
|
|
||||||
auto it = values.begin();
|
|
||||||
auto end = values.end();
|
|
||||||
for (; it != end; ++it) {
|
|
||||||
if (i > 0) {
|
|
||||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
|
||||||
out = detail::copy(formatting.delimiter, out);
|
|
||||||
}
|
|
||||||
out = format_to(out,
|
|
||||||
detail::format_str_quoted(
|
|
||||||
(formatting.add_delimiter_spaces && i > 0), *it),
|
|
||||||
*it);
|
|
||||||
if (++i > formatting.range_length_limit) {
|
|
||||||
out = format_to(out, " ... <other elements>");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
|
||||||
return detail::copy(formatting.postfix, out);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename... T> struct tuple_arg_join : detail::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>
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename FormatContext, typename U,
|
||||||
|
FMT_ENABLE_IF(
|
||||||
|
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
|
||||||
|
const T, T>>::value)>
|
||||||
|
auto format(U& range, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
|
#ifdef FMT_DEPRECATED_BRACED_RANGES
|
||||||
|
Char prefix = '{';
|
||||||
|
Char postfix = '}';
|
||||||
|
#else
|
||||||
|
Char prefix = detail::is_set<T>::value ? '{' : '[';
|
||||||
|
Char postfix = detail::is_set<T>::value ? '}' : ']';
|
||||||
|
#endif
|
||||||
|
auto out = ctx.out();
|
||||||
|
*out++ = prefix;
|
||||||
|
int i = 0;
|
||||||
|
auto it = std::begin(range);
|
||||||
|
auto end = std::end(range);
|
||||||
|
for (; it != end; ++it) {
|
||||||
|
if (i > 0) out = detail::write_delimiter(out);
|
||||||
|
out = detail::write_range_entry<Char>(out, *it);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
*out++ = postfix;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct formatter<
|
||||||
|
T, Char,
|
||||||
|
enable_if_t<
|
||||||
|
detail::is_map<T>::value
|
||||||
|
// Workaround a bug in MSVC 2019 and earlier.
|
||||||
|
#if !FMT_MSC_VER
|
||||||
|
&& (is_formattable<detail::value_type<T>, Char>::value ||
|
||||||
|
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
|
||||||
|
#endif
|
||||||
|
>> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename FormatContext, typename U,
|
||||||
|
FMT_ENABLE_IF(
|
||||||
|
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
|
||||||
|
const T, T>>::value)>
|
||||||
|
auto format(U& map, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
|
auto out = ctx.out();
|
||||||
|
*out++ = '{';
|
||||||
|
int i = 0;
|
||||||
|
for (const auto& item : map) {
|
||||||
|
if (i > 0) out = detail::write_delimiter(out);
|
||||||
|
out = detail::write_range_entry<Char>(out, item.first);
|
||||||
|
*out++ = ':';
|
||||||
|
*out++ = ' ';
|
||||||
|
out = detail::write_range_entry<Char>(out, item.second);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
*out++ = '}';
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
||||||
|
const std::tuple<T...>& tuple;
|
||||||
|
basic_string_view<Char> sep;
|
||||||
|
|
||||||
|
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||||
|
: tuple(t), sep{s} {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename... T>
|
||||||
|
using tuple_arg_join = tuple_join_view<Char, T...>;
|
||||||
|
|
||||||
|
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
|
||||||
|
// support in tuple_join. It is disabled by default because of issues with
|
||||||
|
// the dynamic width and precision.
|
||||||
|
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
|
||||||
|
# define FMT_TUPLE_JOIN_SPECIFIERS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename Char, typename... T>
|
||||||
|
struct formatter<tuple_join_view<Char, T...>, Char> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
|
||||||
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
typename FormatContext::iterator format(
|
auto format(const tuple_join_view<Char, T...>& value,
|
||||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
|
FormatContext& ctx) const -> typename FormatContext::iterator {
|
||||||
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
|
return do_format(value, ctx,
|
||||||
|
std::integral_constant<size_t, sizeof...(T)>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename FormatContext, size_t... N>
|
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
|
||||||
typename FormatContext::iterator format(
|
|
||||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
template <typename ParseContext>
|
||||||
detail::index_sequence<N...>) {
|
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||||
return format_args(value, ctx, std::get<N>(value.tuple)...);
|
std::integral_constant<size_t, 0>)
|
||||||
|
-> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ParseContext, size_t N>
|
||||||
|
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||||
|
std::integral_constant<size_t, N>)
|
||||||
|
-> decltype(ctx.begin()) {
|
||||||
|
auto end = ctx.begin();
|
||||||
|
#if FMT_TUPLE_JOIN_SPECIFIERS
|
||||||
|
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
|
||||||
|
if (N > 1) {
|
||||||
|
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
|
||||||
|
if (end != end1)
|
||||||
|
FMT_THROW(format_error("incompatible format specs for tuple elements"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
typename FormatContext::iterator format_args(
|
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
|
||||||
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
|
std::integral_constant<size_t, 0>) const ->
|
||||||
// NOTE: for compilers that support C++17, this empty function instantiation
|
typename FormatContext::iterator {
|
||||||
// can be replaced with a constexpr branch in the variadic overload.
|
|
||||||
return ctx.out();
|
return ctx.out();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext, typename Arg, typename... Args>
|
template <typename FormatContext, size_t N>
|
||||||
typename FormatContext::iterator format_args(
|
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
||||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
std::integral_constant<size_t, N>) const ->
|
||||||
const Arg& arg, const Args&... args) {
|
typename FormatContext::iterator {
|
||||||
using base = formatter<typename std::decay<Arg>::type, Char>;
|
auto out = std::get<sizeof...(T) - N>(formatters_)
|
||||||
auto out = ctx.out();
|
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
|
||||||
out = base{}.format(arg, ctx);
|
if (N > 1) {
|
||||||
if (sizeof...(Args) > 0) {
|
|
||||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||||
ctx.advance_to(out);
|
ctx.advance_to(out);
|
||||||
return format_args(value, ctx, args...);
|
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Returns an object that formats `tuple` with elements separated by `sep`.
|
Returns an object that formats `tuple` with elements separated by `sep`.
|
||||||
|
@ -354,14 +758,15 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
|
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
|
||||||
string_view sep) {
|
-> tuple_join_view<char, T...> {
|
||||||
return {tuple, sep};
|
return {tuple, sep};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
|
||||||
wstring_view sep) {
|
basic_string_view<wchar_t> sep)
|
||||||
|
-> tuple_join_view<wchar_t, T...> {
|
||||||
return {tuple, sep};
|
return {tuple, sep};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,17 +782,12 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
arg_join<const T*, const T*, char> join(std::initializer_list<T> list,
|
auto join(std::initializer_list<T> list, string_view sep)
|
||||||
string_view sep) {
|
-> join_view<const T*, const T*> {
|
||||||
return join(std::begin(list), std::end(list), sep);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
|
|
||||||
wstring_view sep) {
|
|
||||||
return join(std::begin(list), std::end(list), sep);
|
return join(std::begin(list), std::end(list), sep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMT_MODULE_EXPORT_END
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_RANGES_H_
|
#endif // FMT_RANGES_H_
|
||||||
|
|
|
@ -0,0 +1,236 @@
|
||||||
|
// Formatting library for C++ - optional wchar_t and exotic character support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_XCHAR_H_
|
||||||
|
#define FMT_XCHAR_H_
|
||||||
|
|
||||||
|
#include <cwchar>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace detail {
|
||||||
|
template <typename T>
|
||||||
|
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
|
|
||||||
|
using wstring_view = basic_string_view<wchar_t>;
|
||||||
|
using wformat_parse_context = basic_format_parse_context<wchar_t>;
|
||||||
|
using wformat_context = buffer_context<wchar_t>;
|
||||||
|
using wformat_args = basic_format_args<wformat_context>;
|
||||||
|
using wmemory_buffer = basic_memory_buffer<wchar_t>;
|
||||||
|
|
||||||
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||||
|
// Workaround broken conversion on older gcc.
|
||||||
|
template <typename... Args> using wformat_string = wstring_view;
|
||||||
|
#else
|
||||||
|
template <typename... Args>
|
||||||
|
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <> struct is_char<wchar_t> : std::true_type {};
|
||||||
|
template <> struct is_char<detail::char8_type> : std::true_type {};
|
||||||
|
template <> struct is_char<char16_t> : std::true_type {};
|
||||||
|
template <> struct is_char<char32_t> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
|
||||||
|
const Args&... args) {
|
||||||
|
return {args...};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline namespace literals {
|
||||||
|
constexpr auto operator"" _format(const wchar_t* s, size_t n)
|
||||||
|
-> detail::udl_formatter<wchar_t> {
|
||||||
|
return {{s, n}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||||
|
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
|
||||||
|
return {s};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // namespace literals
|
||||||
|
|
||||||
|
template <typename It, typename Sentinel>
|
||||||
|
auto join(It begin, Sentinel end, wstring_view sep)
|
||||||
|
-> join_view<It, Sentinel, wchar_t> {
|
||||||
|
return {begin, end, sep};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Range>
|
||||||
|
auto join(Range&& range, wstring_view sep)
|
||||||
|
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
|
||||||
|
wchar_t> {
|
||||||
|
return join(std::begin(range), std::end(range), sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto join(std::initializer_list<T> list, wstring_view sep)
|
||||||
|
-> join_view<const T*, const T*, wchar_t> {
|
||||||
|
return join(std::begin(list), std::end(list), sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||||
|
auto vformat(basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||||
|
-> std::basic_string<Char> {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
detail::vformat_to(buffer, format_str, args);
|
||||||
|
return to_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass char_t as a default template parameter instead of using
|
||||||
|
// std::basic_string<char_t<S>> to reduce the symbol size.
|
||||||
|
template <typename S, typename... Args, typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||||
|
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
|
return vformat(to_string_view(format_str), vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Locale, typename S, typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
|
||||||
|
detail::is_exotic_char<Char>::value)>
|
||||||
|
inline auto vformat(
|
||||||
|
const Locale& loc, const S& format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||||
|
-> std::basic_string<Char> {
|
||||||
|
return detail::vformat(loc, to_string_view(format_str), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Locale, typename S, typename... Args,
|
||||||
|
typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
|
||||||
|
detail::is_exotic_char<Char>::value)>
|
||||||
|
inline auto format(const Locale& loc, const S& format_str, Args&&... args)
|
||||||
|
-> std::basic_string<Char> {
|
||||||
|
return detail::vformat(loc, to_string_view(format_str),
|
||||||
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
|
detail::is_exotic_char<Char>::value)>
|
||||||
|
auto vformat_to(OutputIt out, const S& format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||||
|
-> OutputIt {
|
||||||
|
auto&& buf = detail::get_buffer<Char>(out);
|
||||||
|
detail::vformat_to(buf, to_string_view(format_str), args);
|
||||||
|
return detail::get_iterator(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
|
detail::is_exotic_char<Char>::value)>
|
||||||
|
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
|
||||||
|
return vformat_to(out, to_string_view(fmt), vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args, typename Char, size_t SIZE,
|
||||||
|
typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||||
|
FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
|
||||||
|
const S& format_str, Args&&... args) ->
|
||||||
|
typename buffer_context<Char>::iterator {
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
|
detail::vformat_to(buf, to_string_view(format_str), vargs, {});
|
||||||
|
return detail::buffer_appender<Char>(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Locale, typename S, typename OutputIt, typename... Args,
|
||||||
|
typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
|
detail::is_locale<Locale>::value&&
|
||||||
|
detail::is_exotic_char<Char>::value)>
|
||||||
|
inline auto vformat_to(
|
||||||
|
OutputIt out, const Locale& loc, const S& format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
|
||||||
|
auto&& buf = detail::get_buffer<Char>(out);
|
||||||
|
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
|
||||||
|
return detail::get_iterator(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename OutputIt, typename Locale, typename S, typename... Args,
|
||||||
|
typename Char = char_t<S>,
|
||||||
|
bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
|
detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
|
||||||
|
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
|
||||||
|
Args&&... args) ->
|
||||||
|
typename std::enable_if<enable, OutputIt>::type {
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
|
return vformat_to(out, loc, to_string_view(format_str), vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
|
detail::is_exotic_char<Char>::value)>
|
||||||
|
inline auto vformat_to_n(
|
||||||
|
OutputIt out, size_t n, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||||
|
-> format_to_n_result<OutputIt> {
|
||||||
|
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
|
||||||
|
n);
|
||||||
|
detail::vformat_to(buf, format_str, args);
|
||||||
|
return {buf.out(), buf.count()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||||
|
detail::is_exotic_char<Char>::value)>
|
||||||
|
inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
|
||||||
|
const Args&... args) -> format_to_n_result<OutputIt> {
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
|
||||||
|
return vformat_to_n(out, n, to_string_view(fmt), vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args, typename Char = char_t<S>,
|
||||||
|
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
|
||||||
|
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
|
||||||
|
detail::counting_buffer<Char> buf;
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
|
||||||
|
detail::vformat_to(buf, to_string_view(fmt), vargs);
|
||||||
|
return buf.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
|
||||||
|
wmemory_buffer buffer;
|
||||||
|
detail::vformat_to(buffer, fmt, args);
|
||||||
|
buffer.push_back(L'\0');
|
||||||
|
if (std::fputws(buffer.data(), f) == -1)
|
||||||
|
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void vprint(wstring_view fmt, wformat_args args) {
|
||||||
|
vprint(stdout, fmt, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
|
||||||
|
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
|
||||||
|
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Converts *value* to ``std::wstring`` using the default format for type *T*.
|
||||||
|
*/
|
||||||
|
template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
|
||||||
|
return format(FMT_STRING(L"{}"), value);
|
||||||
|
}
|
||||||
|
FMT_MODULE_EXPORT_END
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_XCHAR_H_
|
|
@ -0,0 +1,99 @@
|
||||||
|
module;
|
||||||
|
#ifndef __cpp_modules
|
||||||
|
# error Module not supported.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// put all implementation-provided headers into the global module fragment
|
||||||
|
// to prevent attachment to this module
|
||||||
|
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
|
||||||
|
# define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
#if !defined(WIN32_LEAN_AND_MEAN) && defined(_WIN32)
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <chrono>
|
||||||
|
#include <climits>
|
||||||
|
#include <clocale>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <ctime>
|
||||||
|
#include <cwchar>
|
||||||
|
#include <exception>
|
||||||
|
#include <functional>
|
||||||
|
#include <iterator>
|
||||||
|
#include <limits>
|
||||||
|
#include <locale>
|
||||||
|
#include <memory>
|
||||||
|
#include <ostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <system_error>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#if _MSC_VER
|
||||||
|
# include <intrin.h>
|
||||||
|
#endif
|
||||||
|
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||||
|
# include <xlocale.h>
|
||||||
|
#endif
|
||||||
|
#if __has_include(<winapifamily.h>)
|
||||||
|
# include <winapifamily.h>
|
||||||
|
#endif
|
||||||
|
#if (__has_include(<fcntl.h>) || defined(__APPLE__) || \
|
||||||
|
defined(__linux__)) && \
|
||||||
|
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||||
|
# include <fcntl.h>
|
||||||
|
# include <sys/stat.h>
|
||||||
|
# include <sys/types.h>
|
||||||
|
# ifndef _WIN32
|
||||||
|
# include <unistd.h>
|
||||||
|
# else
|
||||||
|
# include <io.h>
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
export module fmt;
|
||||||
|
|
||||||
|
#define FMT_MODULE_EXPORT export
|
||||||
|
#define FMT_MODULE_EXPORT_BEGIN export {
|
||||||
|
#define FMT_MODULE_EXPORT_END }
|
||||||
|
#define FMT_BEGIN_DETAIL_NAMESPACE \
|
||||||
|
} \
|
||||||
|
namespace detail {
|
||||||
|
#define FMT_END_DETAIL_NAMESPACE \
|
||||||
|
} \
|
||||||
|
export {
|
||||||
|
// all library-provided declarations and definitions
|
||||||
|
// must be in the module purview to be exported
|
||||||
|
#include "fmt/args.h"
|
||||||
|
#include "fmt/chrono.h"
|
||||||
|
#include "fmt/color.h"
|
||||||
|
#include "fmt/compile.h"
|
||||||
|
#include "fmt/format.h"
|
||||||
|
#include "fmt/os.h"
|
||||||
|
#include "fmt/printf.h"
|
||||||
|
#include "fmt/xchar.h"
|
||||||
|
|
||||||
|
// gcc doesn't yet implement private module fragments
|
||||||
|
#if !FMT_GCC_VERSION
|
||||||
|
module : private;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "format.cc"
|
||||||
|
#include "os.cc"
|
|
@ -10,6 +10,52 @@
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
// DEPRECATED!
|
||||||
|
template <typename T = void> struct basic_data {
|
||||||
|
FMT_API static constexpr const char digits[100][2] = {
|
||||||
|
{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'},
|
||||||
|
{'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'},
|
||||||
|
{'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'},
|
||||||
|
{'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'},
|
||||||
|
{'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
|
||||||
|
{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'},
|
||||||
|
{'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'},
|
||||||
|
{'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'},
|
||||||
|
{'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'},
|
||||||
|
{'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
|
||||||
|
{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'},
|
||||||
|
{'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'},
|
||||||
|
{'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'},
|
||||||
|
{'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'},
|
||||||
|
{'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
|
||||||
|
{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'},
|
||||||
|
{'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};
|
||||||
|
FMT_API static constexpr const char hex_digits[] = "0123456789abcdef";
|
||||||
|
FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '};
|
||||||
|
FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1,
|
||||||
|
0};
|
||||||
|
FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1,
|
||||||
|
0};
|
||||||
|
FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
|
||||||
|
0x1000000u | ' '};
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef FMT_SHARED
|
||||||
|
// Required for -flto, -fivisibility=hidden and -shared to work
|
||||||
|
extern template struct basic_data<void>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __cplusplus < 201703L
|
||||||
|
// DEPRECATED! These are here only for ABI compatiblity.
|
||||||
|
template <typename T> constexpr const char basic_data<T>::digits[][2];
|
||||||
|
template <typename T> constexpr const char basic_data<T>::hex_digits[];
|
||||||
|
template <typename T> constexpr const char basic_data<T>::signs[];
|
||||||
|
template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
|
||||||
|
template <typename T>
|
||||||
|
constexpr const char basic_data<T>::right_padding_shifts[];
|
||||||
|
template <typename T> constexpr const unsigned basic_data<T>::prefixes[];
|
||||||
|
#endif
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
||||||
T value) {
|
T value) {
|
||||||
|
@ -23,9 +69,12 @@ int format_float(char* buf, std::size_t size, const char* format, int precision,
|
||||||
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
||||||
: snprintf_ptr(buf, size, format, precision, value);
|
: snprintf_ptr(buf, size, format, precision, value);
|
||||||
}
|
}
|
||||||
} // namespace detail
|
|
||||||
|
|
||||||
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
|
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
||||||
int (*instantiate_format_float)(double, int, detail::float_specs,
|
int (*instantiate_format_float)(double, int, detail::float_specs,
|
||||||
|
@ -38,12 +87,15 @@ template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
|
||||||
|
|
||||||
// Explicit instantiations for char.
|
// Explicit instantiations for char.
|
||||||
|
|
||||||
template FMT_API std::string detail::grouping_impl<char>(locale_ref);
|
template FMT_API auto detail::thousands_sep_impl(locale_ref)
|
||||||
template FMT_API char detail::thousands_sep_impl(locale_ref);
|
-> thousands_sep_result<char>;
|
||||||
template FMT_API char detail::decimal_point_impl(locale_ref);
|
template FMT_API char detail::decimal_point_impl(locale_ref);
|
||||||
|
|
||||||
template FMT_API void detail::buffer<char>::append(const char*, const char*);
|
template FMT_API void detail::buffer<char>::append(const char*, const char*);
|
||||||
|
|
||||||
|
// DEPRECATED!
|
||||||
|
// There is no correspondent extern template in format.h because of
|
||||||
|
// incompatibility between clang and gcc (#2377).
|
||||||
template FMT_API void detail::vformat_to(
|
template FMT_API void detail::vformat_to(
|
||||||
detail::buffer<char>&, string_view,
|
detail::buffer<char>&, string_view,
|
||||||
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
|
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
|
||||||
|
@ -60,10 +112,13 @@ template FMT_API int detail::format_float(long double, int, detail::float_specs,
|
||||||
|
|
||||||
// Explicit instantiations for wchar_t.
|
// Explicit instantiations for wchar_t.
|
||||||
|
|
||||||
template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref);
|
template FMT_API auto detail::thousands_sep_impl(locale_ref)
|
||||||
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
|
-> thousands_sep_result<wchar_t>;
|
||||||
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
|
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
|
||||||
|
|
||||||
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
|
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
|
||||||
const wchar_t*);
|
const wchar_t*);
|
||||||
|
|
||||||
|
template struct detail::basic_data<void>;
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
|
@ -25,21 +25,18 @@
|
||||||
# define WIN32_LEAN_AND_MEAN
|
# define WIN32_LEAN_AND_MEAN
|
||||||
# endif
|
# endif
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
# include <windows.h>
|
|
||||||
|
|
||||||
# define O_CREAT _O_CREAT
|
|
||||||
# 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
|
||||||
|
# ifndef S_IRGRP
|
||||||
# ifdef __MINGW32__
|
# define S_IRGRP 0
|
||||||
# define _SH_DENYNO 0x40
|
# endif
|
||||||
|
# ifndef S_IROTH
|
||||||
|
# define S_IROTH 0
|
||||||
# endif
|
# endif
|
||||||
# endif // _WIN32
|
# endif // _WIN32
|
||||||
#endif // FMT_USE_FCNTL
|
#endif // FMT_USE_FCNTL
|
||||||
|
@ -55,7 +52,7 @@
|
||||||
namespace {
|
namespace {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Return type of read and write functions.
|
// Return type of read and write functions.
|
||||||
using RWResult = int;
|
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.
|
||||||
|
@ -64,7 +61,7 @@ inline unsigned convert_rwcount(std::size_t count) {
|
||||||
}
|
}
|
||||||
#elif FMT_USE_FCNTL
|
#elif FMT_USE_FCNTL
|
||||||
// Return type of read and write functions.
|
// Return type of read and write functions.
|
||||||
using RWResult = ssize_t;
|
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
|
||||||
|
@ -73,14 +70,14 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
detail::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
|
detail::utf16_to_utf8::utf16_to_utf8(basic_string_view<wchar_t> s) {
|
||||||
if (int error_code = convert(s)) {
|
if (int error_code = convert(s)) {
|
||||||
FMT_THROW(windows_error(error_code,
|
FMT_THROW(windows_error(error_code,
|
||||||
"cannot convert string from UTF-16 to UTF-8"));
|
"cannot convert string from UTF-16 to UTF-8"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int detail::utf16_to_utf8::convert(wstring_view s) {
|
int detail::utf16_to_utf8::convert(basic_string_view<wchar_t> s) {
|
||||||
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
|
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
|
||||||
int s_size = static_cast<int>(s.size());
|
int s_size = static_cast<int>(s.size());
|
||||||
if (s_size == 0) {
|
if (s_size == 0) {
|
||||||
|
@ -101,46 +98,85 @@ int detail::utf16_to_utf8::convert(wstring_view s) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void windows_error::init(int err_code, string_view format_str,
|
namespace detail {
|
||||||
format_args args) {
|
|
||||||
error_code_ = err_code;
|
class system_message {
|
||||||
memory_buffer buffer;
|
system_message(const system_message&) = delete;
|
||||||
detail::format_windows_error(buffer, err_code, vformat(format_str, args));
|
void operator=(const system_message&) = delete;
|
||||||
std::runtime_error& base = *this;
|
|
||||||
base = std::runtime_error(to_string(buffer));
|
unsigned long result_;
|
||||||
|
wchar_t* message_;
|
||||||
|
|
||||||
|
static bool is_whitespace(wchar_t c) FMT_NOEXCEPT {
|
||||||
|
return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit system_message(unsigned long error_code)
|
||||||
|
: result_(0), message_(nullptr) {
|
||||||
|
result_ = FormatMessageW(
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
reinterpret_cast<wchar_t*>(&message_), 0, nullptr);
|
||||||
|
if (result_ != 0) {
|
||||||
|
while (result_ != 0 && is_whitespace(message_[result_ - 1])) {
|
||||||
|
--result_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~system_message() { LocalFree(message_); }
|
||||||
|
explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; }
|
||||||
|
operator basic_string_view<wchar_t>() const FMT_NOEXCEPT {
|
||||||
|
return basic_string_view<wchar_t>(message_, result_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class utf8_system_category final : public std::error_category {
|
||||||
|
public:
|
||||||
|
const char* name() const FMT_NOEXCEPT override { return "system"; }
|
||||||
|
std::string message(int error_code) const override {
|
||||||
|
system_message msg(error_code);
|
||||||
|
if (msg) {
|
||||||
|
utf16_to_utf8 utf8_message;
|
||||||
|
if (utf8_message.convert(msg) == ERROR_SUCCESS) {
|
||||||
|
return utf8_message.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "unknown error";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
FMT_API const std::error_category& system_category() FMT_NOEXCEPT {
|
||||||
|
static const detail::utf8_system_category category;
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::system_error vwindows_error(int err_code, string_view format_str,
|
||||||
|
format_args args) {
|
||||||
|
auto ec = std::error_code(err_code, system_category());
|
||||||
|
return std::system_error(ec, vformat(format_str, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
void detail::format_windows_error(detail::buffer<char>& out, int error_code,
|
void detail::format_windows_error(detail::buffer<char>& out, int error_code,
|
||||||
string_view message) FMT_NOEXCEPT {
|
const char* message) FMT_NOEXCEPT {
|
||||||
FMT_TRY {
|
FMT_TRY {
|
||||||
wmemory_buffer buf;
|
system_message msg(error_code);
|
||||||
buf.resize(inline_buffer_size);
|
if (msg) {
|
||||||
for (;;) {
|
utf16_to_utf8 utf8_message;
|
||||||
wchar_t* system_message = &buf[0];
|
if (utf8_message.convert(msg) == ERROR_SUCCESS) {
|
||||||
int result = FormatMessageW(
|
format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
|
return;
|
||||||
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message,
|
|
||||||
static_cast<uint32_t>(buf.size()), nullptr);
|
|
||||||
if (result != 0) {
|
|
||||||
utf16_to_utf8 utf8_message;
|
|
||||||
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
|
||||||
format_to(buffer_appender<char>(out), "{}: {}", message,
|
|
||||||
utf8_message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
||||||
break; // Can't get error message, report error code instead.
|
|
||||||
buf.resize(buf.size() * 2);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FMT_CATCH(...) {}
|
FMT_CATCH(...) {}
|
||||||
format_error_code(out, error_code, message);
|
format_error_code(out, error_code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void report_windows_error(int error_code,
|
void report_windows_error(int error_code, const char* message) FMT_NOEXCEPT {
|
||||||
fmt::string_view message) FMT_NOEXCEPT {
|
|
||||||
report_error(detail::format_windows_error, error_code, message);
|
report_error(detail::format_windows_error, error_code, message);
|
||||||
}
|
}
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
@ -175,7 +211,10 @@ int buffered_file::fileno() const {
|
||||||
|
|
||||||
#if FMT_USE_FCNTL
|
#if FMT_USE_FCNTL
|
||||||
file::file(cstring_view path, int oflag) {
|
file::file(cstring_view path, int oflag) {
|
||||||
int mode = S_IRUSR | S_IWUSR;
|
# ifdef _WIN32
|
||||||
|
using mode_t = int;
|
||||||
|
# endif
|
||||||
|
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
|
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
|
||||||
|
@ -229,14 +268,14 @@ long long file::size() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
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) FMT_THROW(system_error(errno, "cannot read from file"));
|
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
|
||||||
return detail::to_unsigned(result);
|
return detail::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) FMT_THROW(system_error(errno, "cannot write to file"));
|
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
|
||||||
return detail::to_unsigned(result);
|
return detail::to_unsigned(result);
|
||||||
|
@ -260,10 +299,10 @@ void file::dup2(int fd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT {
|
void file::dup2(int fd, std::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) ec = error_code(errno);
|
if (result == -1) ec = std::error_code(errno, std::generic_category());
|
||||||
}
|
}
|
||||||
|
|
||||||
void file::pipe(file& read_end, file& write_end) {
|
void file::pipe(file& read_end, file& write_end) {
|
||||||
|
@ -315,7 +354,7 @@ long getpagesize() {
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ostream::grow(size_t) {
|
FMT_API void ostream::grow(size_t) {
|
||||||
if (this->size() == this->capacity()) flush();
|
if (this->size() == this->capacity()) flush();
|
||||||
}
|
}
|
||||||
#endif // FMT_USE_FCNTL
|
#endif // FMT_USE_FCNTL
|
||||||
|
|
|
@ -6,11 +6,8 @@ clone_depth: 1
|
||||||
|
|
||||||
image:
|
image:
|
||||||
- Visual Studio 2015
|
- Visual Studio 2015
|
||||||
- Visual Studio 2019
|
|
||||||
- Visual Studio 2017
|
|
||||||
|
|
||||||
platform:
|
platform:
|
||||||
- Win32
|
|
||||||
- x64
|
- x64
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
@ -18,13 +15,6 @@ environment:
|
||||||
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
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
build --symlink_prefix=/ # Out of source build
|
|
@ -0,0 +1 @@
|
||||||
|
4.2.1
|
|
@ -0,0 +1,29 @@
|
||||||
|
cc_library(
|
||||||
|
name = "fmt",
|
||||||
|
srcs = [
|
||||||
|
#"src/fmt.cc", # No C++ module support
|
||||||
|
"src/format.cc",
|
||||||
|
"src/os.cc",
|
||||||
|
],
|
||||||
|
hdrs = [
|
||||||
|
"include/fmt/args.h",
|
||||||
|
"include/fmt/chrono.h",
|
||||||
|
"include/fmt/color.h",
|
||||||
|
"include/fmt/compile.h",
|
||||||
|
"include/fmt/core.h",
|
||||||
|
"include/fmt/format.h",
|
||||||
|
"include/fmt/format-inl.h",
|
||||||
|
"include/fmt/locale.h",
|
||||||
|
"include/fmt/os.h",
|
||||||
|
"include/fmt/ostream.h",
|
||||||
|
"include/fmt/printf.h",
|
||||||
|
"include/fmt/ranges.h",
|
||||||
|
"include/fmt/xchar.h",
|
||||||
|
],
|
||||||
|
includes = [
|
||||||
|
"include",
|
||||||
|
"src",
|
||||||
|
],
|
||||||
|
strip_include_prefix = "include",
|
||||||
|
visibility = ["//visibility:public"],
|
||||||
|
)
|
|
@ -0,0 +1,73 @@
|
||||||
|
# Bazel support
|
||||||
|
|
||||||
|
To get [Bazel](https://bazel.build/) working with {fmt} you can copy the files `BUILD.bazel`, `WORKSPACE.bazel`, `.bazelrc`, and `.bazelversion` from this folder (`support/bazel`) to the root folder of this project. This way {fmt} gets bazelized and can be used with Bazel (e.g. doing a `bazel build //...` on {fmt}).
|
||||||
|
|
||||||
|
## Using {fmt} as a dependency
|
||||||
|
|
||||||
|
The following minimal example shows how to use {fmt} as a dependency within a Bazel project.
|
||||||
|
|
||||||
|
The following file structure is assumed:
|
||||||
|
|
||||||
|
```
|
||||||
|
example
|
||||||
|
├── BUILD.bazel
|
||||||
|
├── main.cpp
|
||||||
|
└── WORKSPACE.bazel
|
||||||
|
```
|
||||||
|
|
||||||
|
*main.cpp*:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#include "fmt/core.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
fmt::print("The answer is {}\n", 42);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The expected output of this example is `The answer is 42`.
|
||||||
|
|
||||||
|
*WORKSPACE.bazel*:
|
||||||
|
|
||||||
|
```python
|
||||||
|
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||||
|
|
||||||
|
git_repository(
|
||||||
|
name = "fmt",
|
||||||
|
branch = "master",
|
||||||
|
remote = "https://github.com/fmtlib/fmt",
|
||||||
|
patch_cmds = [
|
||||||
|
"mv support/bazel/.bazelrc .bazelrc",
|
||||||
|
"mv support/bazel/.bazelversion .bazelversion",
|
||||||
|
"mv support/bazel/BUILD.bazel BUILD.bazel",
|
||||||
|
"mv support/bazel/WORKSPACE.bazel WORKSPACE.bazel",
|
||||||
|
],
|
||||||
|
# Windows-related patch commands are only needed in the case MSYS2 is not installed.
|
||||||
|
# More details about the installation process of MSYS2 on Windows systems can be found here:
|
||||||
|
# https://docs.bazel.build/versions/main/install-windows.html#installing-compilers-and-language-runtimes
|
||||||
|
# Even if MSYS2 is installed the Windows related patch commands can still be used.
|
||||||
|
patch_cmds_win = [
|
||||||
|
"Move-Item -Path support/bazel/.bazelrc -Destination .bazelrc",
|
||||||
|
"Move-Item -Path support/bazel/.bazelversion -Destination .bazelversion",
|
||||||
|
"Move-Item -Path support/bazel/BUILD.bazel -Destination BUILD.bazel",
|
||||||
|
"Move-Item -Path support/bazel/WORKSPACE.bazel -Destination WORKSPACE.bazel",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
In the *WORKSPACE* file, the {fmt} GitHub repository is fetched. Using the attribute `patch_cmds` the files `BUILD.bazel`, `WORKSPACE.bazel`, `.bazelrc`, and `.bazelversion` are moved to the root of the {fmt} repository. This way the {fmt} repository is recognized as a bazelized workspace.
|
||||||
|
|
||||||
|
*BUILD.bazel*:
|
||||||
|
|
||||||
|
```python
|
||||||
|
cc_binary(
|
||||||
|
name = "Demo",
|
||||||
|
srcs = ["main.cpp"],
|
||||||
|
deps = ["@fmt"],
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
The *BUILD* file defines a binary named `Demo` that has a dependency to {fmt}.
|
||||||
|
|
||||||
|
To execute the binary you can run `bazel run //:Demo`.
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
workspace(name = "fmt")
|
|
@ -0,0 +1,58 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# Build the documentation in CI.
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import errno, os, shutil, subprocess, sys, urllib
|
||||||
|
from subprocess import call, check_call, Popen, PIPE, STDOUT
|
||||||
|
|
||||||
|
def rmtree_if_exists(dir):
|
||||||
|
try:
|
||||||
|
shutil.rmtree(dir)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno == errno.ENOENT:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Build the docs.
|
||||||
|
fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
sys.path.insert(0, os.path.join(fmt_dir, 'doc'))
|
||||||
|
import build
|
||||||
|
build.create_build_env()
|
||||||
|
html_dir = build.build_docs()
|
||||||
|
|
||||||
|
repo = 'fmtlib.github.io'
|
||||||
|
branch = os.environ['GITHUB_REF']
|
||||||
|
is_ci = 'CI' in os.environ
|
||||||
|
if is_ci and branch != 'refs/heads/master':
|
||||||
|
print('Branch: ' + branch)
|
||||||
|
exit(0) # Ignore non-master branches
|
||||||
|
if is_ci and 'KEY' not in os.environ:
|
||||||
|
# Don't update the repo if building in CI from an account that doesn't have
|
||||||
|
# push access.
|
||||||
|
print('Skipping update of ' + repo)
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
# Clone the fmtlib.github.io repo.
|
||||||
|
rmtree_if_exists(repo)
|
||||||
|
git_url = 'https://github.com/' if is_ci else 'git@github.com:'
|
||||||
|
check_call(['git', 'clone', git_url + 'fmtlib/{}.git'.format(repo)])
|
||||||
|
|
||||||
|
# Copy docs to the repo.
|
||||||
|
target_dir = os.path.join(repo, 'dev')
|
||||||
|
rmtree_if_exists(target_dir)
|
||||||
|
shutil.copytree(html_dir, target_dir, ignore=shutil.ignore_patterns('.*'))
|
||||||
|
if is_ci:
|
||||||
|
check_call(['git', 'config', '--global', 'user.name', 'fmtbot'])
|
||||||
|
check_call(['git', 'config', '--global', 'user.email', 'viz@fmt.dev'])
|
||||||
|
|
||||||
|
# Push docs to GitHub pages.
|
||||||
|
check_call(['git', 'add', '--all'], cwd=repo)
|
||||||
|
if call(['git', 'diff-index', '--quiet', 'HEAD'], cwd=repo):
|
||||||
|
check_call(['git', 'commit', '-m', 'Update documentation'], cwd=repo)
|
||||||
|
cmd = 'git push'
|
||||||
|
if is_ci:
|
||||||
|
cmd += ' https://$KEY@github.com/fmtlib/fmtlib.github.io.git master'
|
||||||
|
p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd=repo)
|
||||||
|
# Print the output without the key.
|
||||||
|
print(p.communicate()[0].decode('utf-8').replace(os.environ['KEY'], '$KEY'))
|
||||||
|
if p.returncode != 0:
|
||||||
|
raise subprocess.CalledProcessError(p.returncode, cmd)
|
|
@ -1,3 +1,4 @@
|
||||||
|
import java.nio.file.Paths
|
||||||
|
|
||||||
// General gradle arguments for root project
|
// General gradle arguments for root project
|
||||||
buildscript {
|
buildscript {
|
||||||
|
@ -7,24 +8,25 @@ buildscript {
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
//
|
//
|
||||||
// https://developer.android.com/studio/releases/gradle-plugin
|
// https://developer.android.com/studio/releases/gradle-plugin#updating-gradle
|
||||||
//
|
//
|
||||||
// Notice that 3.3.0 here is the version of [Android Gradle Plugin]
|
// Notice that 4.0.0 here is the version of [Android Gradle Plugin]
|
||||||
// Accroding to URL above you will need Gradle 5.0 or higher
|
// Accroding to URL above you will need Gradle 6.1 or higher
|
||||||
//
|
//
|
||||||
// If you are using Android Studio, and it is using Gradle's lower
|
classpath "com.android.tools.build:gradle:4.1.1"
|
||||||
// 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 {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output: Shared library (.so) for Android
|
|
||||||
apply plugin: 'com.android.library'
|
|
||||||
|
|
||||||
|
// Project's root where CMakeLists.txt exists: rootDir/support/.cxx -> rootDir
|
||||||
|
def rootDir = Paths.get(project.buildDir.getParent()).getParent()
|
||||||
|
println("rootDir: ${rootDir}")
|
||||||
|
|
||||||
|
// Output: Shared library (.so) for Android
|
||||||
|
apply plugin: "com.android.library"
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 25 // Android 7.0
|
compileSdkVersion 25 // Android 7.0
|
||||||
|
|
||||||
|
@ -41,13 +43,13 @@ android {
|
||||||
include "arm64-v8a", "armeabi-v7a", "x86_64"
|
include "arm64-v8a", "armeabi-v7a", "x86_64"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ndkVersion "21.3.6528147" // ANDROID_NDK_HOME is deprecated. Be explicit
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 21 // Android 5.0+
|
minSdkVersion 21 // Android 5.0+
|
||||||
targetSdkVersion 25 // Follow Compile SDK
|
targetSdkVersion 25 // Follow Compile SDK
|
||||||
versionCode 21 // Follow release count
|
versionCode 34 // Follow release count
|
||||||
versionName "5.3.0" // Follow Official version
|
versionName "7.1.2" // Follow Official version
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
|
||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
|
@ -56,9 +58,9 @@ android {
|
||||||
arguments "-DFMT_TEST=false" // Skip test
|
arguments "-DFMT_TEST=false" // Skip test
|
||||||
arguments "-DFMT_DOC=false" // Skip document
|
arguments "-DFMT_DOC=false" // Skip document
|
||||||
cppFlags "-std=c++17"
|
cppFlags "-std=c++17"
|
||||||
|
targets "fmt"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println("Gradle CMake Plugin: ")
|
|
||||||
println(externalNativeBuild.cmake.cppFlags)
|
println(externalNativeBuild.cmake.cppFlags)
|
||||||
println(externalNativeBuild.cmake.arguments)
|
println(externalNativeBuild.cmake.arguments)
|
||||||
}
|
}
|
||||||
|
@ -69,16 +71,27 @@ android {
|
||||||
// neighbor of the top level cmake
|
// neighbor of the top level cmake
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
path "../CMakeLists.txt"
|
version "3.10.0+"
|
||||||
|
path "${rootDir}/CMakeLists.txt"
|
||||||
// buildStagingDirectory "./build" // Custom path for cmake output
|
// buildStagingDirectory "./build" // Custom path for cmake output
|
||||||
}
|
}
|
||||||
//println(cmake.path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets{
|
sourceSets{
|
||||||
// Android Manifest for Gradle
|
// Android Manifest for Gradle
|
||||||
main {
|
main {
|
||||||
manifest.srcFile 'AndroidManifest.xml'
|
manifest.srcFile "AndroidManifest.xml"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://developer.android.com/studio/build/native-dependencies#build_system_configuration
|
||||||
|
buildFeatures {
|
||||||
|
prefab true
|
||||||
|
prefabPublishing true
|
||||||
|
}
|
||||||
|
prefab {
|
||||||
|
fmt {
|
||||||
|
headers "${rootDir}/include"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,20 +101,32 @@ assemble.doLast
|
||||||
// Instead of `ninja install`, Gradle will deploy the files.
|
// Instead of `ninja install`, Gradle will deploy the files.
|
||||||
// We are doing this since FMT is dependent to the ANDROID_STL after build
|
// We are doing this since FMT is dependent to the ANDROID_STL after build
|
||||||
copy {
|
copy {
|
||||||
from 'build/intermediates/cmake'
|
from "build/intermediates/cmake"
|
||||||
into '../libs'
|
into "${rootDir}/libs"
|
||||||
}
|
}
|
||||||
// Copy debug binaries
|
// Copy debug binaries
|
||||||
copy {
|
copy {
|
||||||
from '../libs/debug/obj'
|
from "${rootDir}/libs/debug/obj"
|
||||||
into '../libs/debug'
|
into "${rootDir}/libs/debug"
|
||||||
}
|
}
|
||||||
// Copy Release binaries
|
// Copy Release binaries
|
||||||
copy {
|
copy {
|
||||||
from '../libs/release/obj'
|
from "${rootDir}/libs/release/obj"
|
||||||
into '../libs/release'
|
into "${rootDir}/libs/release"
|
||||||
}
|
}
|
||||||
// Remove empty directory
|
// Remove empty directory
|
||||||
delete '../libs/debug/obj'
|
delete "${rootDir}/libs/debug/obj"
|
||||||
delete '../libs/release/obj'
|
delete "${rootDir}/libs/release/obj"
|
||||||
|
|
||||||
|
// Copy AAR files. Notice that the aar is named after the folder of this script.
|
||||||
|
copy {
|
||||||
|
from "build/outputs/aar/support-release.aar"
|
||||||
|
into "${rootDir}/libs"
|
||||||
|
rename "support-release.aar", "fmt-release.aar"
|
||||||
|
}
|
||||||
|
copy {
|
||||||
|
from "build/outputs/aar/support-debug.aar"
|
||||||
|
into "${rootDir}/libs"
|
||||||
|
rename "support-debug.aar", "fmt-debug.aar"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
"""Manage site and releases.
|
"""Manage site and releases.
|
||||||
|
|
||||||
|
@ -163,6 +163,13 @@ def update_site(env):
|
||||||
if version.startswith('7.'):
|
if version.startswith('7.'):
|
||||||
b.data = b.data.replace(', std::size_t', ', size_t')
|
b.data = b.data.replace(', std::size_t', ', size_t')
|
||||||
b.data = b.data.replace('join(It, It', 'join(It, Sentinel')
|
b.data = b.data.replace('join(It, It', 'join(It, Sentinel')
|
||||||
|
if version.startswith('7.1.'):
|
||||||
|
b.data = b.data.replace(', std::size_t', ', size_t')
|
||||||
|
b.data = b.data.replace('join(It, It', 'join(It, Sentinel')
|
||||||
|
b.data = b.data.replace(
|
||||||
|
'fmt::format_to(OutputIt, const S&, Args&&...)',
|
||||||
|
'fmt::format_to(OutputIt, const S&, Args&&...) -> ' +
|
||||||
|
'typename std::enable_if<enable, OutputIt>::type')
|
||||||
b.data = b.data.replace('aa long', 'a long')
|
b.data = b.data.replace('aa long', 'a long')
|
||||||
b.data = b.data.replace('serveral', 'several')
|
b.data = b.data.replace('serveral', 'several')
|
||||||
if version.startswith('6.2.'):
|
if version.startswith('6.2.'):
|
||||||
|
@ -233,7 +240,7 @@ def release(args):
|
||||||
# Update the version in the changelog.
|
# Update the version in the changelog.
|
||||||
title_len = 0
|
title_len = 0
|
||||||
for line in fileinput.input(changelog_path, inplace=True):
|
for line in fileinput.input(changelog_path, inplace=True):
|
||||||
if line.decode('utf-8').startswith(version + ' - TBD'):
|
if line.startswith(version + ' - TBD'):
|
||||||
line = version + ' - ' + datetime.date.today().isoformat()
|
line = version + ' - ' + datetime.date.today().isoformat()
|
||||||
title_len = len(line)
|
title_len = len(line)
|
||||||
line += '\n'
|
line += '\n'
|
||||||
|
@ -263,9 +270,9 @@ def release(args):
|
||||||
|
|
||||||
# Create a release on GitHub.
|
# Create a release on GitHub.
|
||||||
fmt_repo.push('origin', 'release')
|
fmt_repo.push('origin', 'release')
|
||||||
params = {'access_token': os.getenv('FMT_TOKEN')}
|
auth_headers = {'Authorization': 'token ' + os.getenv('FMT_TOKEN')}
|
||||||
r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases',
|
r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases',
|
||||||
params=params,
|
headers=auth_headers,
|
||||||
data=json.dumps({'tag_name': version,
|
data=json.dumps({'tag_name': version,
|
||||||
'target_commitish': 'release',
|
'target_commitish': 'release',
|
||||||
'body': changes, 'draft': True}))
|
'body': changes, 'draft': True}))
|
||||||
|
@ -276,8 +283,8 @@ def release(args):
|
||||||
package = 'fmt-{}.zip'.format(version)
|
package = 'fmt-{}.zip'.format(version)
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
'{}/{}/assets?name={}'.format(uploads_url, id, package),
|
'{}/{}/assets?name={}'.format(uploads_url, id, package),
|
||||||
headers={'Content-Type': 'application/zip'},
|
headers={'Content-Type': 'application/zip'} | auth_headers,
|
||||||
params=params, data=open('build/fmt/' + package, 'rb'))
|
data=open('build/fmt/' + package, 'rb'))
|
||||||
if r.status_code != 201:
|
if r.status_code != 201:
|
||||||
raise Exception('Failed to upload an asset ' + str(r))
|
raise Exception('Failed to upload an asset ' + str(r))
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# This script is based on
|
||||||
|
# https://github.com/rust-lang/rust/blob/master/library/core/src/unicode/printable.py
|
||||||
|
# distributed under https://github.com/rust-lang/rust/blob/master/LICENSE-MIT.
|
||||||
|
|
||||||
|
# This script uses the following Unicode tables:
|
||||||
|
# - UnicodeData.txt
|
||||||
|
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
NUM_CODEPOINTS=0x110000
|
||||||
|
|
||||||
|
def to_ranges(iter):
|
||||||
|
current = None
|
||||||
|
for i in iter:
|
||||||
|
if current is None or i != current[1] or i in (0x10000, 0x20000):
|
||||||
|
if current is not None:
|
||||||
|
yield tuple(current)
|
||||||
|
current = [i, i + 1]
|
||||||
|
else:
|
||||||
|
current[1] += 1
|
||||||
|
if current is not None:
|
||||||
|
yield tuple(current)
|
||||||
|
|
||||||
|
def get_escaped(codepoints):
|
||||||
|
for c in codepoints:
|
||||||
|
if (c.class_ or "Cn") in "Cc Cf Cs Co Cn Zl Zp Zs".split() and c.value != ord(' '):
|
||||||
|
yield c.value
|
||||||
|
|
||||||
|
def get_file(f):
|
||||||
|
try:
|
||||||
|
return open(os.path.basename(f))
|
||||||
|
except FileNotFoundError:
|
||||||
|
subprocess.run(["curl", "-O", f], check=True)
|
||||||
|
return open(os.path.basename(f))
|
||||||
|
|
||||||
|
Codepoint = namedtuple('Codepoint', 'value class_')
|
||||||
|
|
||||||
|
def get_codepoints(f):
|
||||||
|
r = csv.reader(f, delimiter=";")
|
||||||
|
prev_codepoint = 0
|
||||||
|
class_first = None
|
||||||
|
for row in r:
|
||||||
|
codepoint = int(row[0], 16)
|
||||||
|
name = row[1]
|
||||||
|
class_ = row[2]
|
||||||
|
|
||||||
|
if class_first is not None:
|
||||||
|
if not name.endswith("Last>"):
|
||||||
|
raise ValueError("Missing Last after First")
|
||||||
|
|
||||||
|
for c in range(prev_codepoint + 1, codepoint):
|
||||||
|
yield Codepoint(c, class_first)
|
||||||
|
|
||||||
|
class_first = None
|
||||||
|
if name.endswith("First>"):
|
||||||
|
class_first = class_
|
||||||
|
|
||||||
|
yield Codepoint(codepoint, class_)
|
||||||
|
prev_codepoint = codepoint
|
||||||
|
|
||||||
|
if class_first is not None:
|
||||||
|
raise ValueError("Missing Last after First")
|
||||||
|
|
||||||
|
for c in range(prev_codepoint + 1, NUM_CODEPOINTS):
|
||||||
|
yield Codepoint(c, None)
|
||||||
|
|
||||||
|
def compress_singletons(singletons):
|
||||||
|
uppers = [] # (upper, # items in lowers)
|
||||||
|
lowers = []
|
||||||
|
|
||||||
|
for i in singletons:
|
||||||
|
upper = i >> 8
|
||||||
|
lower = i & 0xff
|
||||||
|
if len(uppers) == 0 or uppers[-1][0] != upper:
|
||||||
|
uppers.append((upper, 1))
|
||||||
|
else:
|
||||||
|
upper, count = uppers[-1]
|
||||||
|
uppers[-1] = upper, count + 1
|
||||||
|
lowers.append(lower)
|
||||||
|
|
||||||
|
return uppers, lowers
|
||||||
|
|
||||||
|
def compress_normal(normal):
|
||||||
|
# lengths 0x00..0x7f are encoded as 00, 01, ..., 7e, 7f
|
||||||
|
# lengths 0x80..0x7fff are encoded as 80 80, 80 81, ..., ff fe, ff ff
|
||||||
|
compressed = [] # [truelen, (truelenaux), falselen, (falselenaux)]
|
||||||
|
|
||||||
|
prev_start = 0
|
||||||
|
for start, count in normal:
|
||||||
|
truelen = start - prev_start
|
||||||
|
falselen = count
|
||||||
|
prev_start = start + count
|
||||||
|
|
||||||
|
assert truelen < 0x8000 and falselen < 0x8000
|
||||||
|
entry = []
|
||||||
|
if truelen > 0x7f:
|
||||||
|
entry.append(0x80 | (truelen >> 8))
|
||||||
|
entry.append(truelen & 0xff)
|
||||||
|
else:
|
||||||
|
entry.append(truelen & 0x7f)
|
||||||
|
if falselen > 0x7f:
|
||||||
|
entry.append(0x80 | (falselen >> 8))
|
||||||
|
entry.append(falselen & 0xff)
|
||||||
|
else:
|
||||||
|
entry.append(falselen & 0x7f)
|
||||||
|
|
||||||
|
compressed.append(entry)
|
||||||
|
|
||||||
|
return compressed
|
||||||
|
|
||||||
|
def print_singletons(uppers, lowers, uppersname, lowersname):
|
||||||
|
print(" static constexpr singleton {}[] = {{".format(uppersname))
|
||||||
|
for u, c in uppers:
|
||||||
|
print(" {{{:#04x}, {}}},".format(u, c))
|
||||||
|
print(" };")
|
||||||
|
print(" static constexpr unsigned char {}[] = {{".format(lowersname))
|
||||||
|
for i in range(0, len(lowers), 8):
|
||||||
|
print(" {}".format(" ".join("{:#04x},".format(l) for l in lowers[i:i+8])))
|
||||||
|
print(" };")
|
||||||
|
|
||||||
|
def print_normal(normal, normalname):
|
||||||
|
print(" static constexpr unsigned char {}[] = {{".format(normalname))
|
||||||
|
for v in normal:
|
||||||
|
print(" {}".format(" ".join("{:#04x},".format(i) for i in v)))
|
||||||
|
print(" };")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
file = get_file("https://www.unicode.org/Public/UNIDATA/UnicodeData.txt")
|
||||||
|
|
||||||
|
codepoints = get_codepoints(file)
|
||||||
|
|
||||||
|
CUTOFF=0x10000
|
||||||
|
singletons0 = []
|
||||||
|
singletons1 = []
|
||||||
|
normal0 = []
|
||||||
|
normal1 = []
|
||||||
|
extra = []
|
||||||
|
|
||||||
|
for a, b in to_ranges(get_escaped(codepoints)):
|
||||||
|
if a > 2 * CUTOFF:
|
||||||
|
extra.append((a, b - a))
|
||||||
|
elif a == b - 1:
|
||||||
|
if a & CUTOFF:
|
||||||
|
singletons1.append(a & ~CUTOFF)
|
||||||
|
else:
|
||||||
|
singletons0.append(a)
|
||||||
|
elif a == b - 2:
|
||||||
|
if a & CUTOFF:
|
||||||
|
singletons1.append(a & ~CUTOFF)
|
||||||
|
singletons1.append((a + 1) & ~CUTOFF)
|
||||||
|
else:
|
||||||
|
singletons0.append(a)
|
||||||
|
singletons0.append(a + 1)
|
||||||
|
else:
|
||||||
|
if a >= 2 * CUTOFF:
|
||||||
|
extra.append((a, b - a))
|
||||||
|
elif a & CUTOFF:
|
||||||
|
normal1.append((a & ~CUTOFF, b - a))
|
||||||
|
else:
|
||||||
|
normal0.append((a, b - a))
|
||||||
|
|
||||||
|
singletons0u, singletons0l = compress_singletons(singletons0)
|
||||||
|
singletons1u, singletons1l = compress_singletons(singletons1)
|
||||||
|
normal0 = compress_normal(normal0)
|
||||||
|
normal1 = compress_normal(normal1)
|
||||||
|
|
||||||
|
print("""\
|
||||||
|
inline auto is_printable(uint32_t cp) -> bool {\
|
||||||
|
""")
|
||||||
|
print_singletons(singletons0u, singletons0l, 'singletons0', 'singletons0_lower')
|
||||||
|
print_singletons(singletons1u, singletons1l, 'singletons1', 'singletons1_lower')
|
||||||
|
print_normal(normal0, 'normal0')
|
||||||
|
print_normal(normal1, 'normal1')
|
||||||
|
print("""\
|
||||||
|
auto lower = static_cast<uint16_t>(cp);
|
||||||
|
if (cp < 0x10000) {
|
||||||
|
return is_printable(lower, singletons0,
|
||||||
|
sizeof(singletons0) / sizeof(*singletons0),
|
||||||
|
singletons0_lower, normal0, sizeof(normal0));
|
||||||
|
}
|
||||||
|
if (cp < 0x20000) {
|
||||||
|
return is_printable(lower, singletons1,
|
||||||
|
sizeof(singletons1) / sizeof(*singletons1),
|
||||||
|
singletons1_lower, normal1, sizeof(normal1));
|
||||||
|
}\
|
||||||
|
""")
|
||||||
|
for a, b in extra:
|
||||||
|
print(" if (0x{:x} <= cp && cp < 0x{:x}) return false;".format(a, a + b))
|
||||||
|
print("""\
|
||||||
|
return cp < 0x{:x};
|
||||||
|
}}\
|
||||||
|
""".format(NUM_CODEPOINTS))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -65,7 +65,7 @@ class Translator(nodes.NodeVisitor):
|
||||||
self.write('\n\n')
|
self.write('\n\n')
|
||||||
|
|
||||||
def visit_paragraph(self, node):
|
def visit_paragraph(self, node):
|
||||||
pass
|
self.write('\n\n')
|
||||||
|
|
||||||
def depart_paragraph(self, node):
|
def depart_paragraph(self, node):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# Build the project on Travis CI.
|
|
||||||
|
|
||||||
from __future__ import print_function
|
|
||||||
import errno, os, shutil, subprocess, sys, urllib
|
|
||||||
from subprocess import call, check_call, Popen, PIPE, STDOUT
|
|
||||||
|
|
||||||
def rmtree_if_exists(dir):
|
|
||||||
try:
|
|
||||||
shutil.rmtree(dir)
|
|
||||||
except OSError as e:
|
|
||||||
if e.errno == errno.ENOENT:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def makedirs_if_not_exist(dir):
|
|
||||||
try:
|
|
||||||
os.makedirs(dir)
|
|
||||||
except OSError as e:
|
|
||||||
if e.errno != errno.EEXIST:
|
|
||||||
raise
|
|
||||||
|
|
||||||
def install_dependencies():
|
|
||||||
branch = os.environ['TRAVIS_BRANCH']
|
|
||||||
if branch != 'master':
|
|
||||||
print('Branch: ' + branch)
|
|
||||||
exit(0) # Ignore non-master branches
|
|
||||||
check_call('curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key ' +
|
|
||||||
'| sudo apt-key add -', shell=True)
|
|
||||||
check_call('echo "deb https://deb.nodesource.com/node_0.10 precise main" ' +
|
|
||||||
'| sudo tee /etc/apt/sources.list.d/nodesource.list', shell=True)
|
|
||||||
check_call(['sudo', 'apt-get', 'update'])
|
|
||||||
check_call(['sudo', 'apt-get', 'install', 'python-virtualenv', 'nodejs'])
|
|
||||||
check_call(['sudo', 'npm', 'install', '-g', 'less@2.6.1', 'less-plugin-clean-css'])
|
|
||||||
deb_file = 'doxygen_1.8.6-2_amd64.deb'
|
|
||||||
urllib.urlretrieve('http://mirrors.kernel.org/ubuntu/pool/main/d/doxygen/' +
|
|
||||||
deb_file, deb_file)
|
|
||||||
check_call(['sudo', 'dpkg', '-i', deb_file])
|
|
||||||
|
|
||||||
fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
|
||||||
|
|
||||||
build = os.environ['BUILD']
|
|
||||||
if build == 'Doc':
|
|
||||||
travis = 'TRAVIS' in os.environ
|
|
||||||
if travis:
|
|
||||||
install_dependencies()
|
|
||||||
sys.path.insert(0, os.path.join(fmt_dir, 'doc'))
|
|
||||||
import build
|
|
||||||
build.create_build_env()
|
|
||||||
html_dir = build.build_docs()
|
|
||||||
repo = 'fmtlib.github.io'
|
|
||||||
if travis and 'KEY' not in os.environ:
|
|
||||||
# Don't update the repo if building on Travis from an account that
|
|
||||||
# doesn't have push access.
|
|
||||||
print('Skipping update of ' + repo)
|
|
||||||
exit(0)
|
|
||||||
# Clone the fmtlib.github.io repo.
|
|
||||||
rmtree_if_exists(repo)
|
|
||||||
git_url = 'https://github.com/' if travis else 'git@github.com:'
|
|
||||||
check_call(['git', 'clone', git_url + 'fmtlib/{}.git'.format(repo)])
|
|
||||||
# Copy docs to the repo.
|
|
||||||
target_dir = os.path.join(repo, 'dev')
|
|
||||||
rmtree_if_exists(target_dir)
|
|
||||||
shutil.copytree(html_dir, target_dir, ignore=shutil.ignore_patterns('.*'))
|
|
||||||
if travis:
|
|
||||||
check_call(['git', 'config', '--global', 'user.name', 'amplbot'])
|
|
||||||
check_call(['git', 'config', '--global', 'user.email', 'viz@ampl.com'])
|
|
||||||
# Push docs to GitHub pages.
|
|
||||||
check_call(['git', 'add', '--all'], cwd=repo)
|
|
||||||
if call(['git', 'diff-index', '--quiet', 'HEAD'], cwd=repo):
|
|
||||||
check_call(['git', 'commit', '-m', 'Update documentation'], cwd=repo)
|
|
||||||
cmd = 'git push'
|
|
||||||
if travis:
|
|
||||||
cmd += ' https://$KEY@github.com/fmtlib/fmtlib.github.io.git master'
|
|
||||||
p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd=repo)
|
|
||||||
# Print the output without the key.
|
|
||||||
print(p.communicate()[0].replace(os.environ['KEY'], '$KEY'))
|
|
||||||
if p.returncode != 0:
|
|
||||||
raise subprocess.CalledProcessError(p.returncode, cmd)
|
|
||||||
exit(0)
|
|
||||||
|
|
||||||
standard = os.environ['STANDARD']
|
|
||||||
install_dir = os.path.join(fmt_dir, "_install")
|
|
||||||
build_dir = os.path.join(fmt_dir, "_build")
|
|
||||||
test_build_dir = os.path.join(fmt_dir, "_build_test")
|
|
||||||
|
|
||||||
# Configure the library.
|
|
||||||
makedirs_if_not_exist(build_dir)
|
|
||||||
cmake_flags = [
|
|
||||||
'-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build,
|
|
||||||
'-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] +
|
|
||||||
main_cmake_flags, cwd=build_dir)
|
|
||||||
|
|
||||||
# Build the library.
|
|
||||||
check_call(['cmake', '--build','.'], cwd=build_dir)
|
|
||||||
|
|
||||||
# Test the library.
|
|
||||||
env = os.environ.copy()
|
|
||||||
env['CTEST_OUTPUT_ON_FAILURE'] = '1'
|
|
||||||
if call(['make', 'test'], env=env, cwd=build_dir):
|
|
||||||
with open(os.path.join(build_dir, 'Testing', 'Temporary', 'LastTest.log'), 'r') as f:
|
|
||||||
print(f.read())
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
# Install the library.
|
|
||||||
check_call(['make', 'install'], cwd=build_dir)
|
|
||||||
|
|
||||||
# Test installation.
|
|
||||||
makedirs_if_not_exist(test_build_dir)
|
|
||||||
check_call(['cmake', os.path.join(fmt_dir, "test", "find-package-test")] +
|
|
||||||
cmake_flags, cwd=test_build_dir)
|
|
||||||
check_call(['make', '-j4'], cwd=test_build_dir)
|
|
Loading…
Reference in New Issue