dep/fmt: Update to v12.2.0

pull/3782/head
Stenzek 1 month ago
parent b2f9c8fba9
commit c130fc12c0
No known key found for this signature in database

@ -1,16 +1,16 @@
cmake_minimum_required(VERSION 3.8...3.28)
include_guard(GLOBAL)
# Fallback for using newer policies on CMake <3.12.
if (${CMAKE_VERSION} VERSION_LESS 3.12)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
endif ()
# Determine if fmt is built as a subproject (using add_subdirectory)
# or if it is the master project.
# Determine if fmt is built as a subproject (using add_subdirectory) or if it is
# the master project.
if (NOT DEFINED FMT_MASTER_PROJECT)
set(FMT_MASTER_PROJECT OFF)
# NOTE: source vs current_source detection is unreliable
# this heuristic is more generally applicable esp w.r.t FetchContent
# Checking project name is more reliable than checking source directories.
if (NOT DEFINED PROJECT_NAME)
set(FMT_MASTER_PROJECT ON)
message(STATUS "CMake version: ${CMAKE_VERSION}")
@ -23,169 +23,90 @@ function(join result_var)
foreach (arg ${ARGN})
set(result "${result}${arg}")
endforeach ()
set(${result_var} "${result}" PARENT_SCOPE)
set(${result_var}
"${result}"
PARENT_SCOPE)
endfunction ()
# DEPRECATED! Should be merged into add_module_library.
function(enable_module target)
if (MSVC)
if(NOT CMAKE_GENERATOR STREQUAL "Ninja")
set(BMI_DIR "${CMAKE_CURRENT_BINARY_DIR}")
file(TO_NATIVE_PATH "${BMI_DIR}/${target}.ifc" BMI)
target_compile_options(${target}
PRIVATE /interface /ifcOutput ${BMI}
INTERFACE /reference fmt=${BMI})
set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI})
set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
endif()
endif ()
endfunction()
set(FMT_USE_CMAKE_MODULES FALSE)
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.28 AND
CMAKE_GENERATOR STREQUAL "Ninja")
set(FMT_USE_CMAKE_MODULES TRUE)
endif ()
# Adds a library compiled with C++20 module support.
# `enabled` is a CMake variables that specifies if modules are enabled.
# If modules are disabled `add_module_library` falls back to creating a
# non-modular library.
#
# Usage:
# add_module_library(<name> [sources...] FALLBACK [sources...] [IF enabled])
function(add_module_library name)
cmake_parse_arguments(AML "" "IF" "FALLBACK" ${ARGN})
set(sources ${AML_UNPARSED_ARGUMENTS})
add_library(${name})
set_target_properties(${name} PROPERTIES LINKER_LANGUAGE CXX)
if (NOT ${${AML_IF}})
# Create a non-modular library.
target_sources(${name} PRIVATE ${AML_FALLBACK})
set_target_properties(${name} PROPERTIES CXX_SCAN_FOR_MODULES OFF)
return()
endif ()
# Modules require C++20.
target_compile_features(${name} PUBLIC cxx_std_20)
if (CMAKE_COMPILER_IS_GNUCXX)
target_compile_options(${name} PUBLIC -fmodules-ts)
endif ()
if (FMT_USE_CMAKE_MODULES)
target_sources(${name} PUBLIC FILE_SET fmt TYPE CXX_MODULES
FILES ${sources})
else()
# `std` is affected by CMake options and may be higher than C++20.
get_target_property(std ${name} CXX_STANDARD)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(pcms)
foreach (src ${sources})
get_filename_component(pcm ${src} NAME_WE)
set(pcm ${pcm}.pcm)
# Propagate -fmodule-file=*.pcm to targets that link with this library.
target_compile_options(
${name} PUBLIC -fmodule-file=${CMAKE_CURRENT_BINARY_DIR}/${pcm})
# Use an absolute path to prevent target_link_libraries prepending -l
# to it.
set(pcms ${pcms} ${CMAKE_CURRENT_BINARY_DIR}/${pcm})
add_custom_command(
OUTPUT ${pcm}
COMMAND ${CMAKE_CXX_COMPILER}
-std=c++${std} -x c++-module --precompile -c
-o ${pcm} ${CMAKE_CURRENT_SOURCE_DIR}/${src}
"-I$<JOIN:$<TARGET_PROPERTY:${name},INCLUDE_DIRECTORIES>,;-I>"
# Required by the -I generator expression above.
COMMAND_EXPAND_LISTS
DEPENDS ${src})
endforeach ()
# Add .pcm files as sources to make sure they are built before the library.
set(sources)
foreach (pcm ${pcms})
get_filename_component(pcm_we ${pcm} NAME_WE)
set(obj ${pcm_we}.o)
# Use an absolute path to prevent target_link_libraries prepending -l.
set(sources ${sources} ${pcm} ${CMAKE_CURRENT_BINARY_DIR}/${obj})
add_custom_command(
OUTPUT ${obj}
COMMAND ${CMAKE_CXX_COMPILER} $<TARGET_PROPERTY:${name},COMPILE_OPTIONS>
-c -o ${obj} ${pcm}
DEPENDS ${pcm})
endforeach ()
endif ()
target_sources(${name} PRIVATE ${sources})
endif()
endfunction()
include(CMakeParseArguments)
# Sets a cache variable with a docstring joined from multiple arguments:
# set(<variable> <value>... CACHE <type> <docstring>...)
# set_verbose(<variable> <value> CACHE <type> <docstring>...)
# This allows splitting a long docstring for readability.
function(set_verbose)
# cmake_parse_arguments is broken in CMake 3.4 (cannot parse CACHE) so use
# list instead.
list(GET ARGN 0 var)
list(REMOVE_AT ARGN 0)
list(GET ARGN 0 val)
list(REMOVE_AT ARGN 0)
list(REMOVE_AT ARGN 0)
list(GET ARGN 0 type)
list(REMOVE_AT ARGN 0)
function (set_verbose variable value _cache type)
join(doc ${ARGN})
set(${var} ${val} CACHE ${type} ${doc})
set(${variable}
${value}
CACHE ${type} ${doc})
endfunction ()
# Set the default CMAKE_BUILD_TYPE to Release.
# This should be done before the project command since the latter can set
# CMAKE_BUILD_TYPE itself (it does so for nmake).
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 "
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
endif ()
project(FMT CXX)
include(GNUInstallDirs)
set_verbose(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING
"Installation directory for include files, a relative path that "
"will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute path.")
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
OFF)
# Determine support for the C++ module scanning and CXX_MODULE File_Sets
# Requires C++20, CMake >= 3.28 and
# Generators(Ninja >= 1.11 OR Visual Studio >= 17.4).
# Compilers GCC>=14, Clang>=16 or MSVC >= 17.4
# Source: https://cmake.org/cmake/help/latest/manual/cmake-cxxmodules.7.html
set(FMT_USE_CMAKE_MODULES FALSE)
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.28 AND CMAKE_CXX_STANDARD
GREATER_EQUAL 20)
if (CMAKE_GENERATOR STREQUAL "Ninja")
execute_process(COMMAND "${CMAKE_MAKE_PROGRAM}" "--version"
OUTPUT_VARIABLE NINJA_VERSION)
if (NINJA_VERSION VERSION_GREATER_EQUAL 1.11)
if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION
GREATER_EQUAL 15)
OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang"
AND CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 16)
OR (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION
GREATER_EQUAL 1934))
set(FMT_USE_CMAKE_MODULES TRUE)
endif ()
endif ()
elseif (CMAKE_GENERATOR MATCHES "^Visual Studio" AND MSVC_VERSION
GREATER_EQUAL 1934)
set(FMT_USE_CMAKE_MODULES TRUE)
endif ()
endif ()
# Options that control generation of various targets.
option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT})
option(FMT_INSTALL "Generate the install target." ${FMT_MASTER_PROJECT})
option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT})
option(FMT_FUZZ "Generate the fuzz target." OFF)
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
option(FMT_OS "Include OS-specific APIs." ON)
option(FMT_MODULE "Build a module instead of a traditional library." OFF)
option(FMT_MODULE "Build a module library." ${FMT_USE_CMAKE_MODULES})
option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF)
option(FMT_UNICODE "Enable Unicode support." ON)
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
OFF)
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 ()
if (CMAKE_SYSTEM_NAME STREQUAL "MSDOS")
set(FMT_TEST OFF)
message(STATUS "MSDOS is incompatible with gtest")
endif ()
# Get version from base.h
include(GNUInstallDirs) # CMAKE_INSTALL_INCLUDEDIR
set_verbose(
FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING
"Installation directory for include files, a relative path that "
"will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute path.")
set(FMT_DEBUG_POSTFIX
d
CACHE STRING "Debug library postfix.")
# Get version from base.h.
file(READ include/fmt/base.h base_h)
if (NOT base_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
message(FATAL_ERROR "Cannot get FMT_VERSION from base.h.")
@ -211,41 +132,61 @@ include(JoinPaths)
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)
"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")
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")
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
-Wold-style-cast -Wundef
-Wredundant-decls -Wwrite-strings -Wpointer-arith
-Wcast-qual -Wformat=2 -Wmissing-include-dirs
set(PEDANTIC_COMPILE_FLAGS
-pedantic-errors
-Wall
-Wextra
-pedantic
-Wold-style-cast
-Wundef
-Wredundant-decls
-Wwrite-strings
-Wpointer-arith
-Wcast-qual
-Wformat=2
-Wmissing-include-dirs
-Wcast-align
-Wctor-dtor-privacy -Wdisabled-optimization
-Winvalid-pch -Woverloaded-virtual
-Wconversion -Wundef
-Wno-ctor-dtor-privacy -Wno-format-nonliteral)
-Wctor-dtor-privacy
-Wdisabled-optimization
-Winvalid-pch
-Woverloaded-virtual
-Wconversion
-Wundef
-Wno-ctor-dtor-privacy
-Wno-format-nonliteral)
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
-Wno-dangling-else -Wno-unused-local-typedefs)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wno-dangling-else
-Wno-unused-local-typedefs)
endif ()
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion
-Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast
-Wvector-operation-performance -Wsized-deallocation -Wshadow)
set(PEDANTIC_COMPILE_FLAGS
${PEDANTIC_COMPILE_FLAGS}
-Wdouble-promotion
-Wtrampolines
-Wzero-as-null-pointer-constant
-Wuseless-cast
-Wvector-operation-performance
-Wsized-deallocation
-Wshadow)
endif ()
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
-Wduplicated-cond)
# Workaround for GCC regression
# [12/13/14/15 regression] New (since gcc 12) false positive null-dereference in vector.resize
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108860
# Workaround for GCC 12-15 regression:
# a false positive null-dereference in vector.resize
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108860.
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnull-dereference)
endif ()
@ -254,8 +195,15 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
endif ()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wundef
-Wdeprecated -Wweak-vtables -Wshadow
set(PEDANTIC_COMPILE_FLAGS
-Wall
-Wextra
-pedantic
-Wconversion
-Wundef
-Wdeprecated
-Wweak-vtables
-Wshadow
-Wno-gnu-zero-variadic-macro-arguments)
check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
if (HAS_NULLPTR_WARNING)
@ -283,33 +231,64 @@ if (FMT_MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
join(netfxpath
"C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\"
".NETFramework\\v4.0")
file(WRITE run-msbuild.bat "
${MSBUILD_SETUP}
file(WRITE run-msbuild.bat "${MSBUILD_SETUP}
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
endif ()
function(add_headers VAR)
set(headers ${${VAR}})
foreach (header ${ARGN})
set(headers ${headers} include/fmt/${header})
endforeach()
set(${VAR} ${headers} PARENT_SCOPE)
# Sets up a top-level fmt target. Targets that depend on other top-level targets
# should call this because they'll automatically get the required properties.
function (setup_target target kind)
add_library(fmt::${target} ALIAS ${target})
target_include_directories(
${target} ${FMT_SYSTEM_HEADERS_ATTRIBUTE} BEFORE ${kind}
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
if (NOT MSVC)
# Unicode is always supported on compilers other than MSVC.
elseif (FMT_UNICODE)
# Unicode support requires compiling with /utf-8.
target_compile_options(
${target} ${kind}
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/utf-8>)
else ()
target_compile_definitions(${target} ${kind} FMT_UNICODE=0)
endif ()
set_target_properties(
${target}
PROPERTIES VERSION ${FMT_VERSION}
SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
endfunction ()
# Define the fmt library, its includes and the needed defines.
set(FMT_HEADERS)
add_headers(FMT_HEADERS args.h base.h chrono.h color.h compile.h core.h format.h
format-inl.h os.h ostream.h printf.h ranges.h std.h
foreach (
header
args.h
base.h
chrono.h
color.h
compile.h
core.h
format.h
format-inl.h
os.h
ostream.h
printf.h
ranges.h
std.h
xchar.h)
set(FMT_SOURCES src/format.cc)
set(FMT_HEADERS ${FMT_HEADERS} include/fmt/${header})
endforeach ()
add_module_library(fmt src/fmt.cc FALLBACK
${FMT_SOURCES} ${FMT_HEADERS} README.md ChangeLog.md
IF FMT_MODULE)
add_library(fmt::fmt ALIAS fmt)
if (FMT_MODULE)
enable_module(fmt)
elseif (FMT_OS)
# Add the main fmt library.
add_library(fmt src/format.cc ${FMT_HEADERS} README.md ChangeLog.md)
setup_target(fmt PUBLIC)
set_target_properties(fmt PROPERTIES PUBLIC_HEADER "${FMT_HEADERS}")
if (FMT_OS)
target_sources(fmt PRIVATE src/os.cc)
else ()
target_compile_definitions(fmt PRIVATE FMT_OS=0)
@ -328,25 +307,6 @@ else ()
message(WARNING "Feature cxx_std_11 is unknown for the CXX compiler")
endif ()
target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} BEFORE PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
PUBLIC_HEADER "${FMT_HEADERS}"
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}"
# Workaround for Visual Studio 2017:
# Ensure the .pdb is created with the same name and in the same directory
# as the .lib. Newer VS versions already do this by default, but there is no
# harm in setting it for those too. Ignored by other generators.
COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
COMPILE_PDB_NAME "fmt"
COMPILE_PDB_NAME_DEBUG "fmt${FMT_DEBUG_POSTFIX}")
# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target
# property because it's not set by default.
set(FMT_LIB_NAME fmt)
@ -355,37 +315,126 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug")
endif ()
if (BUILD_SHARED_LIBS)
target_compile_definitions(fmt PRIVATE FMT_LIB_EXPORT INTERFACE FMT_SHARED)
target_compile_definitions(
fmt
PRIVATE FMT_LIB_EXPORT
INTERFACE FMT_SHARED)
endif ()
if (FMT_SAFE_DURATION_CAST)
target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST)
endif ()
add_library(fmt-header-only INTERFACE)
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
# Adds a library compiled with C++20 module support.
#
# Usage:
# add_module_library(<name> [sources...] [USE_CMAKE_MODULES TRUE])
function (add_module_library name)
cmake_parse_arguments(AML "" "USE_CMAKE_MODULES" "" ${ARGN})
set(sources ${AML_UNPARSED_ARGUMENTS})
if (NOT MSVC)
# Unicode is always supported on compilers other than MSVC.
elseif (FMT_UNICODE)
# Unicode support requires compiling with /utf-8.
target_compile_options(fmt PUBLIC $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/utf-8>)
target_compile_options(fmt-header-only INTERFACE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/utf-8>)
else ()
target_compile_definitions(fmt PUBLIC FMT_UNICODE=0)
add_library(${name})
set_target_properties(${name} PROPERTIES LINKER_LANGUAGE CXX)
# Modules require C++20.
target_compile_features(${name} PUBLIC cxx_std_20)
if (MSVC)
if (NOT CMAKE_GENERATOR STREQUAL "Ninja")
set(BMI_DIR "${CMAKE_CURRENT_BINARY_DIR}")
file(TO_NATIVE_PATH "${BMI_DIR}/${name}.ifc" BMI)
target_compile_options(
${name}
PRIVATE /interface /ifcOutput ${BMI}
INTERFACE /reference fmt=${BMI})
set_target_properties(${name} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI})
set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
endif ()
endif ()
if (${AML_USE_CMAKE_MODULES})
target_sources(${name} PUBLIC FILE_SET fmt TYPE CXX_MODULES FILES
${sources})
return()
endif ()
if (CMAKE_COMPILER_IS_GNUCXX)
target_compile_options(${name} PUBLIC -fmodules-ts)
endif ()
# `std` is affected by CMake options and may be higher than C++20.
get_target_property(std ${name} CXX_STANDARD)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(pcms)
foreach (src ${sources})
get_filename_component(pcm ${src} NAME_WE)
set(pcm ${pcm}.pcm)
# Propagate -fmodule-file=*.pcm to targets that link with this library.
target_compile_options(
${name} PUBLIC -fmodule-file=${CMAKE_CURRENT_BINARY_DIR}/${pcm})
# Use an absolute path to prevent target_link_libraries prepending -l
# to it.
set(pcms ${pcms} ${CMAKE_CURRENT_BINARY_DIR}/${pcm})
add_custom_command(
OUTPUT ${pcm}
COMMAND
${CMAKE_CXX_COMPILER} -std=c++${std} -x c++-module --precompile -c -o
${pcm} ${CMAKE_CURRENT_SOURCE_DIR}/${src}
"-I$<JOIN:$<TARGET_PROPERTY:${name},INCLUDE_DIRECTORIES>,;-I>"
# Required by the -I generator expression above.
COMMAND_EXPAND_LISTS
DEPENDS ${src})
endforeach ()
# Add .pcm files as sources to make sure they are built before the library.
set(sources)
foreach (pcm ${pcms})
get_filename_component(pcm_we ${pcm} NAME_WE)
set(obj ${pcm_we}.o)
# Use an absolute path to prevent target_link_libraries prepending -l.
set(sources ${sources} ${pcm} ${CMAKE_CURRENT_BINARY_DIR}/${obj})
add_custom_command(
OUTPUT ${obj}
COMMAND ${CMAKE_CXX_COMPILER} $<TARGET_PROPERTY:${name},COMPILE_OPTIONS>
-c -o ${obj} ${pcm}
DEPENDS ${pcm})
endforeach ()
endif ()
target_sources(${name} PRIVATE ${sources})
endfunction ()
if (FMT_MODULE)
add_module_library(fmt-module src/fmt.cc USE_CMAKE_MODULES
${FMT_USE_CMAKE_MODULES})
setup_target(fmt-module PUBLIC)
endif ()
add_library(fmt-header-only INTERFACE)
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_compile_features(fmt-header-only INTERFACE cxx_std_11)
setup_target(fmt-header-only INTERFACE)
target_include_directories(fmt-header-only
${FMT_SYSTEM_HEADERS_ATTRIBUTE} BEFORE INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
add_library(fmt-c STATIC src/fmt-c.cc)
target_compile_features(fmt-c INTERFACE c_std_11)
if (MSVC)
target_compile_options(fmt-c PUBLIC /Zc:preprocessor)
endif ()
target_link_libraries(fmt-c PUBLIC fmt::fmt)
add_library(fmt::fmt-c ALIAS fmt-c)
set_target_properties(fmt-c PROPERTIES PUBLIC_HEADER include/fmt/fmt-c.h)
# Install targets.
if (FMT_INSTALL)
include(CMakePackageConfigHelpers)
set_verbose(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
set_verbose(
FMT_CMAKE_DIR
${CMAKE_INSTALL_LIBDIR}/cmake/fmt
CACHE
STRING
"Installation directory for cmake files, a relative path that "
"will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute "
"path.")
@ -394,11 +443,16 @@ if (FMT_INSTALL)
set(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
set(targets_export_name fmt-targets)
set_verbose(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
set_verbose(
FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
"Installation directory for libraries, a relative path that "
"will be joined to ${CMAKE_INSTALL_PREFIX} or an absolute path.")
set_verbose(FMT_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE STRING
set_verbose(
FMT_PKGCONFIG_DIR
${CMAKE_INSTALL_LIBDIR}/pkgconfig
CACHE
STRING
"Installation directory for pkgconfig (.pc) files, a relative "
"path that will be joined with ${CMAKE_INSTALL_PREFIX} or an "
"absolute path.")
@ -412,52 +466,57 @@ if (FMT_INSTALL)
join_paths(libdir_for_pc_file "\${exec_prefix}" "${FMT_LIB_DIR}")
join_paths(includedir_for_pc_file "\${prefix}" "${FMT_INC_DIR}")
configure_file(
"${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in"
"${pkgconfig}"
configure_file("${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in" "${pkgconfig}"
@ONLY)
configure_package_config_file(
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
${project_config}
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in ${project_config}
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
set(INSTALL_TARGETS fmt fmt-header-only)
set(INSTALL_TARGETS fmt fmt-header-only fmt-c)
set(INSTALL_FILE_SET)
if (FMT_MODULE)
list(APPEND INSTALL_TARGETS fmt-module)
if (FMT_USE_CMAKE_MODULES)
set(INSTALL_FILE_SET FILE_SET fmt DESTINATION "${FMT_INC_DIR}/fmt")
set(INSTALL_FILE_SET FILE_SET fmt DESTINATION ${FMT_INC_DIR}/fmt)
endif ()
endif ()
# Install the library and headers.
install(TARGETS ${INSTALL_TARGETS}
install(
TARGETS ${INSTALL_TARGETS}
COMPONENT fmt_core
EXPORT ${targets_export_name}
LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR}
PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt"
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
${INSTALL_FILE_SET})
PUBLIC_HEADER DESTINATION ${FMT_INC_DIR}/fmt
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ${INSTALL_FILE_SET})
# Use a namespace because CMake provides better diagnostics for namespaced
# imported targets.
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
export(
TARGETS ${INSTALL_TARGETS}
NAMESPACE fmt::
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
# Install version, config and target files.
install(FILES ${project_config} ${version_config}
install(
FILES ${project_config} ${version_config}
DESTINATION ${FMT_CMAKE_DIR}
COMPONENT fmt_core)
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
install(
EXPORT ${targets_export_name}
DESTINATION ${FMT_CMAKE_DIR}
NAMESPACE fmt::
COMPONENT fmt_core)
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}"
install(
FILES "${pkgconfig}"
DESTINATION "${FMT_PKGCONFIG_DIR}"
COMPONENT fmt_core)
endif ()
function (add_doc_target)
find_program(DOXYGEN doxygen
PATHS "$ENV{ProgramFiles}/doxygen/bin"
find_program(DOXYGEN doxygen PATHS "$ENV{ProgramFiles}/doxygen/bin"
"$ENV{ProgramFiles\(x86\)}/doxygen/bin")
if (NOT DOXYGEN)
message(STATUS "Target 'doc' disabled because doxygen not found")
@ -478,18 +537,18 @@ function(add_doc_target)
add_custom_target(
doc
COMMAND
${CMAKE_COMMAND}
-E env PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}/support/python
${MKDOCS} build -f ${CMAKE_CURRENT_SOURCE_DIR}/support/mkdocs.yml
${CMAKE_COMMAND} -E env
PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}/support/python ${MKDOCS} build -f
${CMAKE_CURRENT_SOURCE_DIR}/support/mkdocs.yml
# MkDocs requires the site dir to be outside of the doc dir.
--site-dir ${CMAKE_CURRENT_BINARY_DIR}/doc-html
--no-directory-urls
--site-dir ${CMAKE_CURRENT_BINARY_DIR}/doc-html --no-directory-urls
SOURCES ${sources})
include(GNUInstallDirs)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc-html/
install(
DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc-html/
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt
COMPONENT fmt_doc OPTIONAL)
COMPONENT fmt_doc
OPTIONAL)
endfunction ()
if (FMT_DOC)
@ -523,7 +582,9 @@ if (FMT_MASTER_PROJECT AND EXISTS ${gitignore})
string(REPLACE "*" ".*" line "${line}")
set(ignored_files ${ignored_files} "${line}$" "${line}/")
endforeach ()
set(ignored_files ${ignored_files} /.git /build/doxyxml .vagrant)
# Exclude the whole build tree: the .gitignore-derived `build/` pattern does
# not actually prune the directory or its contents from the source package.
set(ignored_files ${ignored_files} /.git "/build($|/)" .vagrant)
set(CPACK_SOURCE_GENERATOR ZIP)
set(CPACK_SOURCE_IGNORE_FILES ${ignored_files})

@ -1,3 +1,226 @@
# 12.2.0 - 2026-06-16
- Added a C11 API that brings fast, type-safe formatting to C. The new
`fmt-c` library and `fmt/fmt-c.h` header use `_Generic` to dispatch on
argument types and outperform `printf`/`sprintf`. For example:
```c++
#include <fmt/fmt-c.h>
fmt_print(stdout, "The answer is {}.\n", 42);
```
(https://github.com/fmtlib/fmt/issues/4663,
https://github.com/fmtlib/fmt/pull/4671,
https://github.com/fmtlib/fmt/pull/4696,
https://github.com/fmtlib/fmt/issues/4693,
https://github.com/fmtlib/fmt/pull/4694,
https://github.com/fmtlib/fmt/pull/4712,
https://github.com/fmtlib/fmt/pull/4789).
Thanks @Soumik15630m, @Ferdi265 and @localspook.
- Added a separate `fmt::fmt-module` CMake target for C++20 modules and a
CI workflow that exercises module-based builds
(https://github.com/fmtlib/fmt/issues/4684,
https://github.com/fmtlib/fmt/pull/4685,
https://github.com/fmtlib/fmt/issues/4707,
https://github.com/fmtlib/fmt/pull/4708,
https://github.com/fmtlib/fmt/pull/4702,
https://github.com/fmtlib/fmt/pull/4709).
Thanks @MathewBensonCode.
- Enabled the full Dragonbox lookup cache by default for floating-point
formatting unless optimizing for binary size (`__OPTIMIZE_SIZE__`),
giving a ~1025% speedup. Thanks Matthias Kretz for the suggestion.
Average time per `double` on Apple M1 Pro (clang 17, random digits,
smaller is better) measured with
[dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark):
| Method | Time (ns) |
| ----------------------- | --------: |
| fmt (full) | 22.07 |
| fmt (compact) | 29.55 |
| ryu | 35.21 |
| double-conversion | 81.81 |
| `sprintf` | 726.27 |
| `ostringstream` | 864.34 |
- Improved integer formatting performance by ~3%
(https://github.com/fmtlib/fmt/pull/4630). Thanks @user202729.
- Optimized formatting into back-insert iterators by using bulk container
append/insert methods (e.g. on `std::vector<char>` and custom string
types) (https://github.com/fmtlib/fmt/pull/4679). Thanks @user202729.
- Reduced binary size of debug builds (~200k to ~85k in the bloat test) and
improved compile speed when `consteval` is unavailable.
- Made path formatting lossless, preserving ill-formed UTF-16
sequences when converting `std::filesystem::path` to a narrow string.
- Added support for formatting `std::unexpected`
(https://github.com/fmtlib/fmt/pull/4675). Thanks @17steen.
- Added overloads of `fmt::println` that take a `fmt::text_style`
(https://github.com/fmtlib/fmt/pull/4782). Thanks @ahoarau.
- Added support for positional arguments as width and precision specifiers
in `fmt::printf` (https://github.com/fmtlib/fmt/pull/4643).
Thanks @KareemOtoum.
- Made `FMT_STRING` a no-op when `FMT_USE_CONSTEVAL` is enabled, since the
consteval format-string constructor already provides compile-time
validation
(https://github.com/fmtlib/fmt/issues/4611,
https://github.com/fmtlib/fmt/pull/4612). Thanks @friedkeenan.
- Promoted `fmt::detail::named_arg` to the public API as `fmt::named_arg` and
deprecated the detail alias
(https://github.com/fmtlib/fmt/issues/4683,
https://github.com/fmtlib/fmt/pull/4687). Thanks @TPPPP72.
- Moved the `std::byte` formatter from `fmt/format.h` to `fmt/std.h`.
- Provided a default definition for `fmt::is_contiguous`
(https://github.com/fmtlib/fmt/pull/4731,
https://github.com/fmtlib/fmt/pull/4770). Thanks @user202729 and @phprus.
- Added the `FMT_USE_FLOCKFILE` macro to disable the use of `flockfile`
(https://github.com/fmtlib/fmt/issues/4646,
https://github.com/fmtlib/fmt/pull/4666). Thanks @mvastola.
- Added `include_guard(GLOBAL)` so that {fmt} can be used in multiple
submodules of the same project
(https://github.com/fmtlib/fmt/pull/4672). Thanks @torsten48.
- Improved `constexpr` support
(https://github.com/fmtlib/fmt/pull/4659,
https://github.com/fmtlib/fmt/pull/4591).
Thanks @elbeno and @17steen.
- Deprecated the implicit conversion from `fmt::format_string` and
`fmt::basic_fstring` to `string_view` to align with `std::format_string`;
use `format_string::get()` instead.
- Opted out `std::complex` from tuple formatting so that the dedicated
`std::complex` formatter is always used.
- Removed the `fmt::say` function.
- Deprecated the `std::initializer_list` overload of `fmt::join` and the array
overload of `fmt::vformat_to`.
- Made the `<fmt/core.h>` header equivalent to `<fmt/base.h>` by
default. Code that relied on `<fmt/core.h>` pulling in `<fmt/format.h>`
must now either include `<fmt/format.h>` directly or define
`FMT_DEPRECATED_HEAVY_CORE` to opt back in.
- Improved `wchar_t` support: `fmt::join` now accepts `wchar_t` and other
non-`char` separators, and `fmt::format_to_n` now works with `fmt::runtime`
on `wchar_t`
(https://github.com/fmtlib/fmt/pull/4686,
https://github.com/fmtlib/fmt/issues/4714,
https://github.com/fmtlib/fmt/pull/4715).
Thanks @Yancey2023 and @sunmy2019.
- Fixed formatting of `std::tm` with a null `tm_zone`
(https://github.com/fmtlib/fmt/pull/4790). Thanks @Carmel0.
- Fixed compile-time formatting in `fmt/ranges.h`, `fmt/style.h` and
`fmt/std.h` (https://github.com/fmtlib/fmt/pull/4759). Thanks @j4niwzis.
- Fixed an ambiguity between `formatter<std::optional<T>>` in `fmt/std.h`
and `fmt/ranges.h` on C++26 (P3168R2)
(https://github.com/fmtlib/fmt/pull/4761). Thanks @phprus.
- Fixed a GCC PCH breakage triggered by a scoped `#pragma GCC optimize`.
- Fixed a TSAN false positive in the locale handling code
(https://github.com/fmtlib/fmt/issues/4755).
- Fixed compile-time format string checks truncating string literals at
an embedded null byte
(https://github.com/fmtlib/fmt/pull/4732). Thanks @user202729.
- Fixed out-of-bounds reads in `printf` formatting
(https://github.com/fmtlib/fmt/issues/4741,
https://github.com/fmtlib/fmt/pull/4742,
https://github.com/fmtlib/fmt/pull/4800).
Thanks @Algunenano and @aizu-m.
- Fixed the return type of the `f(un)lockfile` wrappers on Windows
(https://github.com/fmtlib/fmt/pull/4739). Thanks @mvastola.
- Worked around a CUDA issue when handling UTF-32 literals
(https://github.com/fmtlib/fmt/pull/4719). Thanks @Cazadorro.
- Fixed missing named-argument validation for compiled format strings
(https://github.com/fmtlib/fmt/pull/4638). Thanks @JaeheonShim.
- Fixed `fmt::format_to_n` in `<fmt/compile.h>` failing to compile when
`<iterator>` is not transitively included
(https://github.com/fmtlib/fmt/issues/4615).
- Fixed handling of pointers in format string compilation with
`FMT_BUILTIN_TYPES=0`.
- Stopped assuming nul termination of the format string in `fmt::printf`.
Thanks @ZUENS2020 for reporting.
- Fixed a build error when locale support is disabled
(https://github.com/fmtlib/fmt/pull/4627). Thanks @marcel-behlau-elfin.
- Fixed a fallback range formatter for types with a `container_type` member
(https://github.com/fmtlib/fmt/issues/4123,
https://github.com/fmtlib/fmt/pull/4660). Thanks @Soumik15630m.
- Fixed C++20 concept detection
(https://github.com/fmtlib/fmt/pull/4653). Thanks @tearfur.
- Fixed a clang compilation failure
(https://github.com/fmtlib/fmt/pull/4718). Thanks @mccakit.
- Fixed various MSVC warnings, including C4305 and conversion warnings on
x86 (https://github.com/fmtlib/fmt/pull/4668,
https://github.com/fmtlib/fmt/pull/4594).
Thanks @kanren3 and @blizzard4591.
- Updated the Android Gradle Plugin to 9.x
(https://github.com/fmtlib/fmt/issues/4651,
https://github.com/fmtlib/fmt/pull/4658). Thanks @Soumik15630m.
- Made various code, build and test improvements
(https://github.com/fmtlib/fmt/pull/4625,
https://github.com/fmtlib/fmt/pull/4639,
https://github.com/fmtlib/fmt/pull/4644,
https://github.com/fmtlib/fmt/pull/4656,
https://github.com/fmtlib/fmt/pull/4680,
https://github.com/fmtlib/fmt/pull/4681,
https://github.com/fmtlib/fmt/pull/4704,
https://github.com/fmtlib/fmt/pull/4710,
https://github.com/fmtlib/fmt/pull/4713,
https://github.com/fmtlib/fmt/pull/4729,
https://github.com/fmtlib/fmt/pull/4751,
https://github.com/fmtlib/fmt/pull/4758,
https://github.com/fmtlib/fmt/pull/4799).
Thanks @ZephyrLykos, @togunchan, @kagancansit, @BerndPetrovitsch,
@Skylion007, @st0rmbtw, @localspook and @EXtremeExploit.
- Improved documentation, including a rewrite of the format string syntax,
better handling of doxygen tags, documenting `output_file`, fixing CSS so
that whitespace is displayed properly, and various smaller fixes
(https://github.com/fmtlib/fmt/pull/4622,
https://github.com/fmtlib/fmt/pull/4626,
https://github.com/fmtlib/fmt/pull/4631,
https://github.com/fmtlib/fmt/pull/4667,
https://github.com/fmtlib/fmt/pull/4616,
https://github.com/fmtlib/fmt/pull/4748).
Thanks @heavywatal, @ZephyrLykos, @user202729, @ssszcmawo, @bigmoonbit
and @Powerbyte7.
- Added building of release artifacts and SLSA provenance in CI, added a
CodeQL workflow, and added the security policy in `.github/SECURITY.md`.
# 12.1.0 - 2025-10-29
- Optimized `buffer::append`, resulting in up to ~16% improvement on spdlog

@ -18,10 +18,3 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- Optional exception to the license ---
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into a machine-executable object form of such
source code, you may redistribute such embedded portions in such object form
without including the above copyright and permission notices.

@ -1,19 +1,25 @@
<img src="https://user-images.githubusercontent.com/576385/156254208-f5b743a9-88cf-439d-b0c0-923d53e8d551.png" alt="{fmt}" width="25%"/>
[![image](https://github.com/fmtlib/fmt/workflows/linux/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux)
[![image](https://github.com/fmtlib/fmt/workflows/macos/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos)
[![image](https://github.com/fmtlib/fmt/workflows/windows/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows)
[![fmt is continuously fuzzed at oss-fuzz](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?\%0Acolspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\%0ASummary&q=proj%3Dfmt&can=1)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8880/badge)](https://www.bestpractices.dev/projects/8880)
[![image](https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge)](https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt)
[![Ask questions at StackOverflow with the tag fmt](https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt)
[![image](https://github.com/fmtlib/fmt/actions/workflows/linux.yml/badge.svg?branch=master)](
https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux)
[![image](https://github.com/fmtlib/fmt/actions/workflows/macos.yml/badge.svg?branch=master)](
https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos)
[![image](https://github.com/fmtlib/fmt/actions/workflows/windows.yml/badge.svg?branch=master)](
https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows)
[![fmt is continuously fuzzed at oss-fuzz](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg)](
https://issues.oss-fuzz.com/issues?q=title:fmt%20cc:victor.zverovich@gmail.com)
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8880/badge)](
https://www.bestpractices.dev/projects/8880)
[![image](https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge)](
https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt)
[![Ask questions at StackOverflow with the tag fmt](
https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt)
[![Support Ukraine](
https://img.shields.io/badge/Support-Ukraine-005BBB?labelColor=FFD500)](https://novaukraine.org/)
**{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams.
If you like this project, please consider donating to one of the funds
that help victims of the war in Ukraine: <https://www.stopputin.net/>.
[Documentation](https://fmt.dev)
[Cheat Sheets](https://hackingcpp.com/cpp/libs/fmt.html)
@ -38,7 +44,7 @@ Try {fmt} in [Compiler Explorer](https://godbolt.org/z/8Mx1EW73v).
[Dragonbox](https://github.com/jk-jeon/dragonbox) algorithm
- Portable Unicode support
- Safe [printf
implementation](https://fmt.dev/latest/api/#printf-formatting)
implementation](https://fmt.dev/latest/api/#printf-api)
including the POSIX extension for positional arguments
- Extensibility: [support for user-defined
types](https://fmt.dev/latest/api/#formatting-user-defined-types)
@ -150,8 +156,8 @@ int main() {
}
```
This can be [5 to 9 times faster than
fprintf](http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html).
This can be [up to 9 times faster than `fprintf`](
http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html).
**Print with colors and text styles**
@ -170,7 +176,7 @@ int main() {
Output on a modern terminal with Unicode support:
![image](https://github.com/fmtlib/fmt/assets/%0A576385/2a93c904-d6fa-4aa6-b453-2618e1c327d7)
![image](https://github.com/fmtlib/fmt/assets/576385/2a93c904-d6fa-4aa6-b453-2618e1c327d7)
# Benchmarks
@ -178,17 +184,17 @@ Output on a modern terminal with Unicode support:
| Library | Method | Run Time, s |
|-------------------|---------------|-------------|
| libc | printf | 0.91 |
| libc++ | std::ostream | 2.49 |
| {fmt} 9.1 | fmt::print | 0.74 |
| Boost Format 1.80 | boost::format | 6.26 |
| Folly Format | folly::format | 1.87 |
| libc | printf | 0.66 |
| libc++ | std::ostream | 1.63 |
| {fmt} 12.1 | fmt::print | 0.44 |
| Boost Format 1.88 | boost::format | 3.89 |
| Folly Format | folly::format | 1.28 |
{fmt} is the fastest of the benchmarked methods, \~20% faster than
{fmt} is the fastest of the benchmarked methods, \~50% faster than
`printf`.
The above results were generated by building `tinyformat_test.cpp` on
macOS 12.6.1 with `clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT`, and
macOS 15.6.1 with `clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT`, and
taking the best of three runs. In the test, the format string
`"%0.10f:%04d:%+g:%s:%p:%c:%%\n"` or equivalent is filled 2,000,000
times with output sent to `/dev/null`; for further details refer to the
@ -217,11 +223,11 @@ in the following tables.
**Optimized build (-O3)**
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|---------------|-----------------|----------------------|--------------------|
|-----------------|-----------------|----------------------|--------------------|
| printf | 1.6 | 54 | 50 |
| IOStreams | 25.9 | 98 | 84 |
| fmt 83652df | 4.8 | 54 | 50 |
| tinyformat | 29.1 | 161 | 136 |
| IOStreams | 28.4 | 98 | 84 |
| {fmt} `1122268` | 5.0 | 54 | 50 |
| tinyformat | 32.6 | 164 | 136 |
| Boost Format | 55.0 | 530 | 317 |
{fmt} is fast to compile and is comparable to `printf` in terms of per-call
@ -230,12 +236,12 @@ binary size (within a rounding error on this system).
**Non-optimized build**
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|---------------|-----------------|----------------------|--------------------|
|-----------------|-----------------|----------------------|--------------------|
| printf | 1.4 | 54 | 50 |
| IOStreams | 23.4 | 92 | 68 |
| {fmt} 83652df | 4.4 | 89 | 85 |
| tinyformat | 24.5 | 204 | 161 |
| Boost Format | 36.4 | 831 | 462 |
| IOStreams | 27.0 | 88 | 68 |
| {fmt} `1122268` | 4.7 | 87 | 84 |
| tinyformat | 28.1 | 185 | 145 |
| Boost Format | 38.9 | 678 | 381 |
`libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries
to compare formatting function overhead only. Boost Format is a
@ -374,98 +380,6 @@ If you are aware of other projects using this library, please let me
know by [email](mailto:victor.zverovich@gmail.com) or by submitting an
[issue](https://github.com/fmtlib/fmt/issues).
# Motivation
So why yet another formatting library?
There are plenty of methods for doing this task, from standard ones like
the printf family of function and iostreams to Boost Format and
FastFormat libraries. The reason for creating a new library is that
every existing solution that I found either had serious issues or
didn\'t provide all the features I needed.
## printf
The good thing about `printf` is that it is pretty fast and readily
available being a part of the C standard library. The main drawback is
that it doesn\'t support user-defined types. `printf` also has safety
issues although they are somewhat mitigated with [\_\_attribute\_\_
((format (printf,
\...))](https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html) in
GCC. There is a POSIX extension that adds positional arguments required
for
[i18n](https://en.wikipedia.org/wiki/Internationalization_and_localization)
to `printf` but it is not a part of C99 and may not be available on some
platforms.
## iostreams
The main issue with iostreams is best illustrated with an example:
``` c++
std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";
```
which is a lot of typing compared to printf:
``` c++
printf("%.2f\n", 1.23456);
```
Matthew Wilson, the author of FastFormat, called this \"chevron hell\".
iostreams don\'t support positional arguments by design.
The good part is that iostreams support user-defined types and are safe
although error handling is awkward.
## Boost Format
This is a very powerful library that supports both `printf`-like format
strings and positional arguments. Its main drawback is performance.
According to various benchmarks, it is much slower than other methods
considered here. Boost Format also has excessive build times and severe
code bloat issues (see [Benchmarks](#benchmarks)).
## FastFormat
This is an interesting library that is fast, safe and has positional
arguments. However, it has significant limitations, citing its author:
> Three features that have no hope of being accommodated within the
> current design are:
>
> - Leading zeros (or any other non-space padding)
> - Octal/hexadecimal encoding
> - Runtime width/alignment specification
It is also quite big and has a heavy dependency, on STLSoft, which might be
too restrictive for use in some projects.
## Boost Spirit.Karma
This is not a formatting library but I decided to include it here for
completeness. As iostreams, it suffers from the problem of mixing
verbatim text with arguments. The library is pretty fast, but slower on
integer formatting than `fmt::format_to` with format string compilation
on Karma\'s own benchmark, see [Converting a hundred million integers to
strings per
second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html).
# License
{fmt} is distributed under the MIT
[license](https://github.com/fmtlib/fmt/blob/master/LICENSE).
# Documentation License
The [Format String Syntax](https://fmt.dev/latest/syntax/) section
in the documentation is based on the one from Python [string module
documentation](https://docs.python.org/3/library/string.html#module-string).
For this reason, the documentation is distributed under the Python
Software Foundation license available in
[doc/python-license.txt](https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt).
It only applies if you distribute the documentation of {fmt}.
# Maintainers
The {fmt} library is maintained by Victor Zverovich
@ -475,12 +389,3 @@ people. See
[Releases](https://github.com/fmtlib/fmt/releases) for some of the
names. Let us know if your contribution is not listed or mentioned
incorrectly and we\'ll make it right.
# Security Policy
To report a security issue, please disclose it at [security
advisory](https://github.com/fmtlib/fmt/security/advisories/new).
This project is maintained by a team of volunteers on a
reasonable-effort basis. As such, please give us at least *90* days to
work on a fix before public exposure.

@ -1,6 +1,6 @@
// Formatting library for C++ - dynamic argument lists
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -113,8 +113,7 @@ FMT_EXPORT template <typename Context> class dynamic_format_arg_store {
data_.emplace_back(arg);
}
template <typename T>
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
template <typename T> void emplace_arg(const named_arg<T, char_type>& arg) {
if (named_info_.empty())
data_.insert(data_.begin(), basic_format_arg<Context>(nullptr, 0));
data_.emplace_back(detail::unwrap(arg.value));
@ -152,7 +151,7 @@ FMT_EXPORT template <typename Context> class dynamic_format_arg_store {
* std::string result = fmt::vformat("{} and {} and {}", store);
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
if FMT_CONSTEXPR20 (need_copy<T>::value)
emplace_arg(dynamic_args_.push<stored_t<T>>(arg));
else
emplace_arg(detail::unwrap(arg));
@ -183,11 +182,10 @@ FMT_EXPORT template <typename Context> class dynamic_format_arg_store {
* 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) {
template <typename T> void push_back(const named_arg<T, char_type>& 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)) {
if FMT_CONSTEXPR20 (need_copy<T>::value) {
emplace_arg(
fmt::arg(arg_name, dynamic_args_.push<stored_t<T>>(arg.value)));
} else {

File diff suppressed because it is too large Load Diff

@ -1,6 +1,6 @@
// Formatting library for C++ - chrono support
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -52,7 +52,7 @@ FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (detail::const_check(F::digits <= T::digits)) {
if FMT_CONSTEXPR20 (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
@ -79,22 +79,21 @@ FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (detail::const_check(F::is_signed && !T::is_signed)) {
if FMT_CONSTEXPR20 (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (fmt::detail::is_negative(from)) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (detail::const_check(F::digits > T::digits) &&
if (F::digits > T::digits &&
from > static_cast<From>(detail::max_value<To>())) {
ec = 1;
return {};
}
}
if (detail::const_check(!F::is_signed && T::is_signed &&
F::digits >= T::digits) &&
if (!F::is_signed && T::is_signed && F::digits >= T::digits &&
from > static_cast<From>(detail::max_value<To>())) {
ec = 1;
return {};
@ -188,7 +187,7 @@ auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
}
// multiply with Factor::num without overflow or underflow
if (detail::const_check(Factor::num != 1)) {
if FMT_CONSTEXPR20 (Factor::num != 1) {
constexpr auto max1 = detail::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
@ -205,7 +204,7 @@ auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
}
// this can't go wrong, right? den>0 is checked earlier.
if (detail::const_check(Factor::den != 1)) {
if FMT_CONSTEXPR20 (Factor::den != 1) {
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
count /= static_cast<common_t>(Factor::den);
}
@ -337,7 +336,7 @@ void write_codecvt(codecvt_result<CodeUnit>& out, string_view in,
template <typename OutputIt>
auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc)
-> OutputIt {
if (const_check(detail::use_utf8) && loc != get_classic_locale()) {
if (detail::use_utf8 && loc != get_classic_locale()) {
// char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
// gcc-4.
#if FMT_MSC_VERSION != 0 || \
@ -434,14 +433,14 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
common_rep count = from.count(); // This conversion is lossless.
// Multiply from.count() by factor and check for overflow.
if (const_check(factor::num != 1)) {
if FMT_CONSTEXPR20 (factor::num != 1) {
if (count > max_value<common_rep>() / factor::num) throw_duration_error();
const auto min = (std::numeric_limits<common_rep>::min)() / factor::num;
if (const_check(!std::is_unsigned<common_rep>::value) && count < min)
if (!std::is_unsigned<common_rep>::value && count < min)
throw_duration_error();
count *= factor::num;
}
if (const_check(factor::den != 1)) count /= factor::den;
if FMT_CONSTEXPR20 (factor::den != 1) count /= factor::den;
int ec = 0;
auto to =
To(safe_duration_cast::lossless_integral_conversion<typename To::rep>(
@ -544,8 +543,7 @@ namespace detail {
// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/.
inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
unsigned c, char sep) {
unsigned long long digits =
a | (b << 24) | (static_cast<unsigned long long>(c) << 48);
ullong digits = a | (b << 24) | (static_cast<ullong>(c) << 48);
// Convert each value to BCD.
// We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b.
// The difference is
@ -559,12 +557,12 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
// Put low nibbles to high bytes and high nibbles to low bytes.
digits = ((digits & 0x00f00000f00000f0) >> 4) |
((digits & 0x000f00000f00000f) << 8);
auto usep = static_cast<unsigned long long>(sep);
auto usep = static_cast<ullong>(sep);
// Add ASCII '0' to each digit byte and insert separators.
digits |= 0x3030003030003030 | (usep << 16) | (usep << 40);
constexpr size_t len = 8;
if (const_check(is_big_endian())) {
if (is_big_endian()) {
char tmp[len];
std::memcpy(tmp, &digits, len);
std::reverse_copy(tmp, tmp + len, buf);
@ -1194,6 +1192,7 @@ class tm_writer {
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)>
void format_tz_name(const T& tm) {
if (!tm.tm_zone) FMT_THROW(format_error("no timezone"));
out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
}
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)>
@ -1576,7 +1575,7 @@ auto format_duration_unit(OutputIt out) -> OutputIt {
return copy_unit(string_view(unit), out, Char());
*out++ = '[';
out = write<Char>(out, Period::num);
if (const_check(Period::den != 1)) {
if FMT_CONSTEXPR20 (Period::den != 1) {
*out++ = '/';
out = write<Char>(out, Period::den);
}
@ -2179,9 +2178,9 @@ struct formatter<sys_time<Duration>, Char> : private formatter<std::tm, Char> {
-> decltype(ctx.out()) {
std::tm tm = gmtime(val);
using period = typename Duration::period;
if (detail::const_check(
period::num == 1 && period::den == 1 &&
!std::is_floating_point<typename Duration::rep>::value)) {
if FMT_CONSTEXPR20 (period::num == 1 && period::den == 1 &&
!std::is_floating_point<
typename Duration::rep>::value) {
detail::set_tm_zone(tm, detail::utc());
return formatter<std::tm, Char>::format(tm, ctx);
}

@ -1,6 +1,6 @@
// Formatting library for C++ - color support
//
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
// Copyright (c) 2018 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -471,7 +471,7 @@ template <typename Char> inline void reset_color(buffer<Char>& buffer) {
template <typename T> struct styled_arg : view {
const T& value;
text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {}
FMT_CONSTEXPR styled_arg(const T& v, text_style s) : value(v), style(s) {}
};
template <typename Char>
@ -528,6 +528,42 @@ void print(text_style ts, format_string<T...> fmt, T&&... args) {
return print(stdout, ts, fmt, std::forward<T>(args)...);
}
inline void vprintln(FILE* f, text_style ts, string_view fmt,
format_args args) {
auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args);
buf.push_back('\n');
print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
}
/**
* Formats a string and prints it to the specified file stream followed by a
* newline, using ANSI escape sequences to specify text formatting.
*
* **Example**:
*
* fmt::println(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... T>
void println(FILE* f, text_style ts, format_string<T...> fmt, T&&... args) {
vprintln(f, ts, fmt.str, vargs<T...>{{args...}});
}
/**
* Formats a string and prints it to stdout followed by a newline, using ANSI
* escape sequences to specify text formatting.
*
* **Example**:
*
* fmt::println(fmt::emphasis::bold | fg(fmt::color::red),
* "Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename... T>
void println(text_style ts, format_string<T...> fmt, T&&... args) {
return println(stdout, ts, fmt, std::forward<T>(args)...);
}
inline auto vformat(text_style ts, string_view fmt, format_args args)
-> std::string {
auto buf = memory_buffer();
@ -583,8 +619,8 @@ inline auto format_to(OutputIt out, text_style ts, format_string<T...> fmt,
template <typename T, typename Char>
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
template <typename FormatContext>
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
-> decltype(ctx.out()) {
FMT_CONSTEXPR auto format(const detail::styled_arg<T>& arg,
FormatContext& ctx) const -> decltype(ctx.out()) {
const auto& ts = arg.style;
auto out = ctx.out();

@ -1,6 +1,6 @@
// Formatting library for C++ - experimental format string compilation
//
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -267,13 +267,15 @@ constexpr auto parse_text(basic_string_view<Char> str, size_t pos) -> size_t {
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
template <typename Args, size_t POS, int ID, bool DYNAMIC_NAMES, typename S>
constexpr auto compile_format_string(S fmt);
template <typename Args, size_t POS, int ID, typename T, typename S>
template <typename Args, size_t POS, int ID, bool DYNAMIC_NAMES, typename T,
typename S>
constexpr auto parse_tail(T head, S fmt) {
if constexpr (POS != basic_string_view<typename S::char_type>(fmt).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(fmt);
constexpr auto tail =
compile_format_string<Args, POS, ID, DYNAMIC_NAMES>(fmt);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
@ -347,13 +349,13 @@ struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
};
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S>
bool DYNAMIC_NAMES, typename S>
constexpr auto parse_replacement_field_then_tail(S fmt) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(fmt);
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>(
return parse_tail<Args, END_POS + 1, NEXT_ID, DYNAMIC_NAMES>(
field<char_type, typename field_type<T>::type, ARG_INDEX>(), fmt);
} else if constexpr (c != ':') {
FMT_THROW(format_error("expected ':'"));
@ -364,7 +366,8 @@ constexpr auto parse_replacement_field_then_tail(S fmt) {
FMT_THROW(format_error("expected '}'"));
return 0;
} else {
return parse_tail<Args, result.end + 1, result.next_arg_id>(
return parse_tail<Args, result.end + 1, result.next_arg_id,
DYNAMIC_NAMES>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt},
fmt);
@ -374,7 +377,7 @@ constexpr auto parse_replacement_field_then_tail(S fmt) {
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
template <typename Args, size_t POS, int ID, bool DYNAMIC_NAMES, typename S>
constexpr auto compile_format_string(S fmt) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(fmt);
@ -382,14 +385,15 @@ constexpr auto compile_format_string(S fmt) {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '{' in format string"));
if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
return parse_tail<Args, POS + 2, ID, DYNAMIC_NAMES>(
make_text(str, POS, 1), fmt);
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
static_assert(ID != manual_indexing_id,
"cannot switch from manual to automatic argument indexing");
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
POS + 1, ID, next_id>(fmt);
return parse_replacement_field_then_tail<
get_type<ID, Args>, Args, POS + 1, ID, next_id, DYNAMIC_NAMES>(fmt);
} else {
constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
@ -402,21 +406,24 @@ constexpr auto compile_format_string(S fmt) {
ID == manual_indexing_id || ID == 0,
"cannot switch from automatic to manual argument indexing");
constexpr auto arg_index = arg_id_result.arg_id.index;
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
fmt);
return parse_replacement_field_then_tail<
get_type<arg_index, Args>, Args, arg_id_end_pos, arg_index,
manual_indexing_id, DYNAMIC_NAMES>(fmt);
} else if constexpr (arg_id_result.kind == arg_id_kind::name) {
constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.name, Args{});
static_assert(arg_index >= 0 || DYNAMIC_NAMES,
"named argument not found");
if constexpr (arg_index >= 0) {
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>(fmt);
arg_index, next_id, DYNAMIC_NAMES>(fmt);
} else if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
return parse_tail<Args, arg_id_end_pos + 1, ID, DYNAMIC_NAMES>(
runtime_named_field<char_type>{arg_id_result.arg_id.name}, fmt);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
@ -426,13 +433,16 @@ constexpr auto compile_format_string(S fmt) {
} else if constexpr (str[POS] == '}') {
if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '}' in format string"));
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
return parse_tail<Args, POS + 2, ID, DYNAMIC_NAMES>(make_text(str, POS, 1),
fmt);
} else {
constexpr auto end = parse_text(str, POS + 1);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), fmt);
return parse_tail<Args, end, ID, DYNAMIC_NAMES>(
make_text(str, POS, end - POS), fmt);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, fmt);
return parse_tail<Args, end, ID, DYNAMIC_NAMES>(
code_unit<char_type>{str[POS]}, fmt);
}
}
}
@ -444,8 +454,11 @@ constexpr auto compile(S fmt) {
if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(fmt);
constexpr int num_static_named_args =
detail::count_static_named_args<Args...>();
constexpr auto result = detail::compile_format_string<
detail::type_list<Args...>, 0, 0,
num_static_named_args != detail::count_named_args<Args...>()>(fmt);
return result;
}
}
@ -522,7 +535,7 @@ auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
fmt::format_to(std::back_inserter(buf), fmt, std::forward<T>(args)...);
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
return {buf.out(), buf.count()};
}
@ -559,8 +572,8 @@ template <size_t N> class static_format_result {
*fmt::format_to(data, fmt, std::forward<T>(args)...) = '\0';
}
auto str() const -> fmt::string_view { return {data, N - 1}; }
auto c_str() const -> const char* { return data; }
FMT_CONSTEXPR auto str() const -> fmt::string_view { return {data, N - 1}; }
FMT_CONSTEXPR auto c_str() const -> const char* { return data; }
};
/**

@ -1,5 +1,14 @@
// This file is only provided for compatibility and may be removed in future
// versions. Use fmt/base.h if you don't need fmt::format and fmt/format.h
// otherwise.
// Formatting library for C++ - core API
//
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#include "base.h"
// Using fmt::format via fmt/core.h has been deprecated since version 11
// and now requires an explicit opt in.
#ifdef FMT_DEPRECATED_HEAVY_CORE
# include "format.h"
#endif

@ -0,0 +1,201 @@
// Formatting library for C++ - the C API
//
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_C_H_
#define FMT_C_H_
#include <stdbool.h> // bool
#include <stddef.h> // size_t
#include <stdio.h> // FILE
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
fmt_int = 1,
fmt_uint,
fmt_bool = 7,
fmt_char,
fmt_float,
fmt_double,
fmt_long_double,
fmt_cstring,
fmt_pointer = 14
} fmt_type;
typedef struct {
fmt_type type;
union {
long long int_value;
unsigned long long uint_value; // Used for FMT_PTR and custom data
bool bool_value;
char char_value;
float float_value;
double double_value;
long double long_double_value;
const char* cstring;
const void* pointer;
} value;
} fmt_arg;
enum { fmt_error = -1, fmt_error_invalid_arg = -2 };
int fmt_vformat(char* buffer, size_t size, const char* fmt, const fmt_arg* args,
size_t num_args);
int fmt_vprint(FILE* stream, const char* fmt, const fmt_arg* args,
size_t num_args);
#ifdef __cplusplus
}
#endif
#ifndef __cplusplus
static inline fmt_arg fmt_from_int(long long x) {
return (fmt_arg){.type = fmt_int, .value.int_value = x};
}
static inline fmt_arg fmt_from_uint(unsigned long long x) {
return (fmt_arg){.type = fmt_uint, .value.uint_value = x};
}
static inline fmt_arg fmt_from_bool(bool x) {
return (fmt_arg){.type = fmt_bool, .value.bool_value = x};
}
static inline fmt_arg fmt_from_char(char x) {
return (fmt_arg){.type = fmt_char, .value.char_value = x};
}
static inline fmt_arg fmt_from_float(float x) {
return (fmt_arg){.type = fmt_float, .value.float_value = x};
}
static inline fmt_arg fmt_from_double(double x) {
return (fmt_arg){.type = fmt_double, .value.double_value = x};
}
static inline fmt_arg fmt_from_long_double(long double x) {
return (fmt_arg){.type = fmt_long_double, .value.long_double_value = x};
}
static inline fmt_arg fmt_from_str(const char* x) {
return (fmt_arg){.type = fmt_cstring, .value.cstring = x};
}
static inline fmt_arg fmt_from_ptr(const void* x) {
return (fmt_arg){.type = fmt_pointer, .value.pointer = x};
}
void fmt_unsupported_type(void);
# if !defined(_MSC_VER) || defined(__clang__)
typedef signed char fmt_signed_char;
# else
typedef enum {} fmt_signed_char;
# endif
// Require modern MSVC with conformant preprocessor.
# if defined(_MSC_VER) && !defined(__clang__) && \
(!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL)
# error "C API requires MSVC 2019+ with /Zc:preprocessor flag."
# endif
# define FMT_MAKE_ARG(x) \
_Generic((x), \
fmt_signed_char: fmt_from_int, \
unsigned char: fmt_from_uint, \
short: fmt_from_int, \
unsigned short: fmt_from_uint, \
int: fmt_from_int, \
unsigned int: fmt_from_uint, \
long: fmt_from_int, \
unsigned long: fmt_from_uint, \
long long: fmt_from_int, \
unsigned long long: fmt_from_uint, \
bool: fmt_from_bool, \
char: fmt_from_char, \
float: fmt_from_float, \
double: fmt_from_double, \
long double: fmt_from_long_double, \
char*: fmt_from_str, \
const char*: fmt_from_str, \
void*: fmt_from_ptr, \
const void*: fmt_from_ptr, \
default: fmt_unsupported_type)(x)
# define FMT_CAT(a, b) FMT_CAT_(a, b)
# define FMT_CAT_(a, b) a##b
# define FMT_NARG_(_unused, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, \
_12, _13, _14, _15, _16, N, ...) \
N
# define FMT_NARG(_unused, ...) \
FMT_NARG_(, ##__VA_ARGS__, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, \
3, 2, 1, 0)
# define FMT_MAP_0(...)
# define FMT_MAP_1(f, a) f(a)
# define FMT_MAP_2(f, a, b) f(a), f(b)
# define FMT_MAP_3(f, a, b, c) f(a), f(b), f(c)
# define FMT_MAP_4(f, a, b, c, d) f(a), f(b), f(c), f(d)
# define FMT_MAP_5(f, a, b, c, d, e) f(a), f(b), f(c), f(d), f(e)
# define FMT_MAP_6(f, a, b, c, d, e, g) f(a), f(b), f(c), f(d), f(e), f(g)
# define FMT_MAP_7(f, a, b, c, d, e, g, h) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h)
# define FMT_MAP_8(f, a, b, c, d, e, g, h, i) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i)
# define FMT_MAP_9(f, a, b, c, d, e, g, h, i, j) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j)
# define FMT_MAP_10(f, a, b, c, d, e, g, h, i, j, k) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k)
# define FMT_MAP_11(f, a, b, c, d, e, g, h, i, j, k, l) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l)
# define FMT_MAP_12(f, a, b, c, d, e, g, h, i, j, k, l, m) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m)
# define FMT_MAP_13(f, a, b, c, d, e, g, h, i, j, k, l, m, n) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m), f(n)
# define FMT_MAP_14(f, a, b, c, d, e, g, h, i, j, k, l, m, n, o) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m), \
f(n), f(o)
# define FMT_MAP_15(f, a, b, c, d, e, g, h, i, j, k, l, m, n, o, p) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m), \
f(n), f(o), f(p)
# define FMT_MAP_16(f, a, b, c, d, e, g, h, i, j, k, l, m, n, o, p, q) \
f(a), f(b), f(c), f(d), f(e), f(g), f(h), f(i), f(j), f(k), f(l), f(m), \
f(n), f(o), f(p), f(q)
# define FMT_MAP(f, ...) \
FMT_CAT(FMT_MAP_, FMT_NARG(, ##__VA_ARGS__))(f, ##__VA_ARGS__)
// select between two expressions depending on whether __VA_ARGS__ is empty
// expands to e if __VA_ARGS__ is empty and n otherwise
# define FMT_VA_SELECT(e, n, ...) \
FMT_NARG_(, ##__VA_ARGS__, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, \
e)
# define FMT_MAKE_NULL(...) NULL
# define FMT_MAKE_ARGLIST(...) \
(fmt_arg[]) { FMT_MAP(FMT_MAKE_ARG, ##__VA_ARGS__) }
# define FMT_EXPAND(v) v
# define FMT_FORMAT_ARGS(fmt, ...) \
(fmt), \
FMT_EXPAND(FMT_VA_SELECT(FMT_MAKE_NULL, FMT_MAKE_ARGLIST, \
##__VA_ARGS__)(__VA_ARGS__)), \
FMT_NARG(, ##__VA_ARGS__)
# define fmt_format(buffer, size, fmt, ...) \
fmt_vformat((buffer), (size), FMT_FORMAT_ARGS((fmt), ##__VA_ARGS__))
# define fmt_print(stream, fmt, ...) \
fmt_vprint((stream), FMT_FORMAT_ARGS((fmt), ##__VA_ARGS__))
#endif // __cplusplus
#endif // FMT_C_H_

@ -1,6 +1,6 @@
// Formatting library for C++ - implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -8,12 +8,17 @@
#ifndef FMT_FORMAT_INL_H_
#define FMT_FORMAT_INL_H_
#ifdef __SANITIZE_THREAD__
extern "C" void __tsan_acquire(void*);
extern "C" void __tsan_release(void*);
#endif
#ifndef FMT_MODULE
# include <stddef.h> // ptrdiff_t
# include <algorithm>
# include <cerrno> // errno
# include <climits>
# include <cmath>
# include <exception>
# include <new> // std::bad_alloc
#endif
#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
@ -30,6 +35,14 @@
# define FMT_FUNC
#endif
#if defined(FMT_USE_FULL_CACHE_DRAGONBOX)
// Use the provided definition.
#elif defined(__OPTIMIZE_SIZE__)
# define FMT_USE_FULL_CACHE_DRAGONBOX 0
#else
# define FMT_USE_FULL_CACHE_DRAGONBOX 1
#endif
FMT_BEGIN_NAMESPACE
#ifndef FMT_CUSTOM_ASSERT_FAIL
@ -70,14 +83,19 @@ template <typename Locale> auto locale_ref::get() const -> Locale {
namespace detail {
FMT_FUNC auto allocate(size_t size) -> void* {
void* p = malloc(size);
if (!p) FMT_THROW(std::bad_alloc());
return p;
}
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
string_view message) noexcept {
// Report error code making sure that the output fits into
// inline_buffer_size to avoid dynamic memory allocation and potential
// bad_alloc.
// Report error code making sure that the output fits into inline_buffer_size
// to avoid dynamic memory allocation and potential bad_alloc.
out.try_resize(0);
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
static constexpr char SEP[] = ": ";
static constexpr char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
@ -195,10 +213,9 @@ inline auto umul96_upper64(uint32_t x, uint64_t y) noexcept -> uint64_t {
// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a
// 128-bit unsigned integer.
inline auto umul192_lower128(uint64_t x, uint128_fallback y) noexcept
-> uint128_fallback {
inline auto umul192_lower128(uint64_t x, uint128 y) noexcept -> uint128 {
uint64_t high = x * y.high();
uint128_fallback high_low = umul128(x, y.low());
uint128 high_low = umul128(x, y.low());
return {high + high_low.high(), high_low.low()};
}
@ -366,13 +383,13 @@ template <> struct cache_accessor<float> {
template <> struct cache_accessor<double> {
using carrier_uint = float_info<double>::carrier_uint;
using cache_entry_type = uint128_fallback;
using cache_entry_type = uint128;
static auto get_cached_power(int k) noexcept -> uint128_fallback {
static auto get_cached_power(int k) noexcept -> uint128 {
FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
"k is out of range");
static constexpr uint128_fallback pow10_significands[] = {
static constexpr uint128 pow10_significands[] = {
#if FMT_USE_FULL_CACHE_DRAGONBOX
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0x9faacf3df73609b1, 0x77b191618c54e9ad},
@ -1058,7 +1075,7 @@ template <> struct cache_accessor<double> {
int offset = k - kb;
// Get base cache.
uint128_fallback base_cache = pow10_significands[cache_index];
uint128 base_cache = pow10_significands[cache_index];
if (offset == 0) return base_cache;
// Compute the required amount of bit-shift.
@ -1067,16 +1084,15 @@ template <> struct cache_accessor<double> {
// Try to recover the real cache.
uint64_t pow5 = powers_of_5_64[offset];
uint128_fallback recovered_cache = umul128(base_cache.high(), pow5);
uint128_fallback middle_low = umul128(base_cache.low(), pow5);
uint128 recovered_cache = umul128(base_cache.high(), pow5);
uint128 middle_low = umul128(base_cache.low(), pow5);
recovered_cache += middle_low.high();
uint64_t high_to_middle = recovered_cache.high() << (64 - alpha);
uint64_t middle_to_low = recovered_cache.low() << (64 - alpha);
recovered_cache =
uint128_fallback{(recovered_cache.low() >> alpha) | high_to_middle,
recovered_cache = uint128{(recovered_cache.low() >> alpha) | high_to_middle,
((middle_low.low() >> alpha) | middle_to_low)};
FMT_ASSERT(recovered_cache.low() + 1 != 0, "");
return {recovered_cache.high(), recovered_cache.low() + 1};
@ -1138,7 +1154,7 @@ template <> struct cache_accessor<double> {
}
};
FMT_FUNC auto get_cached_power(int k) noexcept -> uint128_fallback {
FMT_FUNC auto get_cached_power(int k) noexcept -> uint128 {
return cache_accessor<double>::get_cached_power(k);
}
@ -1463,10 +1479,10 @@ template <typename T> struct span {
};
template <typename F> auto flockfile(F* f) -> decltype(_lock_file(f)) {
_lock_file(f);
return _lock_file(f);
}
template <typename F> auto funlockfile(F* f) -> decltype(_unlock_file(f)) {
_unlock_file(f);
return _unlock_file(f);
}
#ifndef getc_unlocked
@ -1475,12 +1491,16 @@ template <typename F> auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) {
}
#endif
#ifndef FMT_USE_FLOCKFILE
# define FMT_USE_FLOCKFILE 1
#endif
template <typename F = FILE, typename Enable = void>
struct has_flockfile : std::false_type {};
template <typename F>
struct has_flockfile<F, void_t<decltype(flockfile(&std::declval<F&>()))>>
: std::true_type {};
: bool_constant<FMT_USE_FLOCKFILE != 0> {};
// A FILE wrapper. F is FILE defined as a template parameter to make system API
// detection work.
@ -1678,6 +1698,9 @@ class file_print_buffer<F, enable_if_t<has_flockfile<F>::value>>
public:
explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) {
flockfile(f);
#ifdef __SANITIZE_THREAD__
__tsan_acquire(f);
#endif
file_.init_buffer();
auto buf = file_.get_write_buffer();
set(buf.data, buf.size);
@ -1685,7 +1708,10 @@ class file_print_buffer<F, enable_if_t<has_flockfile<F>::value>>
~file_print_buffer() {
file_.advance_write_buffer(size());
bool flush = file_.needs_flush();
F* f = file_; // Make funlockfile depend on the template parameter F
F* f = file_; // Make funlockfile depend on the template parameter F.
#ifdef __SANITIZE_THREAD__
__tsan_release(f);
#endif
funlockfile(f); // for the system API detection to work.
if (flush) fflush(file_);
}

@ -1,7 +1,7 @@
/*
Formatting library for C++
Copyright (c) 2012 - present, Victor Zverovich
Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
@ -47,14 +47,12 @@
#endif
#ifndef FMT_MODULE
# include <stdint.h> // uint32_t
# include <stdlib.h> // malloc, free
# include <string.h> // memcpy
# include <cmath> // std::signbit
# include <cstddef> // std::byte
# include <cstdint> // uint32_t
# include <cstring> // std::memcpy
# include <limits> // std::numeric_limits
# include <new> // std::bad_alloc
# if defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)
// Workaround for pre gcc 5 libstdc++.
# include <memory> // std::allocator_traits
@ -122,14 +120,6 @@
# define FMT_NOINLINE
#endif
#ifdef FMT_DEPRECATED
// Use the provided definition.
#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated)
# define FMT_DEPRECATED [[deprecated]]
#else
# define FMT_DEPRECATED /* deprecated */
#endif
// Detect constexpr std::string.
#if !FMT_USE_CONSTEVAL
# define FMT_USE_CONSTEXPR_STRING 0
@ -224,7 +214,6 @@ namespace detail {
inline auto clz(uint32_t x) -> int {
FMT_ASSERT(x != 0, "");
FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
unsigned long r = 0;
_BitScanReverse(&r, x);
return 31 ^ static_cast<int>(r);
@ -233,7 +222,6 @@ inline auto clz(uint32_t x) -> int {
inline auto clzll(uint64_t x) -> int {
FMT_ASSERT(x != 0, "");
FMT_MSC_WARNING(suppress : 6102) // Suppress a bogus static analysis warning.
unsigned long r = 0;
# ifdef _WIN64
_BitScanReverse64(&r, x);
@ -283,7 +271,7 @@ FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To {
#endif
auto to = To();
// The cast suppresses a bogus -Wclass-memaccess on GCC.
std::memcpy(static_cast<void*>(&to), &from, sizeof(to));
memcpy(static_cast<void*>(&to), &from, sizeof(to));
return to;
}
@ -302,13 +290,13 @@ inline auto is_big_endian() -> bool {
#endif
}
class uint128_fallback {
class uint128 {
private:
uint64_t lo_, hi_;
public:
constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}
constexpr uint128(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
constexpr uint128(uint64_t value = 0) : lo_(value), hi_(0) {}
constexpr auto high() const noexcept -> uint64_t { return hi_; }
constexpr auto low() const noexcept -> uint64_t { return lo_; }
@ -318,92 +306,84 @@ class uint128_fallback {
return static_cast<T>(lo_);
}
friend constexpr auto operator==(const uint128_fallback& lhs,
const uint128_fallback& rhs) -> bool {
friend constexpr auto operator==(const uint128& lhs, const uint128& rhs)
-> bool {
return lhs.hi_ == rhs.hi_ && lhs.lo_ == rhs.lo_;
}
friend constexpr auto operator!=(const uint128_fallback& lhs,
const uint128_fallback& rhs) -> bool {
friend constexpr auto operator!=(const uint128& lhs, const uint128& rhs)
-> bool {
return !(lhs == rhs);
}
friend constexpr auto operator>(const uint128_fallback& lhs,
const uint128_fallback& rhs) -> bool {
friend constexpr auto operator>(const uint128& lhs, const uint128& rhs)
-> bool {
return lhs.hi_ != rhs.hi_ ? lhs.hi_ > rhs.hi_ : lhs.lo_ > rhs.lo_;
}
friend constexpr auto operator|(const uint128_fallback& lhs,
const uint128_fallback& rhs)
-> uint128_fallback {
friend constexpr auto operator|(const uint128& lhs, const uint128& rhs)
-> uint128 {
return {lhs.hi_ | rhs.hi_, lhs.lo_ | rhs.lo_};
}
friend constexpr auto operator&(const uint128_fallback& lhs,
const uint128_fallback& rhs)
-> uint128_fallback {
friend constexpr auto operator&(const uint128& lhs, const uint128& rhs)
-> uint128 {
return {lhs.hi_ & rhs.hi_, lhs.lo_ & rhs.lo_};
}
friend constexpr auto operator~(const uint128_fallback& n)
-> uint128_fallback {
return {~n.hi_, ~n.lo_};
}
friend FMT_CONSTEXPR auto operator+(const uint128_fallback& lhs,
const uint128_fallback& rhs)
-> uint128_fallback {
auto result = uint128_fallback(lhs);
friend FMT_CONSTEXPR auto operator+(const uint128& lhs, const uint128& rhs)
-> uint128 {
auto result = uint128(lhs);
result += rhs;
return result;
}
friend FMT_CONSTEXPR auto operator*(const uint128_fallback& lhs, uint32_t rhs)
-> uint128_fallback {
friend FMT_CONSTEXPR auto operator*(const uint128& lhs, uint32_t rhs)
-> uint128 {
FMT_ASSERT(lhs.hi_ == 0, "");
uint64_t hi = (lhs.lo_ >> 32) * rhs;
uint64_t lo = (lhs.lo_ & ~uint32_t()) * rhs;
uint64_t new_lo = (hi << 32) + lo;
return {(hi >> 32) + (new_lo < lo ? 1 : 0), new_lo};
}
friend constexpr auto operator-(const uint128_fallback& lhs, uint64_t rhs)
-> uint128_fallback {
friend constexpr auto operator-(const uint128& lhs, uint64_t rhs) -> uint128 {
return {lhs.hi_ - (lhs.lo_ < rhs ? 1 : 0), lhs.lo_ - rhs};
}
FMT_CONSTEXPR auto operator>>(int shift) const -> uint128_fallback {
FMT_CONSTEXPR auto operator>>(int shift) const -> uint128 {
if (shift == 64) return {0, hi_};
if (shift > 64) return uint128_fallback(0, hi_) >> (shift - 64);
if (shift > 64) return uint128(0, hi_) >> (shift - 64);
return {hi_ >> shift, (hi_ << (64 - shift)) | (lo_ >> shift)};
}
FMT_CONSTEXPR auto operator<<(int shift) const -> uint128_fallback {
FMT_CONSTEXPR auto operator<<(int shift) const -> uint128 {
if (shift == 64) return {lo_, 0};
if (shift > 64) return uint128_fallback(lo_, 0) << (shift - 64);
if (shift > 64) return uint128(lo_, 0) << (shift - 64);
return {hi_ << shift | (lo_ >> (64 - shift)), (lo_ << shift)};
}
FMT_CONSTEXPR auto operator>>=(int shift) -> uint128_fallback& {
FMT_CONSTEXPR auto operator>>=(int shift) -> uint128& {
return *this = *this >> shift;
}
FMT_CONSTEXPR void operator+=(uint128_fallback n) {
FMT_CONSTEXPR void operator+=(uint128 n) {
uint64_t new_lo = lo_ + n.lo_;
uint64_t new_hi = hi_ + n.hi_ + (new_lo < lo_ ? 1 : 0);
FMT_ASSERT(new_hi >= hi_, "");
lo_ = new_lo;
hi_ = new_hi;
}
FMT_CONSTEXPR void operator&=(uint128_fallback n) {
FMT_CONSTEXPR void operator&=(uint128 n) {
lo_ &= n.lo_;
hi_ &= n.hi_;
}
FMT_CONSTEXPR20 auto operator+=(uint64_t n) noexcept -> uint128_fallback& {
FMT_CONSTEXPR20 auto operator+=(uint64_t n) noexcept -> uint128& {
if (is_constant_evaluated()) {
lo_ += n;
hi_ += (lo_ < n ? 1 : 0);
return *this;
}
#if FMT_HAS_BUILTIN(__builtin_addcll) && !defined(__ibmxl__)
unsigned long long carry;
ullong carry;
lo_ = __builtin_addcll(lo_, n, 0, &carry);
hi_ += carry;
#elif FMT_HAS_BUILTIN(__builtin_ia32_addcarryx_u64) && !defined(__ibmxl__)
unsigned long long result;
ullong result;
auto carry = __builtin_ia32_addcarryx_u64(0, lo_, n, &result);
lo_ = result;
hi_ += carry;
#elif defined(_MSC_VER) && defined(_M_X64)
#elif defined(_MSC_VER) && defined(_M_AMD64)
auto carry = _addcarry_u64(0, lo_, n, &lo_);
_addcarry_u64(carry, hi_, 0, &hi_);
#else
@ -414,7 +394,7 @@ class uint128_fallback {
}
};
using uint128_t = conditional_t<FMT_USE_INT128, uint128_opt, uint128_fallback>;
using uint128_t = conditional_t<FMT_USE_INT128, native_uint128, uint128>;
#ifdef UINTPTR_MAX
using uintptr_t = ::uintptr_t;
@ -431,12 +411,12 @@ template <typename T> constexpr auto num_bits() -> int {
return std::numeric_limits<T>::digits;
}
// std::numeric_limits<T>::digits may return 0 for 128-bit ints.
template <> constexpr auto num_bits<int128_opt>() -> int { return 128; }
template <> constexpr auto num_bits<uint128_opt>() -> int { return 128; }
template <> constexpr auto num_bits<uint128_fallback>() -> int { return 128; }
template <> constexpr auto num_bits<native_int128>() -> int { return 128; }
template <> constexpr auto num_bits<native_uint128>() -> int { return 128; }
template <> constexpr auto num_bits<uint128>() -> int { return 128; }
// A heterogeneous bit_cast used for converting 96-bit long double to uint128_t
// and 128-bit pointers to uint128_fallback.
// and 128-bit pointers to uint128.
template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) > sizeof(From))>
inline auto bit_cast(const From& from) -> To {
constexpr auto size = static_cast<int>(sizeof(From) / sizeof(unsigned short));
@ -444,7 +424,7 @@ inline auto bit_cast(const From& from) -> To {
unsigned short value[static_cast<unsigned>(size)];
} data = bit_cast<data_t>(from);
auto result = To();
if (const_check(is_big_endian())) {
if (is_big_endian()) {
for (int i = 0; i < size; ++i)
result = (result << num_bits<unsigned short>()) | data.value[i];
} else {
@ -493,8 +473,8 @@ template <typename OutputIt,
#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION
__attribute__((no_sanitize("undefined")))
#endif
FMT_CONSTEXPR20 inline auto
reserve(OutputIt it, size_t n) -> typename OutputIt::value_type* {
FMT_CONSTEXPR20 inline auto reserve(OutputIt it, size_t n) ->
typename OutputIt::value_type* {
auto& c = get_container(it);
size_t size = c.size();
c.resize(size + n);
@ -564,10 +544,15 @@ FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* {
if (is_constant_evaluated()) return fill_n<T*, Size, T>(out, count, value);
static_assert(sizeof(T) == 1,
"sizeof(T) must be 1 to use char for initialization");
std::memset(out, value, to_unsigned(count));
memset(out, value, to_unsigned(count));
return out + count;
}
template <typename T, typename V, typename OutputIt>
FMT_CONSTEXPR auto copy(basic_string_view<V> s, OutputIt out) -> OutputIt {
return copy<T>(s.begin(), s.end(), out);
}
template <typename OutChar, typename InputIt, typename OutputIt>
FMT_CONSTEXPR FMT_NOINLINE auto copy_noinline(InputIt begin, InputIt end,
OutputIt out) -> OutputIt {
@ -690,13 +675,13 @@ FMT_CONSTEXPR inline auto display_width_of(uint32_t cp) noexcept -> size_t {
}
template <typename T> struct is_integral : std::is_integral<T> {};
template <> struct is_integral<int128_opt> : std::true_type {};
template <> struct is_integral<native_int128> : std::true_type {};
template <> struct is_integral<uint128_t> : std::true_type {};
template <typename T>
using is_signed =
std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
std::is_same<T, int128_opt>::value>;
std::is_same<T, native_int128>::value>;
template <typename T>
using is_integer =
@ -736,21 +721,17 @@ using fast_float_t = conditional_t<sizeof(T) == sizeof(double), double, float>;
template <typename T>
using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>;
#ifndef FMT_USE_FULL_CACHE_DRAGONBOX
# define FMT_USE_FULL_CACHE_DRAGONBOX 0
#endif
FMT_API auto allocate(size_t size) -> void*;
// An allocator that uses malloc/free to allow removing dependency on the C++
// standard libary runtime. std::decay is used for back_inserter to be found by
// standard library runtime. std::decay is used for back_inserter to be found by
// ADL when applied to memory_buffer.
template <typename T> struct allocator : private std::decay<void> {
using value_type = T;
auto allocate(size_t n) -> T* {
FMT_ASSERT(n <= max_value<size_t>() / sizeof(T), "");
T* p = static_cast<T*>(malloc(n * sizeof(T)));
if (!p) FMT_THROW(std::bad_alloc());
return p;
return static_cast<T*>(detail::allocate(n * sizeof(T)));
}
void deallocate(T* p, size_t) { free(p); }
@ -984,7 +965,7 @@ FMT_API void print(FILE*, string_view);
namespace detail {
template <typename Char, size_t N> struct fixed_string {
FMT_CONSTEXPR20 fixed_string(const Char (&s)[N]) {
FMT_CONSTEXPR fixed_string(const Char (&s)[N]) {
detail::copy<Char, const Char*, Char*>(static_cast<const Char*>(s), s + N,
data);
}
@ -1031,12 +1012,12 @@ using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
(factor) * 100000, (factor) * 1000000, (factor) * 10000000, \
(factor) * 100000000, (factor) * 1000000000
// Converts value in the range [0, 100) to a string.
// GCC generates slightly better code when value is pointer-size.
inline auto digits2(size_t value) -> const char* {
// Converts value in the range [0, 100) to a string. GCC generates a bit better
// code when value is pointer-size (https://www.godbolt.org/z/5fEPMT1cc).
inline auto digits2(size_t value) noexcept -> const char* {
// Align data since unaligned access may be slower when crossing a
// hardware-specific boundary.
alignas(2) static const char data[] =
alignas(2) static constexpr char data[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
@ -1045,9 +1026,25 @@ inline auto digits2(size_t value) -> const char* {
return &data[value * 2];
}
// Given i in [0, 100), let x be the first 7 digits after
// the decimal point of i / 100 in base 2, the first 2 bytes
// after digits2_i(x) is the string representation of i.
inline auto digits2_i(size_t value) noexcept -> const char* {
alignas(2) static constexpr char data[] =
"00010203 0405060707080910 1112"
"131414151617 18192021 222324 "
"25262728 2930313232333435 3637"
"383939404142 43444546 474849 "
"50515253 5455565757585960 6162"
"636464656667 68697071 727374 "
"75767778 7980818282838485 8687"
"888989909192 93949596 979899 ";
return &data[value * 2];
}
template <typename Char> constexpr auto getsign(sign s) -> Char {
return static_cast<char>(((' ' << 24) | ('+' << 16) | ('-' << 8)) >>
(static_cast<int>(s) * 8));
return static_cast<Char>(static_cast<char>(
((' ' << 24) | ('+' << 16) | ('-' << 8)) >> (static_cast<int>(s) * 8)));
}
template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
@ -1065,7 +1062,7 @@ template <typename T> FMT_CONSTEXPR auto count_digits_fallback(T n) -> int {
}
}
#if FMT_USE_INT128
FMT_CONSTEXPR inline auto count_digits(uint128_opt n) -> int {
FMT_CONSTEXPR inline auto count_digits(native_uint128 n) -> int {
return count_digits_fallback(n);
}
#endif
@ -1153,7 +1150,9 @@ FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int {
template <typename Int> constexpr auto digits10() noexcept -> int {
return std::numeric_limits<Int>::digits10;
}
template <> constexpr auto digits10<int128_opt>() noexcept -> int { return 38; }
template <> constexpr auto digits10<native_int128>() noexcept -> int {
return 38;
}
template <> constexpr auto digits10<uint128_t>() noexcept -> int { return 38; }
template <typename Char> struct thousands_sep_result {
@ -1166,7 +1165,7 @@ FMT_API auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char>;
template <typename Char>
inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<Char> {
auto result = thousands_sep_impl<char>(loc);
return {result.grouping, Char(result.thousands_sep)};
return {std::move(result.grouping), Char(result.thousands_sep)};
}
template <>
inline auto thousands_sep(locale_ref loc) -> thousands_sep_result<wchar_t> {
@ -1213,6 +1212,16 @@ FMT_CONSTEXPR20 FMT_INLINE void write2digits(Char* out, size_t value) {
*out = static_cast<Char>('0' + value % 10);
}
template <typename Char>
FMT_INLINE void write2digits_i(Char* out, size_t value) {
if (std::is_same<Char, char>::value && !FMT_OPTIMIZE_SIZE) {
memcpy(out, digits2_i(value), 2);
return;
}
*out++ = static_cast<Char>(digits2_i(value)[0]);
*out = static_cast<Char>(digits2_i(value)[1]);
}
// Formats a decimal unsigned integer value writing to out pointing to a buffer
// of specified size. The caller must ensure that the buffer is large enough.
template <typename Char, typename UInt>
@ -1221,13 +1230,20 @@ FMT_CONSTEXPR20 auto do_format_decimal(Char* out, UInt value, int size)
FMT_ASSERT(size >= count_digits(value), "invalid digit count");
unsigned n = to_unsigned(size);
while (value >= 100) {
n -= 2;
if (!is_constant_evaluated() && sizeof(UInt) == 4) {
auto p = value * static_cast<uint64_t>((1ull << 39) / 100 + 1);
write2digits_i(out + n, p >> (39 - 7) & ((1 << 7) - 1));
value = static_cast<UInt>(p >> 39) +
(static_cast<UInt>(value >= (100u << 25)) << 25);
} else {
// Integer division is slow so do it for a group of two digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
n -= 2;
write2digits(out + n, static_cast<unsigned>(value % 100));
value /= 100;
}
}
if (value >= 10) {
n -= 2;
write2digits(out + n, static_cast<unsigned>(value));
@ -1311,7 +1327,13 @@ class utf8_to_utf16 {
inline auto str() const -> std::wstring { return {&buffer_[0], size()}; }
};
enum class to_utf8_error_policy { abort, replace };
enum class to_utf8_error_policy { abort, replace, wtf };
inline void to_utf8_3bytes(buffer<char>& buf, uint32_t cp) {
buf.push_back(static_cast<char>(0xe0 | (cp >> 12)));
buf.push_back(static_cast<char>(0x80 | ((cp & 0xfff) >> 6)));
buf.push_back(static_cast<char>(0x80 | (cp & 0x3f)));
}
// A converter from UTF-16/UTF-32 (host endian) to UTF-8.
template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
@ -1353,8 +1375,13 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
// Handle a surrogate pair.
++p;
if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
if (policy == to_utf8_error_policy::abort) return false;
switch (policy) {
case to_utf8_error_policy::abort: return false;
case to_utf8_error_policy::replace:
buf.append(string_view("\xEF\xBF\xBD"));
break;
case to_utf8_error_policy::wtf: to_utf8_3bytes(buf, c); break;
}
--p;
continue;
}
@ -1366,9 +1393,7 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
} else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
to_utf8_3bytes(buf, c);
} else if (c >= 0x10000 && c <= 0x10ffff) {
buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
@ -1383,11 +1408,11 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
};
// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
FMT_INLINE auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback {
FMT_INLINE auto umul128(uint64_t x, uint64_t y) noexcept -> uint128 {
#if FMT_USE_INT128
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
auto p = static_cast<native_uint128>(x) * static_cast<native_uint128>(y);
return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
#elif defined(_MSC_VER) && defined(_M_X64)
#elif defined(_MSC_VER) && defined(_M_AMD64)
auto hi = uint64_t();
auto lo = _umul128(x, y, &hi);
return {hi, lo};
@ -1428,9 +1453,9 @@ inline auto floor_log2_pow10(int e) noexcept -> int {
// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
inline auto umul128_upper64(uint64_t x, uint64_t y) noexcept -> uint64_t {
#if FMT_USE_INT128
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
auto p = static_cast<native_uint128>(x) * static_cast<native_uint128>(y);
return static_cast<uint64_t>(p >> 64);
#elif defined(_MSC_VER) && defined(_M_X64)
#elif defined(_MSC_VER) && defined(_M_AMD64)
return __umulh(x, y);
#else
return umul128(x, y).high();
@ -1439,40 +1464,39 @@ inline auto umul128_upper64(uint64_t x, uint64_t y) noexcept -> uint64_t {
// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
// 128-bit unsigned integer.
inline auto umul192_upper128(uint64_t x, uint128_fallback y) noexcept
-> uint128_fallback {
uint128_fallback r = umul128(x, y.high());
inline auto umul192_upper128(uint64_t x, uint128 y) noexcept -> uint128 {
uint128 r = umul128(x, y.high());
r += umul128_upper64(x, y.low());
return r;
}
FMT_API auto get_cached_power(int k) noexcept -> uint128_fallback;
FMT_API auto get_cached_power(int k) noexcept -> uint128;
// Type-specific information that Dragonbox uses.
template <typename T, typename Enable = void> struct float_info;
template <> struct float_info<float> {
using carrier_uint = uint32_t;
static const int exponent_bits = 8;
static const int kappa = 1;
static const int big_divisor = 100;
static const int small_divisor = 10;
static const int min_k = -31;
static const int max_k = 46;
static const int shorter_interval_tie_lower_threshold = -35;
static const int shorter_interval_tie_upper_threshold = -35;
static constexpr int exponent_bits = 8;
static constexpr int kappa = 1;
static constexpr int big_divisor = 100;
static constexpr int small_divisor = 10;
static constexpr int min_k = -31;
enum { max_k = 46 };
static constexpr int shorter_interval_tie_lower_threshold = -35;
static constexpr int shorter_interval_tie_upper_threshold = -35;
};
template <> struct float_info<double> {
using carrier_uint = uint64_t;
static const int exponent_bits = 11;
static const int kappa = 2;
static const int big_divisor = 1000;
static const int small_divisor = 100;
static const int min_k = -292;
static const int max_k = 341;
static const int shorter_interval_tie_lower_threshold = -77;
static const int shorter_interval_tie_upper_threshold = -77;
static constexpr int exponent_bits = 11;
static constexpr int kappa = 2;
static constexpr int big_divisor = 1000;
static constexpr int small_divisor = 100;
static constexpr int min_k = -292;
enum { max_k = 341 };
static constexpr int shorter_interval_tie_lower_threshold = -77;
static constexpr int shorter_interval_tie_upper_threshold = -77;
};
// An 80- or 128-bit floating point number.
@ -1608,7 +1632,7 @@ template <typename F> struct basic_fp {
}
};
using fp = basic_fp<unsigned long long>;
using fp = basic_fp<ullong>;
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT = 0, typename F>
@ -1739,7 +1763,7 @@ FMT_API auto is_printable(uint32_t cp) -> bool;
inline auto needs_escape(uint32_t cp) -> bool {
if (cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\') return true;
if (const_check(FMT_OPTIMIZE_SIZE > 1)) return false;
if FMT_CONSTEXPR20 (FMT_OPTIMIZE_SIZE > 1) return false;
return !is_printable(cp);
}
@ -1754,7 +1778,7 @@ auto find_escape(const Char* begin, const Char* end)
-> find_escape_result<Char> {
for (; begin != end; ++begin) {
uint32_t cp = static_cast<unsigned_char<Char>>(*begin);
if (const_check(sizeof(Char) == 1) && cp >= 0x80) continue;
if (sizeof(Char) == 1 && cp >= 0x80) continue;
if (needs_escape(cp)) return {begin, begin + 1, cp};
}
return {begin, nullptr, 0};
@ -1762,7 +1786,7 @@ auto find_escape(const Char* begin, const Char* end)
inline auto find_escape(const char* begin, const char* end)
-> find_escape_result<char> {
if (const_check(!use_utf8)) return find_escape<char>(begin, end);
if FMT_CONSTEXPR20 (!use_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) {
@ -1890,7 +1914,7 @@ template <typename Char> class digit_grouping {
explicit digit_grouping(locale_ref loc, bool localized = true) {
if (!localized) return;
auto sep = thousands_sep<Char>(loc);
grouping_ = sep.grouping;
grouping_ = std::move(sep.grouping);
if (sep.thousands_sep) thousands_sep_.assign(1, sep.thousands_sep);
}
digit_grouping(std::string grouping, std::basic_string<Char> sep)
@ -2335,9 +2359,8 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end,
++begin;
}
break;
} else if (p == begin) {
break;
}
if (p == begin) break;
p = begin;
}
specs.set_align(alignment);
@ -2534,7 +2557,7 @@ FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f,
auto grouping = Grouping(loc, specs.localized());
size += grouping.count_separators(exp);
return write_padded<Char, align::right>(
out, specs, to_unsigned(size), [&](iterator it) {
out, specs, static_cast<size_t>(size), [&](iterator it) {
if (s != sign::none) *it++ = detail::getsign<Char>(s);
it = write_significand(it, f.significand, significand_size, exp,
decimal_point, grouping);
@ -2550,7 +2573,7 @@ FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f,
bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt();
size += 1 + (pointy ? 1 : 0) + num_zeros;
return write_padded<Char, align::right>(
out, specs, to_unsigned(size), [&](iterator it) {
out, specs, static_cast<size_t>(size), [&](iterator it) {
if (s != sign::none) *it++ = detail::getsign<Char>(s);
*it++ = Char('0');
if (!pointy) return it;
@ -2594,7 +2617,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
*it++ = Char(exp_char);
return write_exponent<Char>(exp, it);
};
auto usize = to_unsigned(size);
size_t usize = static_cast<size_t>(size);
return specs.width > 0
? write_padded<Char, align::right>(out, specs, usize, write)
: base_iterator(out, write(reserve(out, usize)));
@ -3138,8 +3161,9 @@ constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t {
// It is equal to ceil(2^31 + 2^32/10^(k + 1)).
// These are stored in a string literal because we cannot have static arrays
// in constexpr functions and non-static ones are poorly optimized.
return U"\x9999999a\x828f5c29\x80418938\x80068db9\x8000a7c6\x800010c7"
U"\x800001ae\x8000002b"[index];
return uint32_t(u"\x9999\x828f\x8041\x8006\x8000\x8000\x8000\x8000"[index])
<< 16u |
uint32_t(u"\x999a\x5c29\x8938\x8db9\xa7c6\x10c7\x01ae\x002b"[index]);
}
template <typename Float>
@ -3544,8 +3568,8 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
memcpy(ptr, prefix, 2);
ptr += 2;
} else {
*ptr++ = prefix[0];
*ptr++ = prefix[1];
*ptr++ = static_cast<Char>(prefix[0]);
*ptr++ = static_cast<Char>(prefix[1]);
}
if (abs_exponent >= 100) {
*ptr++ = static_cast<Char>('0' + abs_exponent / 100);
@ -3697,12 +3721,12 @@ template <typename Char> struct arg_formatter {
struct dynamic_spec_getter {
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
return is_negative(value) ? ~0ull : static_cast<unsigned long long>(value);
FMT_CONSTEXPR auto operator()(T value) -> ullong {
return is_negative(value) ? ~0ull : static_cast<ullong>(value);
}
template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
FMT_CONSTEXPR auto operator()(T) -> ullong {
report_error("width/precision is not integer");
return 0;
}
@ -3716,7 +3740,7 @@ FMT_CONSTEXPR void handle_dynamic_spec(
auto arg =
kind == arg_id_kind::index ? ctx.arg(ref.index) : ctx.arg(ref.name);
if (!arg) report_error("argument not found");
unsigned long long result = arg.visit(dynamic_spec_getter());
ullong result = arg.visit(dynamic_spec_getter());
if (result > to_unsigned(max_value<int>()))
report_error("width/precision is out of range");
value = static_cast<int>(result);
@ -3751,7 +3775,7 @@ struct udl_arg {
template <typename Char> struct udl_arg {
const Char* str;
template <typename T> auto operator=(T&& value) const -> named_arg<Char, T> {
template <typename T> auto operator=(T&& value) const -> named_arg<T, Char> {
return {str, std::forward<T>(value)};
}
};
@ -3864,7 +3888,7 @@ template <typename OutputIt, typename Char> class generic_context {
constexpr auto out() const -> iterator { return out_; }
void advance_to(iterator it) {
FMT_CONSTEXPR void advance_to(iterator it) {
if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
}
@ -3906,8 +3930,8 @@ template <typename Locale> class format_facet : public Locale::facet {
explicit format_facet(string_view sep = "", std::string grouping = "\3",
std::string decimal_point = ".")
: separator_(sep.data(), sep.size()),
grouping_(grouping),
decimal_point_(decimal_point) {}
grouping_(std::move(grouping)),
decimal_point_(std::move(decimal_point)) {}
auto put(appender out, loc_value val, const format_specs& specs) const
-> bool {
@ -3943,12 +3967,6 @@ template <typename Char, typename Traits, typename Allocator>
class formatter<std::basic_string<Char, Traits, Allocator>, Char>
: public formatter<basic_string_view<Char>, Char> {};
template <int N, typename Char>
struct formatter<detail::bitint<N>, Char> : formatter<long long, Char> {};
template <int N, typename Char>
struct formatter<detail::ubitint<N>, Char>
: formatter<unsigned long long, Char> {};
template <typename Char>
struct formatter<detail::float128, Char>
: detail::native_formatter<detail::float128, Char,
@ -3997,19 +4015,6 @@ constexpr auto format_as(Enum e) noexcept -> underlying_t<Enum> {
}
} // namespace enums
#ifdef __cpp_lib_byte
template <typename Char>
struct formatter<std::byte, Char> : formatter<unsigned, Char> {
static auto format_as(std::byte b) -> unsigned char {
return static_cast<unsigned char>(b);
}
template <typename Context>
auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) {
return formatter<unsigned, Char>::format(format_as(b), ctx);
}
};
#endif
struct bytes {
string_view data;
@ -4142,6 +4147,14 @@ template <typename T, typename Char = char> struct nested_formatter {
inline namespace literals {
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
/**
* User-defined literal equivalent of `fmt::arg`, but with compile-time checks.
*
* **Example**:
*
* using namespace fmt::literals;
* fmt::print("The answer is {answer}.", "answer"_a=42);
*/
template <detail::fixed_string S> constexpr auto operator""_a() {
using char_t = remove_cvref_t<decltype(*S.data)>;
return detail::udl_arg<char_t, sizeof(S.data) / sizeof(char_t), S>();
@ -4166,7 +4179,7 @@ class format_int {
private:
// Buffer should be large enough to hold all digits (digits10 + 1),
// a sign and a null character.
enum { buffer_size = std::numeric_limits<unsigned long long>::digits10 + 3 };
enum { buffer_size = std::numeric_limits<ullong>::digits10 + 3 };
mutable char buffer_[buffer_size];
char* str_;
@ -4196,7 +4209,7 @@ class format_int {
: str_(format_unsigned(value)) {}
FMT_CONSTEXPR20 explicit format_int(unsigned long value)
: str_(format_unsigned(value)) {}
FMT_CONSTEXPR20 explicit format_int(unsigned long long value)
FMT_CONSTEXPR20 explicit format_int(ullong value)
: str_(format_unsigned(value)) {}
/// Returns the number of characters written to the output buffer.
@ -4248,7 +4261,11 @@ class format_int {
* // A compile-time error because 'd' is an invalid specifier for strings.
* std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
*/
#if FMT_USE_CONSTEVAL
# define FMT_STRING(s) s
#else
# define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string)
#endif // FMT_USE_CONSTEVAL
FMT_API auto vsystem_error(int error_code, string_view fmt, format_args args)
-> std::system_error;

@ -1,6 +1,6 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -161,14 +161,6 @@ inline auto system_category() noexcept -> const std::error_category& {
}
#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& fmt, Args&&... args) {
std::system(format("say \"{}\"", format(fmt, args...)).c_str());
}
#endif
// A buffered file.
class buffered_file {
private:

@ -1,6 +1,6 @@
// Formatting library for C++ - std::ostream support
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -150,7 +150,7 @@ inline void vprint(std::ostream& os, string_view fmt, format_args args) {
FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::vargs<T...> vargs = {{args...}};
if (detail::const_check(detail::use_utf8)) return vprint(os, fmt.str, vargs);
if FMT_CONSTEXPR20 (detail::use_utf8) return vprint(os, fmt.str, vargs);
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt.str, vargs);
detail::write_buffer(os, buffer);

@ -1,6 +1,6 @@
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -136,7 +136,7 @@ template <typename T, typename Context> class arg_converter {
void operator()(U value) {
bool is_signed = type_ == 'd' || type_ == 'i';
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) {
if FMT_CONSTEXPR20 (sizeof(target_type) <= sizeof(int)) {
// Extra casts are used to silence warnings.
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
if (is_signed)
@ -330,6 +330,7 @@ template <typename Char, typename GetArg>
auto parse_header(const Char*& it, const Char* end, format_specs& specs,
GetArg get_arg) -> int {
int arg_index = -1;
if (it == end) return arg_index;
Char c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
@ -357,10 +358,23 @@ auto parse_header(const Char*& it, const Char* end, format_specs& specs,
if (specs.width == -1) report_error("number is too big");
} else if (*it == '*') {
++it;
// Check for positional width argument like *1$
if (it != end && *it >= '0' && *it <= '9') {
int width_index = parse_nonnegative_int(it, end, -1);
if (it != end && *it == '$') {
++it;
specs.width = static_cast<int>(
get_arg(width_index).visit(detail::printf_width_handler(specs)));
} else {
// Invalid format, rewind and treat as non-positional
report_error("invalid format specifier");
}
} else {
specs.width = static_cast<int>(
get_arg(-1).visit(detail::printf_width_handler(specs)));
}
}
}
return arg_index;
}
@ -424,6 +438,8 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
}
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
if (it == end) report_error("invalid format string");
auto specs = format_specs();
specs.set_align(align::right);
@ -439,15 +455,28 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
specs.precision = parse_nonnegative_int(it, end, 0);
} else if (c == '*') {
++it;
// Check for positional precision argument like .*1$
if (it != end && *it >= '0' && *it <= '9') {
int precision_index = parse_nonnegative_int(it, end, -1);
if (it != end && *it == '$') {
++it;
specs.precision = static_cast<int>(
get_arg(precision_index).visit(printf_precision_handler()));
} else {
// Invalid format, rewind and treat as non-positional
report_error("invalid format specifier");
}
} else {
specs.precision =
static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
}
} else {
specs.precision = 0;
}
}
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
if (specs.precision >= 0 && is_integral_type(arg.type())) {
// Ignore '0' for non-numeric types or if '-' present.
@ -560,7 +589,7 @@ inline auto vsprintf(basic_string_view<Char> fmt,
/**
* Formats `args` according to specifications in `fmt` and returns the result
* as as string.
* as string.
*
* **Example**:
*

@ -69,11 +69,12 @@ struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
// Member function overloads.
template <typename T>
auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) {
FMT_CONSTEXPR auto range_begin(T&& rng)
-> decltype(static_cast<T&&>(rng).begin()) {
return static_cast<T&&>(rng).begin();
}
template <typename T>
auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
FMT_CONSTEXPR auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
return static_cast<T&&>(rng).end();
}
@ -129,6 +130,13 @@ template <typename T> class is_tuple_like_ {
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename T, typename _ = void>
struct is_optional_like_ : std::false_type {};
template <typename T>
struct is_optional_like_<T, void_t<decltype(std::declval<T>().has_value()),
decltype(std::declval<T>().value())>>
: std::true_type {};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
template <typename T, T... N>
@ -343,8 +351,9 @@ struct formatter<Tuple, Char,
FMT_EXPORT
template <typename T, typename Char> struct is_range {
static constexpr bool value =
detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
static constexpr bool value = detail::is_range_<T>::value &&
!detail::is_optional_like_<T>::value &&
!detail::has_to_string_view<T>::value;
};
namespace detail {
@ -460,7 +469,8 @@ struct range_formatter<
}
template <typename R, typename FormatContext>
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
FMT_CONSTEXPR auto format(R&& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
auto it = detail::range_begin(range);
auto end = detail::range_end(range);
@ -505,8 +515,7 @@ struct formatter<
using nonlocking = void;
FMT_CONSTEXPR formatter() {
if (detail::const_check(range_format_kind<R, Char>::value !=
range_format::set))
if FMT_CONSTEXPR20 (range_format_kind<R, Char>::value != range_format::set)
return;
range_formatter_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
@ -517,7 +526,7 @@ struct formatter<
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
FMT_CONSTEXPR auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
return range_formatter_.format(range, ctx);
}
@ -607,13 +616,14 @@ struct formatter<
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
if FMT_CONSTEXPR20 (range_format_kind<R, Char>::value ==
range_format::debug_string) {
*out++ = '"';
}
out = underlying_.format(
string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
if FMT_CONSTEXPR20 (range_format_kind<R, Char>::value ==
range_format::debug_string)
*out++ = '"';
return out;
}
@ -625,7 +635,7 @@ struct join_view : detail::view {
Sentinel end;
basic_string_view<Char> sep;
join_view(It b, Sentinel e, basic_string_view<Char> s)
FMT_CONSTEXPR join_view(It b, Sentinel e, basic_string_view<Char> s)
: begin(std::move(b)), end(e), sep(s) {}
};
@ -652,7 +662,8 @@ struct formatter<join_view<It, Sentinel, Char>, Char> {
}
template <typename FormatContext>
auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) {
FMT_CONSTEXPR auto format(view& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
using iter =
conditional_t<std::is_copy_constructible<view>::value, It, It&>;
iter it = value.begin;
@ -675,7 +686,7 @@ template <typename Tuple, typename Char> struct tuple_join_view : detail::view {
const Tuple& tuple;
basic_string_view<Char> sep;
tuple_join_view(const Tuple& t, basic_string_view<Char> s)
FMT_CONSTEXPR tuple_join_view(const Tuple& t, basic_string_view<Char> s)
: tuple(t), sep{s} {}
};
@ -694,8 +705,9 @@ struct formatter<tuple_join_view<Tuple, Char>, Char,
}
template <typename FormatContext>
auto format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx) const -> typename FormatContext::iterator {
FMT_CONSTEXPR auto format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx) const ->
typename FormatContext::iterator {
return do_format(value, ctx, std::tuple_size<Tuple>());
}
@ -726,14 +738,16 @@ struct formatter<tuple_join_view<Tuple, Char>, Char,
}
template <typename FormatContext>
auto do_format(const tuple_join_view<Tuple, Char>&, FormatContext& ctx,
FMT_CONSTEXPR auto do_format(const tuple_join_view<Tuple, Char>&,
FormatContext& ctx,
std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator {
return ctx.out();
}
template <typename FormatContext, size_t N>
auto do_format(const tuple_join_view<Tuple, Char>& value, FormatContext& ctx,
FMT_CONSTEXPR auto do_format(const tuple_join_view<Tuple, Char>& value,
FormatContext& ctx,
std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator {
using std::get;
@ -748,9 +762,20 @@ struct formatter<tuple_join_view<Tuple, Char>, Char,
};
namespace detail {
// Check if T has an interface like a container adaptor (e.g. std::stack,
// std::queue, std::priority_queue).
template <typename T> class is_container_adaptor_like {
template <typename Container> struct all {
const Container& c;
auto begin() const -> typename Container::const_iterator { return c.begin(); }
auto end() const -> typename Container::const_iterator { return c.end(); }
};
} // namespace detail
/**
* Specifies if `T` is a container adaptor (like `std::stack`) that should be
* formatted as the underlying container.
*/
FMT_EXPORT
template <typename T> struct is_container_adaptor {
private:
template <typename U> static auto check(U* p) -> typename U::container_type;
template <typename> static void check(...);
@ -759,17 +784,10 @@ template <typename T> class is_container_adaptor_like {
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Container> struct all {
const Container& c;
auto begin() const -> typename Container::const_iterator { return c.begin(); }
auto end() const -> typename Container::const_iterator { return c.end(); }
};
} // namespace detail
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<conjunction<detail::is_container_adaptor_like<T>,
enable_if_t<conjunction<is_container_adaptor<T>,
bool_constant<range_format_kind<T, Char>::value ==
range_format::disabled>>::value>>
: formatter<detail::all<typename T::container_type>, Char> {
@ -809,7 +827,7 @@ auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
* // Output: 01, 02, 03
*/
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& r, string_view sep)
FMT_CONSTEXPR auto join(Range&& r, string_view sep)
-> join_view<decltype(detail::range_begin(r)),
decltype(detail::range_end(r))> {
return {detail::range_begin(r), detail::range_end(r), sep};
@ -840,7 +858,7 @@ FMT_CONSTEXPR auto join(const Tuple& tuple FMT_LIFETIMEBOUND, string_view sep)
* // Output: "1, 2, 3"
*/
template <typename T>
auto join(std::initializer_list<T> list, string_view sep)
FMT_DEPRECATED auto join(std::initializer_list<T> list, string_view sep)
-> join_view<const T*, const T*> {
return join(std::begin(list), std::end(list), sep);
}

@ -1,6 +1,6 @@
// Formatting library for C++ - formatters for standard library types
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -15,7 +15,8 @@
# include <atomic>
# include <bitset>
# include <complex>
# include <exception>
# include <cstddef> // std::byte
# include <exception> // std::exception
# include <functional> // std::reference_wrapper
# include <memory>
# include <thread>
@ -79,16 +80,35 @@
FMT_BEGIN_NAMESPACE
namespace detail {
#ifdef FMT_USE_BITINT
// Use the provided definition.
#elif FMT_CLANG_VERSION >= 1500 && !defined(__CUDACC__)
# define FMT_USE_BITINT 1
#else
# define FMT_USE_BITINT 0
#endif
#if FMT_USE_BITINT
FMT_PRAGMA_CLANG(diagnostic ignored "-Wbit-int-extension")
template <int N> using bitint = _BitInt(N);
template <int N> using ubitint = unsigned _BitInt(N);
#else
template <int N> struct bitint {};
template <int N> struct ubitint {};
#endif // FMT_USE_BITINT
#if FMT_CPP_LIB_FILESYSTEM
template <typename Char, typename PathChar>
auto get_path_string(const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
else
if constexpr (std::is_same_v<Char, char> &&
std::is_same_v<PathChar, wchar_t>) {
return to_utf8<wchar_t>(native, to_utf8_error_policy::wtf);
} else {
return p.string<Char>();
}
}
template <typename Char, typename PathChar>
void write_escaped_path(basic_memory_buffer<Char>& quoted,
@ -113,8 +133,8 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
template <typename Char, typename OutputIt, typename T, typename FormatContext>
auto write_escaped_alternative(OutputIt out, const T& v, FormatContext& ctx)
-> OutputIt {
FMT_CONSTEXPR auto write_escaped_alternative(OutputIt out, const T& v,
FormatContext& ctx) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
@ -434,6 +454,26 @@ struct formatter<std::expected<T, E>, Char,
return out;
}
};
template <typename E, typename Char>
struct formatter<std::unexpected<E>, Char,
std::enable_if_t<is_formattable<E, Char>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::unexpected<E>& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write<Char>(out, "unexpected(");
out = detail::write_escaped_alternative<Char>(out, value.error(), ctx);
*out++ = ')';
return out;
}
};
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location
@ -468,7 +508,7 @@ template <typename Char> struct formatter<std::monostate, Char> {
}
template <typename FormatContext>
auto format(const std::monostate&, FormatContext& ctx) const
FMT_CONSTEXPR auto format(const std::monostate&, FormatContext& ctx) const
-> decltype(ctx.out()) {
return detail::write<Char>(ctx.out(), "monostate");
}
@ -484,7 +524,7 @@ struct formatter<Variant, Char,
}
template <typename FormatContext>
auto format(const Variant& value, FormatContext& ctx) const
FMT_CONSTEXPR20 auto format(const Variant& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
@ -609,6 +649,30 @@ struct formatter<
}
};
template <int N, typename Char>
struct formatter<detail::bitint<N>, Char> : formatter<long long, Char> {
static_assert(N <= 64, "unsupported _BitInt");
static auto format_as(detail::bitint<N> x) -> long long {
return static_cast<long long>(x);
}
template <typename Context>
auto format(detail::bitint<N> x, Context& ctx) const -> decltype(ctx.out()) {
return formatter<long long, Char>::format(format_as(x), ctx);
}
};
template <int N, typename Char>
struct formatter<detail::ubitint<N>, Char> : formatter<ullong, Char> {
static_assert(N <= 64, "unsupported _BitInt");
static auto format_as(detail::ubitint<N> x) -> ullong {
return static_cast<ullong>(x);
}
template <typename Context>
auto format(detail::ubitint<N> x, Context& ctx) const -> decltype(ctx.out()) {
return formatter<ullong, Char>::format(format_as(x), ctx);
}
};
// We can't use std::vector<bool, Allocator>::reference and
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
// in partial specialization.
@ -623,6 +687,20 @@ struct formatter<BitRef, Char,
}
};
#ifdef __cpp_lib_byte
template <typename Char>
struct formatter<std::byte, Char> : formatter<unsigned, Char> {
FMT_CONSTEXPR static auto format_as(std::byte b) -> unsigned char {
return static_cast<unsigned char>(b);
}
template <typename Context>
FMT_CONSTEXPR auto format(std::byte b, Context& ctx) const
-> decltype(ctx.out()) {
return formatter<unsigned, Char>::format(format_as(b), ctx);
}
};
#endif
template <typename T, typename Char>
struct formatter<std::atomic<T>, Char,
enable_if_t<is_formattable<T, Char>::value>>
@ -645,6 +723,11 @@ struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
};
#endif // __cpp_lib_atomic_flag_test
template <typename T> struct is_tuple_like;
template <typename T>
struct is_tuple_like<std::complex<T>> : std::false_type {};
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private:
detail::dynamic_format_specs<Char> specs_;

@ -1,6 +1,6 @@
// Formatting library for C++ - optional wchar_t and exotic character support
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -109,7 +109,7 @@ template <typename Char, typename... T> struct basic_fstring {
}
basic_fstring(runtime_format_string<Char> fmt) : str_(fmt.str) {}
operator basic_string_view<Char>() const { return str_; }
FMT_DEPRECATED operator basic_string_view<Char>() const { return str_; }
auto get() const -> basic_string_view<Char> { return str_; }
};
@ -136,29 +136,42 @@ inline auto operator""_a(const wchar_t* s, size_t) -> detail::udl_arg<wchar_t> {
} // namespace literals
#endif
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 T>
auto arg(const wchar_t* name, const T& arg) -> named_arg<T, wchar_t> {
return {name, arg};
}
template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& range, wstring_view sep)
-> join_view<decltype(std::begin(range)), decltype(std::end(range)),
wchar_t> {
return join(std::begin(range), std::end(range), sep);
template <typename It, typename Sentinel, typename S,
typename Char = typename decltype(detail::to_string_view(
std::declval<S>()))::value_type,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
auto join(It begin, Sentinel end, S&& sep) -> join_view<It, Sentinel, Char> {
return {begin, end, detail::to_string_view(sep)};
}
template <typename Range, typename S,
typename Char = typename decltype(detail::to_string_view(
std::declval<S>()))::value_type,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value &&
!is_tuple_like<Range>::value)>
auto join(Range&& range, S&& sep)
-> join_view<decltype(std::begin(range)), decltype(std::end(range)), Char> {
return {std::begin(range), std::end(range), detail::to_string_view(sep)};
}
template <typename T>
auto join(std::initializer_list<T> list, wstring_view sep)
FMT_DEPRECATED 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 Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<Tuple, wchar_t> {
return {tuple, sep};
template <typename Tuple, typename S,
typename Char = typename decltype(detail::to_string_view(
std::declval<S>()))::value_type,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value&&
is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, S&& sep) -> tuple_join_view<Tuple, Char> {
return {tuple, detail::to_string_view(sep)};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
@ -172,14 +185,13 @@ auto vformat(basic_string_view<Char> fmt,
template <typename... T>
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
return vformat(fmt.get(), fmt::make_wformat_args(args...));
}
template <typename OutputIt, typename... T>
auto format_to(OutputIt out, wformat_string<T...> fmt, T&&... args)
-> OutputIt {
return vformat_to(out, fmt::wstring_view(fmt),
fmt::make_wformat_args(args...));
return vformat_to(out, fmt.get(), fmt::make_wformat_args(args...));
}
// Pass char_t as a default template parameter instead of using
@ -267,10 +279,18 @@ inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
return {buf.out(), buf.count()};
}
template <typename OutputIt, typename... T,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, wchar_t>::value)>
FMT_INLINE auto format_to_n(OutputIt out, size_t n, wformat_string<T...> fmt,
T&&... args) -> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, fmt.get(), fmt::make_wformat_args(args...));
}
template <typename OutputIt, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_exotic_char<Char>::value)>
!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, fmt::basic_string_view<Char>(fmt),
@ -301,11 +321,11 @@ inline void vprint(wstring_view fmt, wformat_args 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...));
return vprint(f, fmt.get(), 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...));
return vprint(fmt.get(), fmt::make_wformat_args(args...));
}
template <typename... T>
@ -327,7 +347,7 @@ inline auto vformat(text_style ts, wstring_view fmt, wformat_args args)
template <typename... T>
inline auto format(text_style ts, wformat_string<T...> fmt, T&&... args)
-> std::wstring {
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
return fmt::vformat(ts, fmt.get(), fmt::make_wformat_args(args...));
}
inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
@ -338,7 +358,8 @@ inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
template <typename... T>
void print(std::wostream& os, wformat_string<T...> fmt, T&&... args) {
vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...));
vprint(os, fmt.get(),
fmt::make_format_args<buffered_context<wchar_t>>(args...));
}
template <typename... T>

@ -0,0 +1,67 @@
// Formatting library for C++ - the C API
//
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/fmt-c.h"
#include <fmt/base.h>
constexpr size_t max_c_format_args = 16;
static int convert_c_format_args(
fmt::basic_format_arg<fmt::format_context>* format_args,
const fmt_arg* args, size_t num_args) {
if (num_args > max_c_format_args) return fmt_error_invalid_arg;
for (size_t i = 0; i < num_args; ++i) {
switch (args[i].type) {
case fmt_int: format_args[i] = args[i].value.int_value; break;
case fmt_uint: format_args[i] = args[i].value.uint_value; break;
case fmt_bool: format_args[i] = args[i].value.bool_value; break;
case fmt_char: format_args[i] = args[i].value.char_value; break;
case fmt_float: format_args[i] = args[i].value.float_value; break;
case fmt_double: format_args[i] = args[i].value.double_value; break;
case fmt_long_double:
format_args[i] = args[i].value.long_double_value;
break;
case fmt_cstring: format_args[i] = args[i].value.cstring; break;
case fmt_pointer: format_args[i] = args[i].value.pointer; break;
default: return fmt_error_invalid_arg;
}
}
return 0;
}
extern "C" int fmt_vformat(char* buffer, size_t size, const char* fmt,
const fmt_arg* args, size_t num_args) {
fmt::basic_format_arg<fmt::format_context> format_args[max_c_format_args];
int error = convert_c_format_args(format_args, args, num_args);
if (error != 0) return error;
FMT_TRY {
auto result = fmt::vformat_to_n(
buffer, size, fmt,
fmt::format_args(format_args, static_cast<int>(num_args)));
return static_cast<int>(result.size);
}
FMT_CATCH(...) {}
return fmt_error;
}
extern "C" int fmt_vprint(FILE* stream, const char* fmt, const fmt_arg* args,
size_t num_args) {
fmt::basic_format_arg<fmt::format_context> format_args[max_c_format_args];
int error = convert_c_format_args(format_args, args, num_args);
if (error != 0) return error;
FMT_TRY {
fmt::vprint(stream, fmt,
fmt::format_args(format_args, static_cast<int>(num_args)));
return 0;
}
FMT_CATCH(...) {}
return fmt_error;
}

@ -1,3 +1,10 @@
// Formatting library for C++ - C++20 module
//
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
module;
#define FMT_MODULE
@ -134,11 +141,6 @@ extern "C++" {
}
#endif
// gcc doesn't yet implement private module fragments
#if !FMT_GCC_VERSION
module :private;
#endif
#ifdef FMT_ATTACH_TO_GLOBAL_MODULE
extern "C++" {
#endif

@ -1,6 +1,6 @@
// Formatting library for C++
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.

@ -1,6 +1,6 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
@ -13,7 +13,6 @@
#include "fmt/os.h"
#ifndef FMT_MODULE
# include <climits>
# if FMT_USE_FCNTL
# include <sys/stat.h>
@ -35,6 +34,8 @@
# ifdef _WIN32
# include <windows.h>
# include <climits> // CHAR_BIT
# endif
#endif
@ -61,6 +62,7 @@
namespace {
#ifdef _WIN32
// Return type of read and write functions.
using rwresult = int;
@ -69,18 +71,6 @@ using rwresult = int;
inline unsigned convert_rwcount(size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#elif FMT_USE_FCNTL
// Return type of read and write functions.
using rwresult = ssize_t;
inline auto convert_rwcount(size_t count) -> size_t { return count; }
#endif
} // namespace
FMT_BEGIN_NAMESPACE
#ifdef _WIN32
namespace detail {
class system_message {
system_message(const system_message&) = delete;
@ -109,8 +99,8 @@ class system_message {
}
~system_message() { LocalFree(message_); }
explicit operator bool() const noexcept { return result_ != 0; }
operator basic_string_view<wchar_t>() const noexcept {
return basic_string_view<wchar_t>(message_, result_);
operator fmt::basic_string_view<wchar_t>() const noexcept {
return fmt::basic_string_view<wchar_t>(message_, result_);
}
};
@ -120,7 +110,7 @@ class utf8_system_category final : public std::error_category {
std::string message(int error_code) const override {
auto&& msg = system_message(error_code);
if (msg) {
auto utf8_message = to_utf8<wchar_t>();
auto utf8_message = fmt::detail::to_utf8<wchar_t>();
if (utf8_message.convert(msg)) {
return utf8_message.str();
}
@ -129,10 +119,22 @@ class utf8_system_category final : public std::error_category {
}
};
} // namespace detail
#elif FMT_USE_FCNTL
// Return type of read and write functions.
using rwresult = ssize_t;
inline auto convert_rwcount(size_t count) -> size_t { return count; }
#endif
} // namespace
FMT_BEGIN_NAMESPACE
#ifdef _WIN32
FMT_API const std::error_category& system_category() noexcept {
static const detail::utf8_system_category category;
static const utf8_system_category category;
return category;
}
@ -162,6 +164,7 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
void report_windows_error(int error_code, const char* message) noexcept {
do_report_error(detail::format_windows_error, error_code, message);
}
#endif // _WIN32
buffered_file::~buffered_file() noexcept {

@ -1,7 +1,11 @@
# A CMake script to find SetEnv.cmd.
find_program(WINSDK_SETENV NAMES SetEnv.cmd
PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]/bin")
find_program(
WINSDK_SETENV
NAMES SetEnv.cmd
PATHS
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]/bin"
)
if (WINSDK_SETENV AND PRINT_PATH)
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${WINSDK_SETENV}")
endif ()

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

Loading…
Cancel
Save