mirror of
https://github.com/gabime/spdlog.git
synced 2025-09-29 01:29:35 +08:00
Compare commits
11 Commits
v1.15.2
...
fmt-11.2.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
14ab43ec0b | ||
![]() |
ebf72d7d3e | ||
![]() |
829deec2ee | ||
![]() |
943fcbd761 | ||
![]() |
7e022c4300 | ||
![]() |
548b264254 | ||
![]() |
847db3375f | ||
![]() |
bb8694b50f | ||
![]() |
cec28bf839 | ||
![]() |
bd0609d7a0 | ||
![]() |
1f4959c832 |
209
CMakeLists.txt
209
CMakeLists.txt
@@ -18,39 +18,38 @@ include(GNUInstallDirs)
|
|||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Set default build to release
|
# Set default build to release
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||||
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
|
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Compiler config
|
# Compiler config
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
if (SPDLOG_USE_STD_FORMAT)
|
if(SPDLOG_USE_STD_FORMAT)
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
elseif (NOT CMAKE_CXX_STANDARD)
|
elseif(NOT CMAKE_CXX_STANDARD)
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
if (CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS" OR CMAKE_SYSTEM_NAME MATCHES "MINGW")
|
if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS" OR CMAKE_SYSTEM_NAME MATCHES "MINGW")
|
||||||
set(CMAKE_CXX_EXTENSIONS ON)
|
set(CMAKE_CXX_EXTENSIONS ON)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog
|
# Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Check if spdlog is being used directly or via add_subdirectory, but allow overriding
|
# Check if spdlog is being used directly or via add_subdirectory, but allow overriding
|
||||||
if (NOT DEFINED SPDLOG_MASTER_PROJECT)
|
if(NOT DEFINED SPDLOG_MASTER_PROJECT)
|
||||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||||
set(SPDLOG_MASTER_PROJECT ON)
|
set(SPDLOG_MASTER_PROJECT ON)
|
||||||
else ()
|
else()
|
||||||
set(SPDLOG_MASTER_PROJECT OFF)
|
set(SPDLOG_MASTER_PROJECT OFF)
|
||||||
endif ()
|
endif()
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
option(SPDLOG_BUILD_ALL "Build all artifacts" OFF)
|
option(SPDLOG_BUILD_ALL "Build all artifacts" OFF)
|
||||||
|
|
||||||
@@ -77,9 +76,9 @@ option(SPDLOG_BUILD_BENCH "Build benchmarks (Requires https://github.com/google/
|
|||||||
# sanitizer options
|
# sanitizer options
|
||||||
option(SPDLOG_SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF)
|
option(SPDLOG_SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF)
|
||||||
option(SPDLOG_SANITIZE_THREAD "Enable thread sanitizer in tests" OFF)
|
option(SPDLOG_SANITIZE_THREAD "Enable thread sanitizer in tests" OFF)
|
||||||
if (SPDLOG_SANITIZE_ADDRESS AND SPDLOG_SANITIZE_THREAD)
|
if(SPDLOG_SANITIZE_ADDRESS AND SPDLOG_SANITIZE_THREAD)
|
||||||
message(FATAL_ERROR "SPDLOG_SANITIZE_ADDRESS and SPDLOG_SANITIZE_THREAD are mutually exclusive")
|
message(FATAL_ERROR "SPDLOG_SANITIZE_ADDRESS and SPDLOG_SANITIZE_THREAD are mutually exclusive")
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
# warning options
|
# warning options
|
||||||
option(SPDLOG_BUILD_WARNINGS "Enable compiler warnings" OFF)
|
option(SPDLOG_BUILD_WARNINGS "Enable compiler warnings" OFF)
|
||||||
@@ -92,38 +91,38 @@ option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
|
|||||||
option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF)
|
option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF)
|
||||||
option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF)
|
option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF)
|
||||||
|
|
||||||
if (SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO)
|
if(SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO)
|
||||||
message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
|
message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
if (SPDLOG_USE_STD_FORMAT AND SPDLOG_FMT_EXTERNAL_HO)
|
if(SPDLOG_USE_STD_FORMAT AND SPDLOG_FMT_EXTERNAL_HO)
|
||||||
message(FATAL_ERROR "SPDLOG_USE_STD_FORMAT and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
|
message(FATAL_ERROR "SPDLOG_USE_STD_FORMAT and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
if (SPDLOG_USE_STD_FORMAT AND SPDLOG_FMT_EXTERNAL)
|
if(SPDLOG_USE_STD_FORMAT AND SPDLOG_FMT_EXTERNAL)
|
||||||
message(FATAL_ERROR "SPDLOG_USE_STD_FORMAT and SPDLOG_FMT_EXTERNAL are mutually exclusive")
|
message(FATAL_ERROR "SPDLOG_USE_STD_FORMAT and SPDLOG_FMT_EXTERNAL are mutually exclusive")
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
# misc tweakme options
|
# misc tweakme options
|
||||||
if (WIN32)
|
if(WIN32)
|
||||||
option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF)
|
option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF)
|
||||||
option(SPDLOG_WCHAR_FILENAMES "Support wchar filenames" OFF)
|
option(SPDLOG_WCHAR_FILENAMES "Support wchar filenames" OFF)
|
||||||
option(SPDLOG_WCHAR_CONSOLE "Support wchar output to console" OFF)
|
option(SPDLOG_WCHAR_CONSOLE "Support wchar output to console" OFF)
|
||||||
else ()
|
else()
|
||||||
set(SPDLOG_WCHAR_SUPPORT OFF CACHE BOOL "non supported option" FORCE)
|
set(SPDLOG_WCHAR_SUPPORT OFF CACHE BOOL "non supported option" FORCE)
|
||||||
set(SPDLOG_WCHAR_FILENAMES OFF CACHE BOOL "non supported option" FORCE)
|
set(SPDLOG_WCHAR_FILENAMES OFF CACHE BOOL "non supported option" FORCE)
|
||||||
set(SPDLOG_WCHAR_CONSOLE OFF CACHE BOOL "non supported option" FORCE)
|
set(SPDLOG_WCHAR_CONSOLE OFF CACHE BOOL "non supported option" FORCE)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
if (MSVC)
|
if(MSVC)
|
||||||
option(SPDLOG_MSVC_UTF8 "Enable/disable msvc /utf-8 flag required by fmt lib" ON)
|
option(SPDLOG_MSVC_UTF8 "Enable/disable msvc /utf-8 flag required by fmt lib" ON)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
option(SPDLOG_CLOCK_COARSE "Use CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
|
option(SPDLOG_CLOCK_COARSE "Use CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
|
||||||
else ()
|
else()
|
||||||
set(SPDLOG_CLOCK_COARSE OFF CACHE BOOL "non supported option" FORCE)
|
set(SPDLOG_CLOCK_COARSE OFF CACHE BOOL "non supported option" FORCE)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF)
|
option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF)
|
||||||
option(SPDLOG_NO_THREAD_ID "prevent spdlog from querying the thread id on each log call if thread id is not needed" OFF)
|
option(SPDLOG_NO_THREAD_ID "prevent spdlog from querying the thread id on each log call if thread id is not needed" OFF)
|
||||||
@@ -138,15 +137,15 @@ option(SPDLOG_FWRITE_UNLOCKED "Use the unlocked variant of fwrite. Leave this on
|
|||||||
# clang-tidy
|
# clang-tidy
|
||||||
option(SPDLOG_TIDY "run clang-tidy" OFF)
|
option(SPDLOG_TIDY "run clang-tidy" OFF)
|
||||||
|
|
||||||
if (SPDLOG_TIDY)
|
if(SPDLOG_TIDY)
|
||||||
set(CMAKE_CXX_CLANG_TIDY "clang-tidy")
|
set(CMAKE_CXX_CLANG_TIDY "clang-tidy")
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
message(STATUS "Enabled clang-tidy")
|
message(STATUS "Enabled clang-tidy")
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
if (SPDLOG_BUILD_PIC)
|
if(SPDLOG_BUILD_PIC)
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
|
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
|
||||||
@@ -155,34 +154,34 @@ message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
|
|||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
set(SPDLOG_SRCS src/spdlog.cpp src/stdout_sinks.cpp src/color_sinks.cpp src/file_sinks.cpp src/async.cpp src/cfg.cpp)
|
set(SPDLOG_SRCS src/spdlog.cpp src/stdout_sinks.cpp src/color_sinks.cpp src/file_sinks.cpp src/async.cpp src/cfg.cpp)
|
||||||
|
|
||||||
if (NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
if(NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
||||||
list(APPEND SPDLOG_SRCS src/bundled_fmtlib_format.cpp)
|
list(APPEND SPDLOG_SRCS src/bundled_fmtlib_format.cpp)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
if (SPDLOG_BUILD_SHARED OR BUILD_SHARED_LIBS)
|
if(SPDLOG_BUILD_SHARED OR BUILD_SHARED_LIBS)
|
||||||
if (WIN32)
|
if(WIN32)
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
|
||||||
list(APPEND SPDLOG_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
list(APPEND SPDLOG_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
||||||
endif ()
|
endif()
|
||||||
add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
|
add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
|
||||||
target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB)
|
target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB)
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||||
target_compile_options(spdlog PUBLIC $<$<AND:$<CXX_COMPILER_ID:MSVC>,$<NOT:$<COMPILE_LANGUAGE:CUDA>>>:/wd4251
|
target_compile_options(spdlog PUBLIC $<$<AND:$<CXX_COMPILER_ID:MSVC>,$<NOT:$<COMPILE_LANGUAGE:CUDA>>>:/wd4251
|
||||||
/wd4275>)
|
/wd4275>)
|
||||||
endif ()
|
endif()
|
||||||
if (NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
if(NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
||||||
target_compile_definitions(spdlog PRIVATE FMT_LIB_EXPORT PUBLIC FMT_SHARED)
|
target_compile_definitions(spdlog PRIVATE FMT_LIB_EXPORT PUBLIC FMT_SHARED)
|
||||||
endif ()
|
endif()
|
||||||
else ()
|
else()
|
||||||
add_library(spdlog STATIC ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
|
add_library(spdlog STATIC ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
add_library(spdlog::spdlog ALIAS spdlog)
|
add_library(spdlog::spdlog ALIAS spdlog)
|
||||||
|
|
||||||
set(SPDLOG_INCLUDES_LEVEL "")
|
set(SPDLOG_INCLUDES_LEVEL "")
|
||||||
if (SPDLOG_SYSTEM_INCLUDES)
|
if(SPDLOG_SYSTEM_INCLUDES)
|
||||||
set(SPDLOG_INCLUDES_LEVEL "SYSTEM")
|
set(SPDLOG_INCLUDES_LEVEL "SYSTEM")
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(spdlog PUBLIC SPDLOG_COMPILED_LIB)
|
target_compile_definitions(spdlog PUBLIC SPDLOG_COMPILED_LIB)
|
||||||
target_include_directories(spdlog ${SPDLOG_INCLUDES_LEVEL} PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
target_include_directories(spdlog ${SPDLOG_INCLUDES_LEVEL} PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
||||||
@@ -194,17 +193,17 @@ set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION
|
|||||||
${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR})
|
${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR})
|
||||||
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX d)
|
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX d)
|
||||||
|
|
||||||
if (COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH)
|
if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH)
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pch.h.in ${PROJECT_BINARY_DIR}/spdlog_pch.h @ONLY)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pch.h.in ${PROJECT_BINARY_DIR}/spdlog_pch.h @ONLY)
|
||||||
target_precompile_headers(spdlog PRIVATE ${PROJECT_BINARY_DIR}/spdlog_pch.h)
|
target_precompile_headers(spdlog PRIVATE ${PROJECT_BINARY_DIR}/spdlog_pch.h)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
# sanitizer support
|
# sanitizer support
|
||||||
if (SPDLOG_SANITIZE_ADDRESS)
|
if(SPDLOG_SANITIZE_ADDRESS)
|
||||||
spdlog_enable_addr_sanitizer(spdlog)
|
spdlog_enable_addr_sanitizer(spdlog)
|
||||||
elseif (SPDLOG_SANITIZE_THREAD)
|
elseif(SPDLOG_SANITIZE_THREAD)
|
||||||
spdlog_enable_thread_sanitizer(spdlog)
|
spdlog_enable_thread_sanitizer(spdlog)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Header only version
|
# Header only version
|
||||||
@@ -220,55 +219,55 @@ target_link_libraries(spdlog_header_only INTERFACE Threads::Threads)
|
|||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Use fmt package if using external fmt
|
# Use fmt package if using external fmt
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
if (SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
|
if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
|
||||||
if (NOT TARGET fmt::fmt)
|
if(NOT TARGET fmt::fmt)
|
||||||
find_package(fmt CONFIG REQUIRED)
|
find_package(fmt CONFIG REQUIRED)
|
||||||
endif ()
|
endif()
|
||||||
target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL)
|
target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL)
|
||||||
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL)
|
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL)
|
||||||
|
|
||||||
# use external fmt-header-only
|
# use external fmt-header-only
|
||||||
if (SPDLOG_FMT_EXTERNAL_HO)
|
if(SPDLOG_FMT_EXTERNAL_HO)
|
||||||
target_link_libraries(spdlog PUBLIC fmt::fmt-header-only)
|
target_link_libraries(spdlog PUBLIC fmt::fmt-header-only)
|
||||||
target_link_libraries(spdlog_header_only INTERFACE fmt::fmt-header-only)
|
target_link_libraries(spdlog_header_only INTERFACE fmt::fmt-header-only)
|
||||||
else () # use external compile fmt
|
else() # use external compile fmt
|
||||||
target_link_libraries(spdlog PUBLIC fmt::fmt)
|
target_link_libraries(spdlog PUBLIC fmt::fmt)
|
||||||
target_link_libraries(spdlog_header_only INTERFACE fmt::fmt)
|
target_link_libraries(spdlog_header_only INTERFACE fmt::fmt)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
set(PKG_CONFIG_REQUIRES fmt) # add dependency to pkg-config
|
set(PKG_CONFIG_REQUIRES fmt) # add dependency to pkg-config
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Check if fwrite_unlocked/_fwrite_nolock is available
|
# Check if fwrite_unlocked/_fwrite_nolock is available
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
if (SPDLOG_FWRITE_UNLOCKED)
|
if(SPDLOG_FWRITE_UNLOCKED)
|
||||||
include(CheckSymbolExists)
|
include(CheckSymbolExists)
|
||||||
if (WIN32)
|
if(WIN32)
|
||||||
check_symbol_exists(_fwrite_nolock "stdio.h" HAVE_FWRITE_UNLOCKED)
|
check_symbol_exists(_fwrite_nolock "stdio.h" HAVE_FWRITE_UNLOCKED)
|
||||||
else ()
|
else()
|
||||||
check_symbol_exists(fwrite_unlocked "stdio.h" HAVE_FWRITE_UNLOCKED)
|
check_symbol_exists(fwrite_unlocked "stdio.h" HAVE_FWRITE_UNLOCKED)
|
||||||
endif ()
|
endif()
|
||||||
if (HAVE_FWRITE_UNLOCKED)
|
if(HAVE_FWRITE_UNLOCKED)
|
||||||
target_compile_definitions(spdlog PRIVATE SPDLOG_FWRITE_UNLOCKED)
|
target_compile_definitions(spdlog PRIVATE SPDLOG_FWRITE_UNLOCKED)
|
||||||
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FWRITE_UNLOCKED)
|
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FWRITE_UNLOCKED)
|
||||||
endif ()
|
endif()
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Add required libraries for Android CMake build
|
# Add required libraries for Android CMake build
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
if (ANDROID)
|
if(ANDROID)
|
||||||
target_link_libraries(spdlog PUBLIC log)
|
target_link_libraries(spdlog PUBLIC log)
|
||||||
target_link_libraries(spdlog_header_only INTERFACE log)
|
target_link_libraries(spdlog_header_only INTERFACE log)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Misc definitions according to tweak options
|
# Misc definitions according to tweak options
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
set(SPDLOG_WCHAR_TO_UTF8_SUPPORT ${SPDLOG_WCHAR_SUPPORT})
|
set(SPDLOG_WCHAR_TO_UTF8_SUPPORT ${SPDLOG_WCHAR_SUPPORT})
|
||||||
set(SPDLOG_UTF8_TO_WCHAR_CONSOLE ${SPDLOG_WCHAR_CONSOLE})
|
set(SPDLOG_UTF8_TO_WCHAR_CONSOLE ${SPDLOG_WCHAR_CONSOLE})
|
||||||
foreach (
|
foreach(
|
||||||
SPDLOG_OPTION
|
SPDLOG_OPTION
|
||||||
SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
SPDLOG_UTF8_TO_WCHAR_CONSOLE
|
SPDLOG_UTF8_TO_WCHAR_CONSOLE
|
||||||
@@ -281,64 +280,66 @@ foreach (
|
|||||||
SPDLOG_NO_ATOMIC_LEVELS
|
SPDLOG_NO_ATOMIC_LEVELS
|
||||||
SPDLOG_DISABLE_DEFAULT_LOGGER
|
SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
SPDLOG_USE_STD_FORMAT)
|
SPDLOG_USE_STD_FORMAT)
|
||||||
if (${SPDLOG_OPTION})
|
if(${SPDLOG_OPTION})
|
||||||
target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION})
|
target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION})
|
||||||
target_compile_definitions(spdlog_header_only INTERFACE ${SPDLOG_OPTION})
|
target_compile_definitions(spdlog_header_only INTERFACE ${SPDLOG_OPTION})
|
||||||
endif ()
|
endif()
|
||||||
endforeach ()
|
endforeach()
|
||||||
|
|
||||||
if (MSVC)
|
if(MSVC)
|
||||||
target_compile_options(spdlog PRIVATE "/Zc:__cplusplus")
|
target_compile_options(spdlog PRIVATE "/Zc:__cplusplus")
|
||||||
target_compile_options(spdlog_header_only INTERFACE "/Zc:__cplusplus")
|
target_compile_options(spdlog_header_only INTERFACE "/Zc:__cplusplus")
|
||||||
if (SPDLOG_MSVC_UTF8)
|
if(SPDLOG_MSVC_UTF8)
|
||||||
# fmtlib requires the /utf-8 flag when building with msvc.
|
# fmtlib requires the /utf-8 flag when building with msvc. see https://github.com/fmtlib/fmt/pull/4159 on the
|
||||||
# see https://github.com/fmtlib/fmt/pull/4159 on the purpose of the additional
|
# purpose of the additional
|
||||||
# "$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>"
|
# "$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>"
|
||||||
target_compile_options(spdlog PUBLIC $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/utf-8>)
|
target_compile_options(spdlog PUBLIC $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/utf-8>)
|
||||||
target_compile_options(spdlog_header_only INTERFACE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/utf-8>)
|
target_compile_options(spdlog_header_only
|
||||||
endif ()
|
INTERFACE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/utf-8>)
|
||||||
endif ()
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# If exceptions are disabled, disable them in the bundled fmt as well
|
# If exceptions are disabled, disable them in the bundled fmt as well
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
if (SPDLOG_NO_EXCEPTIONS)
|
if(SPDLOG_NO_EXCEPTIONS)
|
||||||
if (NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
||||||
target_compile_definitions(spdlog PUBLIC FMT_EXCEPTIONS=0)
|
target_compile_definitions(spdlog PUBLIC FMT_USE_EXCEPTIONS=0)
|
||||||
endif ()
|
endif()
|
||||||
if (NOT MSVC)
|
if(NOT MSVC)
|
||||||
target_compile_options(spdlog PRIVATE -fno-exceptions)
|
target_compile_options(spdlog PRIVATE -fno-exceptions)
|
||||||
else ()
|
else()
|
||||||
target_compile_options(spdlog PRIVATE /EHs-c-)
|
target_compile_options(spdlog PRIVATE /EHs-c-)
|
||||||
endif ()
|
target_compile_definitions(spdlog PRIVATE _HAS_EXCEPTIONS=0)
|
||||||
endif ()
|
endif()
|
||||||
|
endif()
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Build binaries
|
# Build binaries
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
if (SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_EXAMPLE_HO OR SPDLOG_BUILD_ALL)
|
if(SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_EXAMPLE_HO OR SPDLOG_BUILD_ALL)
|
||||||
message(STATUS "Generating example(s)")
|
message(STATUS "Generating example(s)")
|
||||||
add_subdirectory(example)
|
add_subdirectory(example)
|
||||||
spdlog_enable_warnings(example)
|
spdlog_enable_warnings(example)
|
||||||
if (SPDLOG_BUILD_EXAMPLE_HO)
|
if(SPDLOG_BUILD_EXAMPLE_HO)
|
||||||
spdlog_enable_warnings(example_header_only)
|
spdlog_enable_warnings(example_header_only)
|
||||||
endif ()
|
endif()
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
if (SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_TESTS_HO OR SPDLOG_BUILD_ALL)
|
if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_TESTS_HO OR SPDLOG_BUILD_ALL)
|
||||||
message(STATUS "Generating tests")
|
message(STATUS "Generating tests")
|
||||||
enable_testing()
|
enable_testing()
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
if (SPDLOG_BUILD_BENCH OR SPDLOG_BUILD_ALL)
|
if(SPDLOG_BUILD_BENCH OR SPDLOG_BUILD_ALL)
|
||||||
message(STATUS "Generating benchmarks")
|
message(STATUS "Generating benchmarks")
|
||||||
add_subdirectory(bench)
|
add_subdirectory(bench)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Install
|
# Install
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
if (SPDLOG_INSTALL)
|
if(SPDLOG_INSTALL)
|
||||||
message(STATUS "Generating install")
|
message(STATUS "Generating install")
|
||||||
set(project_config_in "${CMAKE_CURRENT_LIST_DIR}/cmake/spdlogConfig.cmake.in")
|
set(project_config_in "${CMAKE_CURRENT_LIST_DIR}/cmake/spdlogConfig.cmake.in")
|
||||||
set(project_config_out "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfig.cmake")
|
set(project_config_out "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfig.cmake")
|
||||||
@@ -359,24 +360,24 @@ if (SPDLOG_INSTALL)
|
|||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
|
||||||
if (NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
if(NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
||||||
install(DIRECTORY include/${PROJECT_NAME}/fmt/bundled/
|
install(DIRECTORY include/${PROJECT_NAME}/fmt/bundled/
|
||||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/fmt/bundled/")
|
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/fmt/bundled/")
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Install pkg-config file
|
# Install pkg-config file
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
if (IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
|
if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
set(PKG_CONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}")
|
set(PKG_CONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
else ()
|
else()
|
||||||
set(PKG_CONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
set(PKG_CONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
endif ()
|
endif()
|
||||||
if (IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
|
if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
|
||||||
set(PKG_CONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}")
|
set(PKG_CONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}")
|
||||||
else ()
|
else()
|
||||||
set(PKG_CONFIG_LIBDIR "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
|
set(PKG_CONFIG_LIBDIR "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||||
endif ()
|
endif()
|
||||||
get_target_property(PKG_CONFIG_DEFINES spdlog INTERFACE_COMPILE_DEFINITIONS)
|
get_target_property(PKG_CONFIG_DEFINES spdlog INTERFACE_COMPILE_DEFINITIONS)
|
||||||
string(REPLACE ";" " -D" PKG_CONFIG_DEFINES "${PKG_CONFIG_DEFINES}")
|
string(REPLACE ";" " -D" PKG_CONFIG_DEFINES "${PKG_CONFIG_DEFINES}")
|
||||||
string(CONCAT PKG_CONFIG_DEFINES "-D" "${PKG_CONFIG_DEFINES}")
|
string(CONCAT PKG_CONFIG_DEFINES "-D" "${PKG_CONFIG_DEFINES}")
|
||||||
@@ -400,4 +401,4 @@ if (SPDLOG_INSTALL)
|
|||||||
# Support creation of installable packages
|
# Support creation of installable packages
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
include(cmake/spdlogCPack.cmake)
|
include(cmake/spdlogCPack.cmake)
|
||||||
endif ()
|
endif()
|
||||||
|
13
README.md
13
README.md
@@ -11,7 +11,7 @@ Fast C++ logging library
|
|||||||
|
|
||||||
## Install
|
## Install
|
||||||
#### Header-only version
|
#### Header-only version
|
||||||
Copy the include [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler.
|
Copy the include [folder](include/spdlog) to your build tree and use a C++11 compiler.
|
||||||
|
|
||||||
#### Compiled version (recommended - much faster compile times)
|
#### Compiled version (recommended - much faster compile times)
|
||||||
```console
|
```console
|
||||||
@@ -19,7 +19,7 @@ $ git clone https://github.com/gabime/spdlog.git
|
|||||||
$ cd spdlog && mkdir build && cd build
|
$ cd spdlog && mkdir build && cd build
|
||||||
$ cmake .. && cmake --build .
|
$ cmake .. && cmake --build .
|
||||||
```
|
```
|
||||||
see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/CMakeLists.txt) on how to use.
|
see example [CMakeLists.txt](example/CMakeLists.txt) on how to use.
|
||||||
|
|
||||||
## Platforms
|
## Platforms
|
||||||
* Linux, FreeBSD, OpenBSD, Solaris, AIX
|
* Linux, FreeBSD, OpenBSD, Solaris, AIX
|
||||||
@@ -48,7 +48,7 @@ see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/
|
|||||||
* Headers only or compiled
|
* Headers only or compiled
|
||||||
* Feature-rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library.
|
* Feature-rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library.
|
||||||
* Asynchronous mode (optional)
|
* Asynchronous mode (optional)
|
||||||
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
|
* [Custom](https://github.com/gabime/spdlog/wiki/Custom-formatting) formatting.
|
||||||
* Multi/Single threaded loggers.
|
* Multi/Single threaded loggers.
|
||||||
* Various log targets:
|
* Various log targets:
|
||||||
* Rotating log files.
|
* Rotating log files.
|
||||||
@@ -58,7 +58,7 @@ see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/
|
|||||||
* Windows event log.
|
* Windows event log.
|
||||||
* Windows debugger (```OutputDebugString(..)```).
|
* Windows debugger (```OutputDebugString(..)```).
|
||||||
* Log to Qt widgets ([example](#log-to-qt-with-nice-colors)).
|
* Log to Qt widgets ([example](#log-to-qt-with-nice-colors)).
|
||||||
* Easily [extendable](https://github.com/gabime/spdlog/wiki/4.-Sinks#implementing-your-own-sink) with custom log targets.
|
* Easily [extendable](https://github.com/gabime/spdlog/wiki/Sinks#implementing-your-own-sink) with custom log targets.
|
||||||
* Log filtering - log levels can be modified at runtime as well as compile time.
|
* Log filtering - log levels can be modified at runtime as well as compile time.
|
||||||
* Support for loading log levels from argv or environment var.
|
* Support for loading log levels from argv or environment var.
|
||||||
* [Backtrace](#backtrace-support) support - store debug messages in a ring buffer and display them later on demand.
|
* [Backtrace](#backtrace-support) support - store debug messages in a ring buffer and display them later on demand.
|
||||||
@@ -467,7 +467,7 @@ void mdc_example()
|
|||||||
---
|
---
|
||||||
## Benchmarks
|
## Benchmarks
|
||||||
|
|
||||||
Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
|
Below are some [benchmarks](bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
|
||||||
|
|
||||||
#### Synchronous mode
|
#### Synchronous mode
|
||||||
```
|
```
|
||||||
@@ -519,7 +519,8 @@ Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/ben
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
|
|
||||||
|
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki) pages.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
// details/file_helper-inl.h
|
// details/file_helper-inl.h
|
||||||
// details/os-inl.h
|
// details/os-inl.h
|
||||||
// fmt/bundled/core.h
|
|
||||||
// fmt/bundled/posix.h
|
// fmt/bundled/posix.h
|
||||||
// logger-inl.h
|
// logger-inl.h
|
||||||
// sinks/daily_file_sink.h
|
// sinks/daily_file_sink.h
|
||||||
@@ -23,7 +22,6 @@
|
|||||||
|
|
||||||
// details/os-inl.h
|
// details/os-inl.h
|
||||||
// details/pattern_formatter-inl.h
|
// details/pattern_formatter-inl.h
|
||||||
// fmt/bundled/core.h
|
|
||||||
// fmt/bundled/format-inl.h
|
// fmt/bundled/format-inl.h
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
@@ -73,7 +71,6 @@
|
|||||||
// details/registry.h
|
// details/registry.h
|
||||||
// details/tcp_client-windows.h
|
// details/tcp_client-windows.h
|
||||||
// details/tcp_client.h
|
// details/tcp_client.h
|
||||||
// fmt/bundled/core.h
|
|
||||||
// sinks/android_sink.h
|
// sinks/android_sink.h
|
||||||
// sinks/ansicolor_sink.h
|
// sinks/ansicolor_sink.h
|
||||||
// sinks/basic_file_sink.h
|
// sinks/basic_file_sink.h
|
||||||
@@ -150,7 +147,6 @@
|
|||||||
|
|
||||||
// common.h
|
// common.h
|
||||||
// details/fmt_helper.h
|
// details/fmt_helper.h
|
||||||
// fmt/bundled/core.h
|
|
||||||
// fmt/bundled/ranges.h
|
// fmt/bundled/ranges.h
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
@@ -269,7 +269,7 @@ void multi_sink_example() {
|
|||||||
struct my_type {
|
struct my_type {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
explicit my_type(int i)
|
explicit my_type(int i)
|
||||||
: i(i){}
|
: i(i) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef SPDLOG_USE_STD_FORMAT // when using fmtlib
|
#ifndef SPDLOG_USE_STD_FORMAT // when using fmtlib
|
||||||
@@ -382,14 +382,14 @@ void replace_default_logger_example() {
|
|||||||
spdlog::set_default_logger(old_logger);
|
spdlog::set_default_logger(old_logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mapped Diagnostic Context (MDC) is a map that stores key-value pairs (string values) in thread local storage.
|
// Mapped Diagnostic Context (MDC) is a map that stores key-value pairs (string values) in thread
|
||||||
// Each thread maintains its own MDC, which loggers use to append diagnostic information to log outputs.
|
// local storage. Each thread maintains its own MDC, which loggers use to append diagnostic
|
||||||
// Note: it is not supported in asynchronous mode due to its reliance on thread-local storage.
|
// information to log outputs. Note: it is not supported in asynchronous mode due to its reliance on
|
||||||
|
// thread-local storage.
|
||||||
|
|
||||||
#ifndef SPDLOG_NO_TLS
|
#ifndef SPDLOG_NO_TLS
|
||||||
#include "spdlog/mdc.h"
|
#include "spdlog/mdc.h"
|
||||||
void mdc_example()
|
void mdc_example() {
|
||||||
{
|
|
||||||
spdlog::mdc::put("key1", "value1");
|
spdlog::mdc::put("key1", "value1");
|
||||||
spdlog::mdc::put("key2", "value2");
|
spdlog::mdc::put("key2", "value2");
|
||||||
// if not using the default format, you can use the %& formatter to print mdc data as well
|
// if not using the default format, you can use the %& formatter to print mdc data as well
|
||||||
|
@@ -89,8 +89,7 @@ inline void init_thread_pool(size_t q_size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void init_thread_pool(size_t q_size, size_t thread_count) {
|
inline void init_thread_pool(size_t q_size, size_t thread_count) {
|
||||||
init_thread_pool(
|
init_thread_pool(q_size, thread_count, [] {}, [] {});
|
||||||
q_size, thread_count, [] {}, [] {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the global thread pool.
|
// get the global thread pool.
|
||||||
|
@@ -33,7 +33,7 @@ SPDLOG_INLINE spdlog::async_logger::async_logger(std::string logger_name,
|
|||||||
// send the log message to the thread pool
|
// send the log message to the thread pool
|
||||||
SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg){
|
SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg){
|
||||||
SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){
|
SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){
|
||||||
pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
|
pool_ptr -> post_log(shared_from_this(), msg, overflow_policy_);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw_spdlog_ex("async log: thread pool doesn't exist anymore");
|
throw_spdlog_ex("async log: thread pool doesn't exist anymore");
|
||||||
@@ -45,7 +45,7 @@ SPDLOG_LOGGER_CATCH(msg.source)
|
|||||||
// send flush request to the thread pool
|
// send flush request to the thread pool
|
||||||
SPDLOG_INLINE void spdlog::async_logger::flush_(){
|
SPDLOG_INLINE void spdlog::async_logger::flush_(){
|
||||||
SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){
|
SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){
|
||||||
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
|
pool_ptr -> post_flush(shared_from_this(), overflow_policy_);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
|
throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
|
||||||
|
@@ -483,13 +483,12 @@ SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// find the size to allocate for the result buffer
|
// find the size to allocate for the result buffer
|
||||||
int result_size =
|
int result_size = ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, NULL, 0);
|
||||||
::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, NULL, 0);
|
|
||||||
|
|
||||||
if (result_size > 0) {
|
if (result_size > 0) {
|
||||||
target.resize(result_size);
|
target.resize(result_size);
|
||||||
result_size = ::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, target.data(),
|
result_size =
|
||||||
result_size);
|
::MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, target.data(), result_size);
|
||||||
if (result_size > 0) {
|
if (result_size > 0) {
|
||||||
assert(result_size == target.size());
|
assert(result_size == target.size());
|
||||||
return;
|
return;
|
||||||
@@ -593,13 +592,13 @@ SPDLOG_INLINE bool fsync(FILE *fp) {
|
|||||||
// Do non-locking fwrite if possible by the os or use the regular locking fwrite
|
// Do non-locking fwrite if possible by the os or use the regular locking fwrite
|
||||||
// Return true on success.
|
// Return true on success.
|
||||||
SPDLOG_INLINE bool fwrite_bytes(const void *ptr, const size_t n_bytes, FILE *fp) {
|
SPDLOG_INLINE bool fwrite_bytes(const void *ptr, const size_t n_bytes, FILE *fp) {
|
||||||
#if defined(_WIN32) && defined(SPDLOG_FWRITE_UNLOCKED)
|
#if defined(_WIN32) && defined(SPDLOG_FWRITE_UNLOCKED)
|
||||||
return _fwrite_nolock(ptr, 1, n_bytes, fp) == n_bytes;
|
return _fwrite_nolock(ptr, 1, n_bytes, fp) == n_bytes;
|
||||||
#elif defined(SPDLOG_FWRITE_UNLOCKED)
|
#elif defined(SPDLOG_FWRITE_UNLOCKED)
|
||||||
return ::fwrite_unlocked(ptr, 1, n_bytes, fp) == n_bytes;
|
return ::fwrite_unlocked(ptr, 1, n_bytes, fp) == n_bytes;
|
||||||
#else
|
#else
|
||||||
return std::fwrite(ptr, 1, n_bytes, fp) == n_bytes;
|
return std::fwrite(ptr, 1, n_bytes, fp) == n_bytes;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace os
|
} // namespace os
|
||||||
|
@@ -54,6 +54,11 @@ SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger)
|
|||||||
register_logger_(std::move(new_logger));
|
register_logger_(std::move(new_logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::register_or_replace(std::shared_ptr<logger> new_logger) {
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
register_or_replace_(std::move(new_logger));
|
||||||
|
}
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger) {
|
SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger) {
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
new_logger->set_formatter(formatter_->clone());
|
new_logger->set_formatter(formatter_->clone());
|
||||||
@@ -252,10 +257,14 @@ SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger) {
|
SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger) {
|
||||||
auto logger_name = new_logger->name();
|
auto &logger_name = new_logger->name();
|
||||||
throw_if_exists_(logger_name);
|
throw_if_exists_(logger_name);
|
||||||
loggers_[logger_name] = std::move(new_logger);
|
loggers_[logger_name] = std::move(new_logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::register_or_replace_(std::shared_ptr<logger> new_logger) {
|
||||||
|
loggers_[new_logger->name()] = std::move(new_logger);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
@@ -31,6 +31,7 @@ public:
|
|||||||
registry &operator=(const registry &) = delete;
|
registry &operator=(const registry &) = delete;
|
||||||
|
|
||||||
void register_logger(std::shared_ptr<logger> new_logger);
|
void register_logger(std::shared_ptr<logger> new_logger);
|
||||||
|
void register_or_replace(std::shared_ptr<logger> new_logger);
|
||||||
void initialize_logger(std::shared_ptr<logger> new_logger);
|
void initialize_logger(std::shared_ptr<logger> new_logger);
|
||||||
std::shared_ptr<logger> get(const std::string &logger_name);
|
std::shared_ptr<logger> get(const std::string &logger_name);
|
||||||
std::shared_ptr<logger> default_logger();
|
std::shared_ptr<logger> default_logger();
|
||||||
@@ -105,6 +106,7 @@ private:
|
|||||||
|
|
||||||
void throw_if_exists_(const std::string &logger_name);
|
void throw_if_exists_(const std::string &logger_name);
|
||||||
void register_logger_(std::shared_ptr<logger> new_logger);
|
void register_logger_(std::shared_ptr<logger> new_logger);
|
||||||
|
void register_or_replace_(std::shared_ptr<logger> new_logger);
|
||||||
bool set_level_from_cfg_(logger *logger);
|
bool set_level_from_cfg_(logger *logger);
|
||||||
std::mutex logger_map_mutex_, flusher_mutex_;
|
std::mutex logger_map_mutex_, flusher_mutex_;
|
||||||
std::recursive_mutex tp_mutex_;
|
std::recursive_mutex tp_mutex_;
|
||||||
|
@@ -38,8 +38,7 @@ SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items,
|
|||||||
: thread_pool(q_max_items, threads_n, on_thread_start, [] {}) {}
|
: thread_pool(q_max_items, threads_n, on_thread_start, [] {}) {}
|
||||||
|
|
||||||
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
|
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
|
||||||
: thread_pool(
|
: thread_pool(q_max_items, threads_n, [] {}, [] {}) {}
|
||||||
q_max_items, threads_n, [] {}, [] {}) {}
|
|
||||||
|
|
||||||
// message all threads to terminate gracefully join them
|
// message all threads to terminate gracefully join them
|
||||||
SPDLOG_INLINE thread_pool::~thread_pool() {
|
SPDLOG_INLINE thread_pool::~thread_pool() {
|
||||||
|
@@ -142,8 +142,8 @@ struct formatter<spdlog::details::dump_info<T>, char> {
|
|||||||
|
|
||||||
// format the given bytes range as hex
|
// format the given bytes range as hex
|
||||||
template <typename FormatContext, typename Container>
|
template <typename FormatContext, typename Container>
|
||||||
auto format(const spdlog::details::dump_info<Container> &the_range, FormatContext &ctx) const
|
auto format(const spdlog::details::dump_info<Container> &the_range,
|
||||||
-> decltype(ctx.out()) {
|
FormatContext &ctx) const -> decltype(ctx.out()) {
|
||||||
SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
|
SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
|
||||||
SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
|
SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
|
||||||
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
|
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
|
||||||
|
@@ -21,7 +21,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
||||||
#define FMT_VERSION 110104
|
#define FMT_VERSION 110200
|
||||||
|
|
||||||
// Detect compiler versions.
|
// Detect compiler versions.
|
||||||
#if defined(__clang__) && !defined(__ibmxl__)
|
#if defined(__clang__) && !defined(__ibmxl__)
|
||||||
@@ -209,20 +209,6 @@
|
|||||||
# define FMT_DEPRECATED /* deprecated */
|
# define FMT_DEPRECATED /* deprecated */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FMT_ALWAYS_INLINE
|
|
||||||
// Use the provided definition.
|
|
||||||
#elif FMT_GCC_VERSION || FMT_CLANG_VERSION
|
|
||||||
# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
|
|
||||||
#else
|
|
||||||
# define FMT_ALWAYS_INLINE inline
|
|
||||||
#endif
|
|
||||||
// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
|
|
||||||
#ifdef NDEBUG
|
|
||||||
# define FMT_INLINE FMT_ALWAYS_INLINE
|
|
||||||
#else
|
|
||||||
# define FMT_INLINE inline
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
|
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
|
||||||
# define FMT_VISIBILITY(value) __attribute__((visibility(value)))
|
# define FMT_VISIBILITY(value) __attribute__((visibility(value)))
|
||||||
#else
|
#else
|
||||||
@@ -249,6 +235,28 @@
|
|||||||
# define FMT_MSC_WARNING(...)
|
# define FMT_MSC_WARNING(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Enable minimal optimizations for more compact code in debug mode.
|
||||||
|
FMT_PRAGMA_GCC(push_options)
|
||||||
|
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
|
||||||
|
FMT_PRAGMA_GCC(optimize("Og"))
|
||||||
|
# define FMT_GCC_OPTIMIZED
|
||||||
|
#endif
|
||||||
|
FMT_PRAGMA_CLANG(diagnostic push)
|
||||||
|
|
||||||
|
#ifdef FMT_ALWAYS_INLINE
|
||||||
|
// Use the provided definition.
|
||||||
|
#elif FMT_GCC_VERSION || FMT_CLANG_VERSION
|
||||||
|
# define FMT_ALWAYS_INLINE inline __attribute__((always_inline))
|
||||||
|
#else
|
||||||
|
# define FMT_ALWAYS_INLINE inline
|
||||||
|
#endif
|
||||||
|
// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
|
||||||
|
#if defined(NDEBUG) || defined(FMT_GCC_OPTIMIZED)
|
||||||
|
# define FMT_INLINE FMT_ALWAYS_INLINE
|
||||||
|
#else
|
||||||
|
# define FMT_INLINE inline
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_BEGIN_NAMESPACE
|
#ifndef FMT_BEGIN_NAMESPACE
|
||||||
# define FMT_BEGIN_NAMESPACE \
|
# define FMT_BEGIN_NAMESPACE \
|
||||||
namespace fmt { \
|
namespace fmt { \
|
||||||
@@ -297,13 +305,6 @@
|
|||||||
using unused = int[]; \
|
using unused = int[]; \
|
||||||
(void)unused { 0, (expr, 0)... }
|
(void)unused { 0, (expr, 0)... }
|
||||||
|
|
||||||
// Enable minimal optimizations for more compact code in debug mode.
|
|
||||||
FMT_PRAGMA_GCC(push_options)
|
|
||||||
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
|
|
||||||
FMT_PRAGMA_GCC(optimize("Og"))
|
|
||||||
#endif
|
|
||||||
FMT_PRAGMA_CLANG(diagnostic push)
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
// Implementations of enable_if_t and other metafunctions for older systems.
|
// Implementations of enable_if_t and other metafunctions for older systems.
|
||||||
@@ -325,8 +326,8 @@ using underlying_t = typename std::underlying_type<T>::type;
|
|||||||
template <typename T> using decay_t = typename std::decay<T>::type;
|
template <typename T> using decay_t = typename std::decay<T>::type;
|
||||||
using nullptr_t = decltype(nullptr);
|
using nullptr_t = decltype(nullptr);
|
||||||
|
|
||||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
|
#if (FMT_GCC_VERSION && FMT_GCC_VERSION < 500) || FMT_MSC_VERSION
|
||||||
// A workaround for gcc 4.9 to make void_t work in a SFINAE context.
|
// A workaround for gcc 4.9 & MSVC v141 to make void_t work in a SFINAE context.
|
||||||
template <typename...> struct void_t_impl {
|
template <typename...> struct void_t_impl {
|
||||||
using type = void;
|
using type = void;
|
||||||
};
|
};
|
||||||
@@ -526,20 +527,20 @@ template <typename Char> class basic_string_view {
|
|||||||
|
|
||||||
constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
|
constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {}
|
||||||
|
|
||||||
/// Constructs a string reference object from a C string and a size.
|
/// Constructs a string view object from a C string and a size.
|
||||||
constexpr basic_string_view(const Char* s, size_t count) noexcept
|
constexpr basic_string_view(const Char* s, size_t count) noexcept
|
||||||
: data_(s), size_(count) {}
|
: data_(s), size_(count) {}
|
||||||
|
|
||||||
constexpr basic_string_view(nullptr_t) = delete;
|
constexpr basic_string_view(nullptr_t) = delete;
|
||||||
|
|
||||||
/// Constructs a string reference object from a C string.
|
/// Constructs a string view object from a C string.
|
||||||
#if FMT_GCC_VERSION
|
#if FMT_GCC_VERSION
|
||||||
FMT_ALWAYS_INLINE
|
FMT_ALWAYS_INLINE
|
||||||
#endif
|
#endif
|
||||||
FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) {
|
FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) {
|
||||||
#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
|
#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
|
||||||
if (std::is_same<Char, char>::value) {
|
if (std::is_same<Char, char>::value && !detail::is_constant_evaluated()) {
|
||||||
size_ = __builtin_strlen(detail::narrow(s));
|
size_ = __builtin_strlen(detail::narrow(s)); // strlen is not costexpr.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -548,7 +549,7 @@ template <typename Char> class basic_string_view {
|
|||||||
size_ = len;
|
size_ = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructs a string reference from a `std::basic_string` or a
|
/// Constructs a string view from a `std::basic_string` or a
|
||||||
/// `std::basic_string_view` object.
|
/// `std::basic_string_view` object.
|
||||||
template <typename S,
|
template <typename S,
|
||||||
FMT_ENABLE_IF(detail::is_std_string_like<S>::value&& std::is_same<
|
FMT_ENABLE_IF(detail::is_std_string_like<S>::value&& std::is_same<
|
||||||
@@ -585,7 +586,6 @@ template <typename Char> class basic_string_view {
|
|||||||
return starts_with(basic_string_view<Char>(s));
|
return starts_with(basic_string_view<Char>(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lexicographically compare this string reference to other.
|
|
||||||
FMT_CONSTEXPR auto compare(basic_string_view other) const -> int {
|
FMT_CONSTEXPR auto compare(basic_string_view other) const -> int {
|
||||||
int result =
|
int result =
|
||||||
detail::compare(data_, other.data_, min_of(size_, other.size_));
|
detail::compare(data_, other.data_, min_of(size_, other.size_));
|
||||||
@@ -616,7 +616,7 @@ template <typename Char> class basic_string_view {
|
|||||||
|
|
||||||
using string_view = basic_string_view<char>;
|
using string_view = basic_string_view<char>;
|
||||||
|
|
||||||
/// Specifies if `T` is an extended character type. Can be specialized by users.
|
// DEPRECATED! Will be merged with is_char and moved to detail.
|
||||||
template <typename T> struct is_xchar : std::false_type {};
|
template <typename T> struct is_xchar : std::false_type {};
|
||||||
template <> struct is_xchar<wchar_t> : std::true_type {};
|
template <> struct is_xchar<wchar_t> : std::true_type {};
|
||||||
template <> struct is_xchar<char16_t> : std::true_type {};
|
template <> struct is_xchar<char16_t> : std::true_type {};
|
||||||
@@ -625,7 +625,7 @@ template <> struct is_xchar<char32_t> : std::true_type {};
|
|||||||
template <> struct is_xchar<char8_t> : std::true_type {};
|
template <> struct is_xchar<char8_t> : std::true_type {};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// DEPRECATED! Will be replaced with an alias to prevent specializations.
|
// Specifies if `T` is a character (code unit) type.
|
||||||
template <typename T> struct is_char : is_xchar<T> {};
|
template <typename T> struct is_char : is_xchar<T> {};
|
||||||
template <> struct is_char<char> : std::true_type {};
|
template <> struct is_char<char> : std::true_type {};
|
||||||
|
|
||||||
@@ -1032,6 +1032,11 @@ enum {
|
|||||||
|
|
||||||
struct view {};
|
struct view {};
|
||||||
|
|
||||||
|
template <typename T, typename Enable = std::true_type>
|
||||||
|
struct is_view : std::false_type {};
|
||||||
|
template <typename T>
|
||||||
|
struct is_view<T, bool_constant<sizeof(T) != 0>> : std::is_base_of<view, T> {};
|
||||||
|
|
||||||
template <typename Char, typename T> struct named_arg;
|
template <typename Char, typename T> struct named_arg;
|
||||||
template <typename T> struct is_named_arg : std::false_type {};
|
template <typename T> struct is_named_arg : std::false_type {};
|
||||||
template <typename T> struct is_static_named_arg : std::false_type {};
|
template <typename T> struct is_static_named_arg : std::false_type {};
|
||||||
@@ -1064,6 +1069,16 @@ template <typename Char> struct named_arg_info {
|
|||||||
int id;
|
int id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// named_args is non-const to suppress a bogus -Wmaybe-uninitalized in gcc 13.
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR void check_for_duplicate(named_arg_info<Char>* named_args,
|
||||||
|
int named_arg_index,
|
||||||
|
basic_string_view<Char> arg_name) {
|
||||||
|
for (int i = 0; i < named_arg_index; ++i) {
|
||||||
|
if (named_args[i].name == arg_name) report_error("duplicate named arg");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char, typename T, FMT_ENABLE_IF(!is_named_arg<T>::value)>
|
template <typename Char, typename T, FMT_ENABLE_IF(!is_named_arg<T>::value)>
|
||||||
void init_named_arg(named_arg_info<Char>*, int& arg_index, int&, const T&) {
|
void init_named_arg(named_arg_info<Char>*, int& arg_index, int&, const T&) {
|
||||||
++arg_index;
|
++arg_index;
|
||||||
@@ -1071,6 +1086,7 @@ void init_named_arg(named_arg_info<Char>*, int& arg_index, int&, const T&) {
|
|||||||
template <typename Char, typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
|
template <typename Char, typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
|
||||||
void init_named_arg(named_arg_info<Char>* named_args, int& arg_index,
|
void init_named_arg(named_arg_info<Char>* named_args, int& arg_index,
|
||||||
int& named_arg_index, const T& arg) {
|
int& named_arg_index, const T& arg) {
|
||||||
|
check_for_duplicate<Char>(named_args, named_arg_index, arg.name);
|
||||||
named_args[named_arg_index++] = {arg.name, arg_index++};
|
named_args[named_arg_index++] = {arg.name, arg_index++};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1084,12 +1100,13 @@ template <typename T, typename Char,
|
|||||||
FMT_ENABLE_IF(is_static_named_arg<T>::value)>
|
FMT_ENABLE_IF(is_static_named_arg<T>::value)>
|
||||||
FMT_CONSTEXPR void init_static_named_arg(named_arg_info<Char>* named_args,
|
FMT_CONSTEXPR void init_static_named_arg(named_arg_info<Char>* named_args,
|
||||||
int& arg_index, int& named_arg_index) {
|
int& arg_index, int& named_arg_index) {
|
||||||
|
check_for_duplicate<Char>(named_args, named_arg_index, T::name);
|
||||||
named_args[named_arg_index++] = {T::name, arg_index++};
|
named_args[named_arg_index++] = {T::name, arg_index++};
|
||||||
}
|
}
|
||||||
|
|
||||||
// To minimize the number of types we need to deal with, long is translated
|
// To minimize the number of types we need to deal with, long is translated
|
||||||
// either to int or to long long depending on its size.
|
// either to int or to long long depending on its size.
|
||||||
enum { long_short = sizeof(long) == sizeof(int) };
|
enum { long_short = sizeof(long) == sizeof(int) && FMT_BUILTIN_TYPES };
|
||||||
using long_type = conditional_t<long_short, int, long long>;
|
using long_type = conditional_t<long_short, int, long long>;
|
||||||
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
|
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
|
||||||
|
|
||||||
@@ -1706,7 +1723,17 @@ class format_string_checker {
|
|||||||
-> const Char* {
|
-> const Char* {
|
||||||
context_.advance_to(begin);
|
context_.advance_to(begin);
|
||||||
if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_);
|
if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_);
|
||||||
while (begin != end && *begin != '}') ++begin;
|
|
||||||
|
// If id is out of range, it means we do not know the type and cannot parse
|
||||||
|
// the format at compile time. Instead, skip over content until we finish
|
||||||
|
// the format spec, accounting for any nested replacements.
|
||||||
|
for (int bracket_count = 0;
|
||||||
|
begin != end && (bracket_count > 0 || *begin != '}'); ++begin) {
|
||||||
|
if (*begin == '{')
|
||||||
|
++bracket_count;
|
||||||
|
else if (*begin == '}')
|
||||||
|
--bracket_count;
|
||||||
|
}
|
||||||
return begin;
|
return begin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2703,7 +2730,7 @@ template <typename... T> struct fstring {
|
|||||||
template <size_t N>
|
template <size_t N>
|
||||||
FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) {
|
FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) {
|
||||||
using namespace detail;
|
using namespace detail;
|
||||||
static_assert(count<(std::is_base_of<view, remove_reference_t<T>>::value &&
|
static_assert(count<(is_view<remove_cvref_t<T>>::value &&
|
||||||
std::is_reference<T>::value)...>() == 0,
|
std::is_reference<T>::value)...>() == 0,
|
||||||
"passing views as lvalues is disallowed");
|
"passing views as lvalues is disallowed");
|
||||||
if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack()));
|
if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack()));
|
||||||
|
@@ -22,21 +22,6 @@
|
|||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
namespace fmt_detail {
|
|
||||||
struct time_zone {
|
|
||||||
template <typename Duration, typename T>
|
|
||||||
auto to_sys(T)
|
|
||||||
-> std::chrono::time_point<std::chrono::system_clock, Duration> {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <typename... T> inline auto current_zone(T...) -> time_zone* {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... T> inline void _tzset(T...) {}
|
|
||||||
} // namespace fmt_detail
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
// Enable safe chrono durations, unless explicitly disabled.
|
// Enable safe chrono durations, unless explicitly disabled.
|
||||||
@@ -435,14 +420,11 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc,
|
|||||||
return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
|
return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Rep1, typename Rep2>
|
template <typename T, typename U>
|
||||||
struct is_same_arithmetic_type
|
using is_similar_arithmetic_type =
|
||||||
: public std::integral_constant<bool,
|
bool_constant<(std::is_integral<T>::value && std::is_integral<U>::value) ||
|
||||||
(std::is_integral<Rep1>::value &&
|
(std::is_floating_point<T>::value &&
|
||||||
std::is_integral<Rep2>::value) ||
|
std::is_floating_point<U>::value)>;
|
||||||
(std::is_floating_point<Rep1>::value &&
|
|
||||||
std::is_floating_point<Rep2>::value)> {
|
|
||||||
};
|
|
||||||
|
|
||||||
FMT_NORETURN inline void throw_duration_error() {
|
FMT_NORETURN inline void throw_duration_error() {
|
||||||
FMT_THROW(format_error("cannot format duration"));
|
FMT_THROW(format_error("cannot format duration"));
|
||||||
@@ -501,9 +483,9 @@ auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
template <typename To, typename FromRep, typename FromPeriod,
|
||||||
typename To, typename FromRep, typename FromPeriod,
|
FMT_ENABLE_IF(
|
||||||
FMT_ENABLE_IF(!is_same_arithmetic_type<FromRep, typename To::rep>::value)>
|
!is_similar_arithmetic_type<FromRep, typename To::rep>::value)>
|
||||||
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
|
auto duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
|
||||||
// Mixed integer <-> float cast is not supported by safe_duration_cast.
|
// Mixed integer <-> float cast is not supported by safe_duration_cast.
|
||||||
return std::chrono::duration_cast<To>(from);
|
return std::chrono::duration_cast<To>(from);
|
||||||
@@ -519,12 +501,30 @@ auto to_time_t(sys_time<Duration> time_point) -> std::time_t {
|
|||||||
.count();
|
.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Workaround a bug in libstdc++ which sets __cpp_lib_chrono to 201907 without
|
namespace tz {
|
||||||
// providing current_zone(): https://github.com/fmtlib/fmt/issues/4160.
|
|
||||||
template <typename T> FMT_CONSTEXPR auto has_current_zone() -> bool {
|
// DEPRECATED!
|
||||||
using namespace std::chrono;
|
struct time_zone {
|
||||||
using namespace fmt_detail;
|
template <typename Duration, typename LocalTime>
|
||||||
return !std::is_same<decltype(current_zone()), fmt_detail::time_zone*>::value;
|
auto to_sys(LocalTime) -> sys_time<Duration> {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <typename... T> auto current_zone(T...) -> time_zone* {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T> void _tzset(T...) {}
|
||||||
|
} // namespace tz
|
||||||
|
|
||||||
|
// DEPRECATED!
|
||||||
|
inline void tzset_once() {
|
||||||
|
static bool init = []() {
|
||||||
|
using namespace tz;
|
||||||
|
_tzset();
|
||||||
|
return false;
|
||||||
|
}();
|
||||||
|
ignore_unused(init);
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
@@ -535,7 +535,7 @@ FMT_BEGIN_EXPORT
|
|||||||
* expressed in local time. Unlike `std::localtime`, this function is
|
* expressed in local time. Unlike `std::localtime`, this function is
|
||||||
* thread-safe on most platforms.
|
* thread-safe on most platforms.
|
||||||
*/
|
*/
|
||||||
inline auto localtime(std::time_t time) -> std::tm {
|
FMT_DEPRECATED inline auto localtime(std::time_t time) -> std::tm {
|
||||||
struct dispatcher {
|
struct dispatcher {
|
||||||
std::time_t time_;
|
std::time_t time_;
|
||||||
std::tm tm_;
|
std::tm tm_;
|
||||||
@@ -572,11 +572,11 @@ inline auto localtime(std::time_t time) -> std::tm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if FMT_USE_LOCAL_TIME
|
#if FMT_USE_LOCAL_TIME
|
||||||
template <typename Duration,
|
template <typename Duration>
|
||||||
FMT_ENABLE_IF(detail::has_current_zone<Duration>())>
|
FMT_DEPRECATED auto localtime(std::chrono::local_time<Duration> time)
|
||||||
inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
|
-> std::tm {
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
using namespace fmt_detail;
|
using namespace detail::tz;
|
||||||
return localtime(detail::to_time_t(current_zone()->to_sys<Duration>(time)));
|
return localtime(detail::to_time_t(current_zone()->to_sys<Duration>(time)));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -911,7 +911,14 @@ template <typename Derived> struct null_chrono_spec_handler {
|
|||||||
FMT_CONSTEXPR void on_tz_name() { unsupported(); }
|
FMT_CONSTEXPR void on_tz_name() { unsupported(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
|
class tm_format_checker : public null_chrono_spec_handler<tm_format_checker> {
|
||||||
|
private:
|
||||||
|
bool has_timezone_ = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr explicit tm_format_checker(bool has_timezone)
|
||||||
|
: has_timezone_(has_timezone) {}
|
||||||
|
|
||||||
FMT_NORETURN inline void unsupported() {
|
FMT_NORETURN inline void unsupported() {
|
||||||
FMT_THROW(format_error("no format"));
|
FMT_THROW(format_error("no format"));
|
||||||
}
|
}
|
||||||
@@ -949,8 +956,12 @@ struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
|
|||||||
FMT_CONSTEXPR void on_24_hour_time() {}
|
FMT_CONSTEXPR void on_24_hour_time() {}
|
||||||
FMT_CONSTEXPR void on_iso_time() {}
|
FMT_CONSTEXPR void on_iso_time() {}
|
||||||
FMT_CONSTEXPR void on_am_pm() {}
|
FMT_CONSTEXPR void on_am_pm() {}
|
||||||
FMT_CONSTEXPR void on_utc_offset(numeric_system) {}
|
FMT_CONSTEXPR void on_utc_offset(numeric_system) {
|
||||||
FMT_CONSTEXPR void on_tz_name() {}
|
if (!has_timezone_) FMT_THROW(format_error("no timezone"));
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR void on_tz_name() {
|
||||||
|
if (!has_timezone_) FMT_THROW(format_error("no timezone"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline auto tm_wday_full_name(int wday) -> const char* {
|
inline auto tm_wday_full_name(int wday) -> const char* {
|
||||||
@@ -980,24 +991,27 @@ inline auto tm_mon_short_name(int mon) -> const char* {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename = void>
|
template <typename T, typename = void>
|
||||||
struct has_member_data_tm_gmtoff : std::false_type {};
|
struct has_tm_gmtoff : std::false_type {};
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct has_member_data_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>>
|
struct has_tm_gmtoff<T, void_t<decltype(T::tm_gmtoff)>> : std::true_type {};
|
||||||
: std::true_type {};
|
|
||||||
|
|
||||||
template <typename T, typename = void>
|
template <typename T, typename = void> struct has_tm_zone : std::false_type {};
|
||||||
struct has_member_data_tm_zone : std::false_type {};
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct has_member_data_tm_zone<T, void_t<decltype(T::tm_zone)>>
|
struct has_tm_zone<T, void_t<decltype(T::tm_zone)>> : std::true_type {};
|
||||||
: std::true_type {};
|
|
||||||
|
|
||||||
inline void tzset_once() {
|
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)>
|
||||||
static bool init = []() {
|
bool set_tm_zone(T& time, char* tz) {
|
||||||
using namespace fmt_detail;
|
time.tm_zone = tz;
|
||||||
_tzset();
|
return true;
|
||||||
|
}
|
||||||
|
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)>
|
||||||
|
bool set_tm_zone(T&, char*) {
|
||||||
return false;
|
return false;
|
||||||
}();
|
}
|
||||||
ignore_unused(init);
|
|
||||||
|
inline char* utc() {
|
||||||
|
static char tz[] = "UTC";
|
||||||
|
return tz;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts value to Int and checks that it's in the range [0, upper).
|
// Converts value to Int and checks that it's in the range [0, upper).
|
||||||
@@ -1005,7 +1019,7 @@ template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
|||||||
inline auto to_nonnegative_int(T value, Int upper) -> Int {
|
inline auto to_nonnegative_int(T value, Int upper) -> Int {
|
||||||
if (!std::is_unsigned<Int>::value &&
|
if (!std::is_unsigned<Int>::value &&
|
||||||
(value < 0 || to_unsigned(value) > to_unsigned(upper))) {
|
(value < 0 || to_unsigned(value) > to_unsigned(upper))) {
|
||||||
FMT_THROW(fmt::format_error("chrono value is out of range"));
|
FMT_THROW(format_error("chrono value is out of range"));
|
||||||
}
|
}
|
||||||
return static_cast<Int>(value);
|
return static_cast<Int>(value);
|
||||||
}
|
}
|
||||||
@@ -1090,7 +1104,7 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
|
|||||||
|
|
||||||
// Format subseconds which are given as a floating point type with an
|
// Format subseconds which are given as a floating point type with an
|
||||||
// appropriate number of digits. We cannot pass the Duration here, as we
|
// appropriate number of digits. We cannot pass the Duration here, as we
|
||||||
// explicitly need to pass the Rep value in the chrono_formatter.
|
// explicitly need to pass the Rep value in the duration_formatter.
|
||||||
template <typename Duration>
|
template <typename Duration>
|
||||||
void write_floating_seconds(memory_buffer& buf, Duration duration,
|
void write_floating_seconds(memory_buffer& buf, Duration duration,
|
||||||
int num_fractional_digits = -1) {
|
int num_fractional_digits = -1) {
|
||||||
@@ -1124,7 +1138,7 @@ class tm_writer {
|
|||||||
static constexpr int days_per_week = 7;
|
static constexpr int days_per_week = 7;
|
||||||
|
|
||||||
const std::locale& loc_;
|
const std::locale& loc_;
|
||||||
const bool is_classic_;
|
bool is_classic_;
|
||||||
OutputIt out_;
|
OutputIt out_;
|
||||||
const Duration* subsecs_;
|
const Duration* subsecs_;
|
||||||
const std::tm& tm_;
|
const std::tm& tm_;
|
||||||
@@ -1160,8 +1174,8 @@ class tm_writer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto tm_hour12() const noexcept -> int {
|
auto tm_hour12() const noexcept -> int {
|
||||||
const auto h = tm_hour();
|
auto h = tm_hour();
|
||||||
const auto z = h < 12 ? h : h - 12;
|
auto z = h < 12 ? h : h - 12;
|
||||||
return z == 0 ? 12 : z;
|
return z == 0 ? 12 : z;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1177,11 +1191,11 @@ class tm_writer {
|
|||||||
|
|
||||||
// Algorithm: https://en.wikipedia.org/wiki/ISO_week_date.
|
// Algorithm: https://en.wikipedia.org/wiki/ISO_week_date.
|
||||||
auto iso_year_weeks(long long curr_year) const noexcept -> int {
|
auto iso_year_weeks(long long curr_year) const noexcept -> int {
|
||||||
const auto prev_year = curr_year - 1;
|
auto prev_year = curr_year - 1;
|
||||||
const auto curr_p =
|
auto curr_p =
|
||||||
(curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
|
(curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) %
|
||||||
days_per_week;
|
days_per_week;
|
||||||
const auto prev_p =
|
auto prev_p =
|
||||||
(prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
|
(prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) %
|
||||||
days_per_week;
|
days_per_week;
|
||||||
return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
|
return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0);
|
||||||
@@ -1191,15 +1205,15 @@ class tm_writer {
|
|||||||
days_per_week;
|
days_per_week;
|
||||||
}
|
}
|
||||||
auto tm_iso_week_year() const noexcept -> long long {
|
auto tm_iso_week_year() const noexcept -> long long {
|
||||||
const auto year = tm_year();
|
auto year = tm_year();
|
||||||
const auto w = iso_week_num(tm_yday(), tm_wday());
|
auto w = iso_week_num(tm_yday(), tm_wday());
|
||||||
if (w < 1) return year - 1;
|
if (w < 1) return year - 1;
|
||||||
if (w > iso_year_weeks(year)) return year + 1;
|
if (w > iso_year_weeks(year)) return year + 1;
|
||||||
return year;
|
return year;
|
||||||
}
|
}
|
||||||
auto tm_iso_week_of_year() const noexcept -> int {
|
auto tm_iso_week_of_year() const noexcept -> int {
|
||||||
const auto year = tm_year();
|
auto year = tm_year();
|
||||||
const auto w = iso_week_num(tm_yday(), tm_wday());
|
auto w = iso_week_num(tm_yday(), tm_wday());
|
||||||
if (w < 1) return iso_year_weeks(year - 1);
|
if (w < 1) return iso_year_weeks(year - 1);
|
||||||
if (w > iso_year_weeks(year)) return 1;
|
if (w > iso_year_weeks(year)) return 1;
|
||||||
return w;
|
return w;
|
||||||
@@ -1236,9 +1250,8 @@ class tm_writer {
|
|||||||
uint32_or_64_or_128_t<long long> n = to_unsigned(year);
|
uint32_or_64_or_128_t<long long> n = to_unsigned(year);
|
||||||
const int num_digits = count_digits(n);
|
const int num_digits = count_digits(n);
|
||||||
if (negative && pad == pad_type::zero) *out_++ = '-';
|
if (negative && pad == pad_type::zero) *out_++ = '-';
|
||||||
if (width > num_digits) {
|
if (width > num_digits)
|
||||||
out_ = detail::write_padding(out_, pad, width - num_digits);
|
out_ = detail::write_padding(out_, pad, width - num_digits);
|
||||||
}
|
|
||||||
if (negative && pad != pad_type::zero) *out_++ = '-';
|
if (negative && pad != pad_type::zero) *out_++ = '-';
|
||||||
out_ = format_decimal<Char>(out_, n, num_digits);
|
out_ = format_decimal<Char>(out_, n, num_digits);
|
||||||
}
|
}
|
||||||
@@ -1259,45 +1272,22 @@ class tm_writer {
|
|||||||
write2(static_cast<int>(offset % 60));
|
write2(static_cast<int>(offset % 60));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(has_member_data_tm_gmtoff<T>::value)>
|
template <typename T, FMT_ENABLE_IF(has_tm_gmtoff<T>::value)>
|
||||||
void format_utc_offset_impl(const T& tm, numeric_system ns) {
|
void format_utc_offset(const T& tm, numeric_system ns) {
|
||||||
write_utc_offset(tm.tm_gmtoff, ns);
|
write_utc_offset(tm.tm_gmtoff, ns);
|
||||||
}
|
}
|
||||||
template <typename T, FMT_ENABLE_IF(!has_member_data_tm_gmtoff<T>::value)>
|
template <typename T, FMT_ENABLE_IF(!has_tm_gmtoff<T>::value)>
|
||||||
void format_utc_offset_impl(const T& tm, numeric_system ns) {
|
void format_utc_offset(const T&, numeric_system ns) {
|
||||||
#if defined(_WIN32) && defined(_UCRT)
|
write_utc_offset(0, ns);
|
||||||
tzset_once();
|
|
||||||
long offset = 0;
|
|
||||||
_get_timezone(&offset);
|
|
||||||
if (tm.tm_isdst) {
|
|
||||||
long dstbias = 0;
|
|
||||||
_get_dstbias(&dstbias);
|
|
||||||
offset += dstbias;
|
|
||||||
}
|
|
||||||
write_utc_offset(-offset, ns);
|
|
||||||
#else
|
|
||||||
if (ns == numeric_system::standard) return format_localized('z');
|
|
||||||
|
|
||||||
// Extract timezone offset from timezone conversion functions.
|
|
||||||
std::tm gtm = tm;
|
|
||||||
std::time_t gt = std::mktime(>m);
|
|
||||||
std::tm ltm = gmtime(gt);
|
|
||||||
std::time_t lt = std::mktime(<m);
|
|
||||||
long long offset = gt - lt;
|
|
||||||
write_utc_offset(offset, ns);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(has_member_data_tm_zone<T>::value)>
|
template <typename T, FMT_ENABLE_IF(has_tm_zone<T>::value)>
|
||||||
void format_tz_name_impl(const T& tm) {
|
void format_tz_name(const T& tm) {
|
||||||
if (is_classic_)
|
|
||||||
out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
|
out_ = write_tm_str<Char>(out_, tm.tm_zone, loc_);
|
||||||
else
|
|
||||||
format_localized('Z');
|
|
||||||
}
|
}
|
||||||
template <typename T, FMT_ENABLE_IF(!has_member_data_tm_zone<T>::value)>
|
template <typename T, FMT_ENABLE_IF(!has_tm_zone<T>::value)>
|
||||||
void format_tz_name_impl(const T&) {
|
void format_tz_name(const T&) {
|
||||||
format_localized('Z');
|
out_ = std::copy_n(utc(), 3, out_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void format_localized(char format, char modifier = 0) {
|
void format_localized(char format, char modifier = 0) {
|
||||||
@@ -1408,8 +1398,8 @@ class tm_writer {
|
|||||||
out_ = copy<Char>(std::begin(buf) + offset, std::end(buf), out_);
|
out_ = copy<Char>(std::begin(buf) + offset, std::end(buf), out_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); }
|
void on_utc_offset(numeric_system ns) { format_utc_offset(tm_, ns); }
|
||||||
void on_tz_name() { format_tz_name_impl(tm_); }
|
void on_tz_name() { format_tz_name(tm_); }
|
||||||
|
|
||||||
void on_year(numeric_system ns, pad_type pad) {
|
void on_year(numeric_system ns, pad_type pad) {
|
||||||
if (is_classic_ || ns == numeric_system::standard)
|
if (is_classic_ || ns == numeric_system::standard)
|
||||||
@@ -1483,11 +1473,10 @@ class tm_writer {
|
|||||||
void on_day_of_year(pad_type pad) {
|
void on_day_of_year(pad_type pad) {
|
||||||
auto yday = tm_yday() + 1;
|
auto yday = tm_yday() + 1;
|
||||||
auto digit1 = yday / 100;
|
auto digit1 = yday / 100;
|
||||||
if (digit1 != 0) {
|
if (digit1 != 0)
|
||||||
write1(digit1);
|
write1(digit1);
|
||||||
} else {
|
else
|
||||||
out_ = detail::write_padding(out_, pad);
|
out_ = detail::write_padding(out_, pad);
|
||||||
}
|
|
||||||
write2(yday % 100, pad);
|
write2(yday % 100, pad);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1624,18 +1613,16 @@ template <typename Rep, typename Period,
|
|||||||
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
|
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
|
||||||
inline auto get_milliseconds(std::chrono::duration<Rep, Period> d)
|
inline auto get_milliseconds(std::chrono::duration<Rep, Period> d)
|
||||||
-> std::chrono::duration<Rep, std::milli> {
|
-> std::chrono::duration<Rep, std::milli> {
|
||||||
// this may overflow and/or the result may not fit in the
|
// This may overflow and/or the result may not fit in the target type.
|
||||||
// target type.
|
|
||||||
#if FMT_SAFE_DURATION_CAST
|
#if FMT_SAFE_DURATION_CAST
|
||||||
using CommonSecondsType =
|
using common_seconds_type =
|
||||||
typename std::common_type<decltype(d), std::chrono::seconds>::type;
|
typename std::common_type<decltype(d), std::chrono::seconds>::type;
|
||||||
const auto d_as_common = detail::duration_cast<CommonSecondsType>(d);
|
auto d_as_common = detail::duration_cast<common_seconds_type>(d);
|
||||||
const auto d_as_whole_seconds =
|
auto d_as_whole_seconds =
|
||||||
detail::duration_cast<std::chrono::seconds>(d_as_common);
|
detail::duration_cast<std::chrono::seconds>(d_as_common);
|
||||||
// this conversion should be nonproblematic
|
// This conversion should be nonproblematic.
|
||||||
const auto diff = d_as_common - d_as_whole_seconds;
|
auto diff = d_as_common - d_as_whole_seconds;
|
||||||
const auto ms =
|
auto ms = detail::duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
|
||||||
detail::duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
|
|
||||||
return ms;
|
return ms;
|
||||||
#else
|
#else
|
||||||
auto s = detail::duration_cast<std::chrono::seconds>(d);
|
auto s = detail::duration_cast<std::chrono::seconds>(d);
|
||||||
@@ -1707,32 +1694,28 @@ class get_locale {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename FormatContext, typename OutputIt, typename Rep,
|
template <typename Char, typename Rep, typename Period>
|
||||||
typename Period>
|
struct duration_formatter {
|
||||||
struct chrono_formatter {
|
using iterator = basic_appender<Char>;
|
||||||
FormatContext& context;
|
iterator out;
|
||||||
OutputIt out;
|
|
||||||
int precision;
|
|
||||||
bool localized = false;
|
|
||||||
// rep is unsigned to avoid overflow.
|
// rep is unsigned to avoid overflow.
|
||||||
using rep =
|
using rep =
|
||||||
conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
|
conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
|
||||||
unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
|
unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
|
||||||
rep val;
|
rep val;
|
||||||
|
int precision;
|
||||||
|
locale_ref locale;
|
||||||
|
bool localized = false;
|
||||||
using seconds = std::chrono::duration<rep>;
|
using seconds = std::chrono::duration<rep>;
|
||||||
seconds s;
|
seconds s;
|
||||||
using milliseconds = std::chrono::duration<rep, std::milli>;
|
using milliseconds = std::chrono::duration<rep, std::milli>;
|
||||||
bool negative;
|
bool negative;
|
||||||
|
|
||||||
using char_type = typename FormatContext::char_type;
|
using tm_writer_type = tm_writer<iterator, Char>;
|
||||||
using tm_writer_type = tm_writer<OutputIt, char_type>;
|
|
||||||
|
|
||||||
chrono_formatter(FormatContext& ctx, OutputIt o,
|
duration_formatter(iterator o, std::chrono::duration<Rep, Period> d,
|
||||||
std::chrono::duration<Rep, Period> d)
|
locale_ref loc)
|
||||||
: context(ctx),
|
: out(o), val(static_cast<rep>(d.count())), locale(loc), negative(false) {
|
||||||
out(o),
|
|
||||||
val(static_cast<rep>(d.count())),
|
|
||||||
negative(false) {
|
|
||||||
if (d.count() < 0) {
|
if (d.count() < 0) {
|
||||||
val = 0 - val;
|
val = 0 - val;
|
||||||
negative = true;
|
negative = true;
|
||||||
@@ -1746,19 +1729,16 @@ struct chrono_formatter {
|
|||||||
|
|
||||||
// returns true if nan or inf, writes to out.
|
// returns true if nan or inf, writes to out.
|
||||||
auto handle_nan_inf() -> bool {
|
auto handle_nan_inf() -> bool {
|
||||||
if (isfinite(val)) {
|
if (isfinite(val)) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (isnan(val)) {
|
if (isnan(val)) {
|
||||||
write_nan();
|
write_nan();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// must be +-inf
|
// must be +-inf
|
||||||
if (val > 0) {
|
if (val > 0)
|
||||||
write_pinf();
|
std::copy_n("inf", 3, out);
|
||||||
} else {
|
else
|
||||||
write_ninf();
|
std::copy_n("-inf", 4, out);
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1786,11 +1766,10 @@ struct chrono_formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void write_sign() {
|
void write_sign() {
|
||||||
if (negative) {
|
if (!negative) return;
|
||||||
*out++ = '-';
|
*out++ = '-';
|
||||||
negative = false;
|
negative = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void write(Rep value, int width, pad_type pad = pad_type::zero) {
|
void write(Rep value, int width, pad_type pad = pad_type::zero) {
|
||||||
write_sign();
|
write_sign();
|
||||||
@@ -1801,24 +1780,22 @@ struct chrono_formatter {
|
|||||||
if (width > num_digits) {
|
if (width > num_digits) {
|
||||||
out = detail::write_padding(out, pad, width - num_digits);
|
out = detail::write_padding(out, pad, width - num_digits);
|
||||||
}
|
}
|
||||||
out = format_decimal<char_type>(out, n, num_digits);
|
out = format_decimal<Char>(out, n, num_digits);
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_nan() { std::copy_n("nan", 3, out); }
|
void write_nan() { std::copy_n("nan", 3, out); }
|
||||||
void write_pinf() { std::copy_n("inf", 3, out); }
|
|
||||||
void write_ninf() { std::copy_n("-inf", 4, out); }
|
|
||||||
|
|
||||||
template <typename Callback, typename... Args>
|
template <typename Callback, typename... Args>
|
||||||
void format_tm(const tm& time, Callback cb, Args... args) {
|
void format_tm(const tm& time, Callback cb, Args... args) {
|
||||||
if (isnan(val)) return write_nan();
|
if (isnan(val)) return write_nan();
|
||||||
get_locale loc(localized, context.locale());
|
get_locale loc(localized, locale);
|
||||||
auto w = tm_writer_type(loc, out, time);
|
auto w = tm_writer_type(loc, out, time);
|
||||||
(w.*cb)(args...);
|
(w.*cb)(args...);
|
||||||
out = w.out();
|
out = w.out();
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_text(const char_type* begin, const char_type* end) {
|
void on_text(const Char* begin, const Char* end) {
|
||||||
copy<char_type>(begin, end, out);
|
copy<Char>(begin, end, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are not implemented because durations don't have date information.
|
// These are not implemented because durations don't have date information.
|
||||||
@@ -1888,13 +1865,12 @@ struct chrono_formatter {
|
|||||||
write_floating_seconds(buf, std::chrono::duration<rep, Period>(val),
|
write_floating_seconds(buf, std::chrono::duration<rep, Period>(val),
|
||||||
precision);
|
precision);
|
||||||
if (negative) *out++ = '-';
|
if (negative) *out++ = '-';
|
||||||
if (buf.size() < 2 || buf[1] == '.') {
|
if (buf.size() < 2 || buf[1] == '.')
|
||||||
out = detail::write_padding(out, pad);
|
out = detail::write_padding(out, pad);
|
||||||
}
|
out = copy<Char>(buf.begin(), buf.end(), out);
|
||||||
out = copy<char_type>(buf.begin(), buf.end(), out);
|
|
||||||
} else {
|
} else {
|
||||||
write(second(), 2, pad);
|
write(second(), 2, pad);
|
||||||
write_fractional_seconds<char_type>(
|
write_fractional_seconds<Char>(
|
||||||
out, std::chrono::duration<rep, Period>(val), precision);
|
out, std::chrono::duration<rep, Period>(val), precision);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -1936,12 +1912,10 @@ struct chrono_formatter {
|
|||||||
void on_duration_value() {
|
void on_duration_value() {
|
||||||
if (handle_nan_inf()) return;
|
if (handle_nan_inf()) return;
|
||||||
write_sign();
|
write_sign();
|
||||||
out = format_duration_value<char_type>(out, val, precision);
|
out = format_duration_value<Char>(out, val, precision);
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_duration_unit() {
|
void on_duration_unit() { out = format_duration_unit<Char, Period>(out); }
|
||||||
out = format_duration_unit<char_type, Period>(out);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
@@ -2011,12 +1985,11 @@ class year_month_day {
|
|||||||
constexpr auto month() const noexcept -> fmt::month { return month_; }
|
constexpr auto month() const noexcept -> fmt::month { return month_; }
|
||||||
constexpr auto day() const noexcept -> fmt::day { return day_; }
|
constexpr auto day() const noexcept -> fmt::day { return day_; }
|
||||||
};
|
};
|
||||||
#endif
|
#endif // __cpp_lib_chrono >= 201907
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct formatter<weekday, Char> : private formatter<std::tm, Char> {
|
struct formatter<weekday, Char> : private formatter<std::tm, Char> {
|
||||||
private:
|
private:
|
||||||
bool localized_ = false;
|
|
||||||
bool use_tm_formatter_ = false;
|
bool use_tm_formatter_ = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -2024,8 +1997,7 @@ struct formatter<weekday, Char> : private formatter<std::tm, Char> {
|
|||||||
auto it = ctx.begin(), end = ctx.end();
|
auto it = ctx.begin(), end = ctx.end();
|
||||||
if (it != end && *it == 'L') {
|
if (it != end && *it == 'L') {
|
||||||
++it;
|
++it;
|
||||||
localized_ = true;
|
this->set_localized();
|
||||||
return it;
|
|
||||||
}
|
}
|
||||||
use_tm_formatter_ = it != end && *it != '}';
|
use_tm_formatter_ = it != end && *it != '}';
|
||||||
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
|
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
|
||||||
@@ -2036,7 +2008,7 @@ struct formatter<weekday, Char> : private formatter<std::tm, Char> {
|
|||||||
auto time = std::tm();
|
auto time = std::tm();
|
||||||
time.tm_wday = static_cast<int>(wd.c_encoding());
|
time.tm_wday = static_cast<int>(wd.c_encoding());
|
||||||
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
|
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
|
||||||
detail::get_locale loc(localized_, ctx.locale());
|
detail::get_locale loc(this->localized(), ctx.locale());
|
||||||
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
|
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
|
||||||
w.on_abbr_weekday();
|
w.on_abbr_weekday();
|
||||||
return w.out();
|
return w.out();
|
||||||
@@ -2070,7 +2042,6 @@ struct formatter<day, Char> : private formatter<std::tm, Char> {
|
|||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct formatter<month, Char> : private formatter<std::tm, Char> {
|
struct formatter<month, Char> : private formatter<std::tm, Char> {
|
||||||
private:
|
private:
|
||||||
bool localized_ = false;
|
|
||||||
bool use_tm_formatter_ = false;
|
bool use_tm_formatter_ = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -2078,8 +2049,7 @@ struct formatter<month, Char> : private formatter<std::tm, Char> {
|
|||||||
auto it = ctx.begin(), end = ctx.end();
|
auto it = ctx.begin(), end = ctx.end();
|
||||||
if (it != end && *it == 'L') {
|
if (it != end && *it == 'L') {
|
||||||
++it;
|
++it;
|
||||||
localized_ = true;
|
this->set_localized();
|
||||||
return it;
|
|
||||||
}
|
}
|
||||||
use_tm_formatter_ = it != end && *it != '}';
|
use_tm_formatter_ = it != end && *it != '}';
|
||||||
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
|
return use_tm_formatter_ ? formatter<std::tm, Char>::parse(ctx) : it;
|
||||||
@@ -2090,7 +2060,7 @@ struct formatter<month, Char> : private formatter<std::tm, Char> {
|
|||||||
auto time = std::tm();
|
auto time = std::tm();
|
||||||
time.tm_mon = static_cast<int>(static_cast<unsigned>(m)) - 1;
|
time.tm_mon = static_cast<int>(static_cast<unsigned>(m)) - 1;
|
||||||
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
|
if (use_tm_formatter_) return formatter<std::tm, Char>::format(time, ctx);
|
||||||
detail::get_locale loc(localized_, ctx.locale());
|
detail::get_locale loc(this->localized(), ctx.locale());
|
||||||
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
|
auto w = detail::tm_writer<decltype(ctx.out()), Char>(loc, ctx.out(), time);
|
||||||
w.on_abbr_month();
|
w.on_abbr_month();
|
||||||
return w.out();
|
return w.out();
|
||||||
@@ -2154,7 +2124,6 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
|||||||
format_specs specs_;
|
format_specs specs_;
|
||||||
detail::arg_ref<Char> width_ref_;
|
detail::arg_ref<Char> width_ref_;
|
||||||
detail::arg_ref<Char> precision_ref_;
|
detail::arg_ref<Char> precision_ref_;
|
||||||
bool localized_ = false;
|
|
||||||
basic_string_view<Char> fmt_;
|
basic_string_view<Char> fmt_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -2177,7 +2146,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
|||||||
it = detail::parse_precision(it, end, specs_, precision_ref_, ctx);
|
it = detail::parse_precision(it, end, specs_, precision_ref_, ctx);
|
||||||
}
|
}
|
||||||
if (it != end && *it == 'L') {
|
if (it != end && *it == 'L') {
|
||||||
localized_ = true;
|
specs_.set_localized();
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
end = detail::parse_chrono_format(it, end, checker);
|
end = detail::parse_chrono_format(it, end, checker);
|
||||||
@@ -2204,11 +2173,10 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
|||||||
out = detail::format_duration_value<Char>(out, d.count(), precision);
|
out = detail::format_duration_value<Char>(out, d.count(), precision);
|
||||||
detail::format_duration_unit<Char, Period>(out);
|
detail::format_duration_unit<Char, Period>(out);
|
||||||
} else {
|
} else {
|
||||||
using chrono_formatter =
|
auto f =
|
||||||
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period>;
|
detail::duration_formatter<Char, Rep, Period>(out, d, ctx.locale());
|
||||||
auto f = chrono_formatter(ctx, out, d);
|
|
||||||
f.precision = precision;
|
f.precision = precision;
|
||||||
f.localized = localized_;
|
f.localized = specs_.localized();
|
||||||
detail::parse_chrono_format(begin, end, f);
|
detail::parse_chrono_format(begin, end, f);
|
||||||
}
|
}
|
||||||
return detail::write(
|
return detail::write(
|
||||||
@@ -2220,30 +2188,15 @@ template <typename Char> struct formatter<std::tm, Char> {
|
|||||||
private:
|
private:
|
||||||
format_specs specs_;
|
format_specs specs_;
|
||||||
detail::arg_ref<Char> width_ref_;
|
detail::arg_ref<Char> width_ref_;
|
||||||
|
basic_string_view<Char> fmt_ =
|
||||||
|
detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
basic_string_view<Char> fmt_;
|
auto localized() const -> bool { return specs_.localized(); }
|
||||||
|
FMT_CONSTEXPR void set_localized() { specs_.set_localized(); }
|
||||||
|
|
||||||
template <typename Duration, typename FormatContext>
|
FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx, bool has_timezone)
|
||||||
auto do_format(const std::tm& tm, FormatContext& ctx,
|
-> const Char* {
|
||||||
const Duration* subsecs) const -> decltype(ctx.out()) {
|
|
||||||
auto specs = specs_;
|
|
||||||
auto buf = basic_memory_buffer<Char>();
|
|
||||||
auto out = basic_appender<Char>(buf);
|
|
||||||
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
|
|
||||||
ctx);
|
|
||||||
|
|
||||||
auto loc_ref = ctx.locale();
|
|
||||||
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
|
|
||||||
auto w =
|
|
||||||
detail::tm_writer<decltype(out), Char, Duration>(loc, out, tm, subsecs);
|
|
||||||
detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w);
|
|
||||||
return detail::write(
|
|
||||||
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
|
||||||
auto it = ctx.begin(), end = ctx.end();
|
auto it = ctx.begin(), end = ctx.end();
|
||||||
if (it == end || *it == '}') return it;
|
if (it == end || *it == '}') return it;
|
||||||
|
|
||||||
@@ -2256,12 +2209,41 @@ template <typename Char> struct formatter<std::tm, Char> {
|
|||||||
if (it == end) return it;
|
if (it == end) return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
end = detail::parse_chrono_format(it, end, detail::tm_format_checker());
|
if (*it == 'L') {
|
||||||
|
specs_.set_localized();
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
|
||||||
|
end = detail::parse_chrono_format(it, end,
|
||||||
|
detail::tm_format_checker(has_timezone));
|
||||||
// Replace the default format string only if the new spec is not empty.
|
// Replace the default format string only if the new spec is not empty.
|
||||||
if (end != it) fmt_ = {it, detail::to_unsigned(end - it)};
|
if (end != it) fmt_ = {it, detail::to_unsigned(end - it)};
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Duration, typename FormatContext>
|
||||||
|
auto do_format(const std::tm& tm, FormatContext& ctx,
|
||||||
|
const Duration* subsecs) const -> decltype(ctx.out()) {
|
||||||
|
auto specs = specs_;
|
||||||
|
auto buf = basic_memory_buffer<Char>();
|
||||||
|
auto out = basic_appender<Char>(buf);
|
||||||
|
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
|
||||||
|
ctx);
|
||||||
|
|
||||||
|
auto loc_ref = specs.localized() ? ctx.locale() : detail::locale_ref();
|
||||||
|
detail::get_locale loc(static_cast<bool>(loc_ref), loc_ref);
|
||||||
|
auto w = detail::tm_writer<basic_appender<Char>, Char, Duration>(
|
||||||
|
loc, out, tm, subsecs);
|
||||||
|
detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w);
|
||||||
|
return detail::write(
|
||||||
|
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||||
|
return do_parse(ctx, detail::has_tm_gmtoff<std::tm>::value);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const std::tm& tm, FormatContext& ctx) const
|
auto format(const std::tm& tm, FormatContext& ctx) const
|
||||||
-> decltype(ctx.out()) {
|
-> decltype(ctx.out()) {
|
||||||
@@ -2269,10 +2251,11 @@ template <typename Char> struct formatter<std::tm, Char> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// DEPRECATED! Reversed order of template parameters.
|
||||||
template <typename Char, typename Duration>
|
template <typename Char, typename Duration>
|
||||||
struct formatter<sys_time<Duration>, Char> : formatter<std::tm, Char> {
|
struct formatter<sys_time<Duration>, Char> : private formatter<std::tm, Char> {
|
||||||
FMT_CONSTEXPR formatter() {
|
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||||
this->fmt_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>();
|
return this->do_parse(ctx, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
@@ -2283,6 +2266,7 @@ struct formatter<sys_time<Duration>, Char> : formatter<std::tm, Char> {
|
|||||||
if (detail::const_check(
|
if (detail::const_check(
|
||||||
period::num == 1 && period::den == 1 &&
|
period::num == 1 && period::den == 1 &&
|
||||||
!std::is_floating_point<typename Duration::rep>::value)) {
|
!std::is_floating_point<typename Duration::rep>::value)) {
|
||||||
|
detail::set_tm_zone(tm, detail::utc());
|
||||||
return formatter<std::tm, Char>::format(tm, ctx);
|
return formatter<std::tm, Char>::format(tm, ctx);
|
||||||
}
|
}
|
||||||
Duration epoch = val.time_since_epoch();
|
Duration epoch = val.time_since_epoch();
|
||||||
@@ -2290,11 +2274,13 @@ struct formatter<sys_time<Duration>, Char> : formatter<std::tm, Char> {
|
|||||||
epoch - detail::duration_cast<std::chrono::seconds>(epoch));
|
epoch - detail::duration_cast<std::chrono::seconds>(epoch));
|
||||||
if (subsecs.count() < 0) {
|
if (subsecs.count() < 0) {
|
||||||
auto second = detail::duration_cast<Duration>(std::chrono::seconds(1));
|
auto second = detail::duration_cast<Duration>(std::chrono::seconds(1));
|
||||||
if (tm.tm_sec != 0)
|
if (tm.tm_sec != 0) {
|
||||||
--tm.tm_sec;
|
--tm.tm_sec;
|
||||||
else
|
} else {
|
||||||
tm = gmtime(val - second);
|
tm = gmtime(val - second);
|
||||||
subsecs += detail::duration_cast<Duration>(std::chrono::seconds(1));
|
detail::set_tm_zone(tm, detail::utc());
|
||||||
|
}
|
||||||
|
subsecs += second;
|
||||||
}
|
}
|
||||||
return formatter<std::tm, Char>::do_format(tm, ctx, &subsecs);
|
return formatter<std::tm, Char>::do_format(tm, ctx, &subsecs);
|
||||||
}
|
}
|
||||||
@@ -2312,23 +2298,29 @@ struct formatter<utc_time<Duration>, Char>
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename Duration, typename Char>
|
template <typename Duration, typename Char>
|
||||||
struct formatter<local_time<Duration>, Char> : formatter<std::tm, Char> {
|
struct formatter<local_time<Duration>, Char>
|
||||||
FMT_CONSTEXPR formatter() {
|
: private formatter<std::tm, Char> {
|
||||||
this->fmt_ = detail::string_literal<Char, '%', 'F', ' ', '%', 'T'>();
|
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||||
|
return this->do_parse(ctx, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(local_time<Duration> val, FormatContext& ctx) const
|
auto format(local_time<Duration> val, FormatContext& ctx) const
|
||||||
-> decltype(ctx.out()) {
|
-> decltype(ctx.out()) {
|
||||||
|
auto time_since_epoch = val.time_since_epoch();
|
||||||
|
auto seconds_since_epoch =
|
||||||
|
detail::duration_cast<std::chrono::seconds>(time_since_epoch);
|
||||||
|
// Use gmtime to prevent time zone conversion since local_time has an
|
||||||
|
// unspecified time zone.
|
||||||
|
std::tm t = gmtime(seconds_since_epoch.count());
|
||||||
using period = typename Duration::period;
|
using period = typename Duration::period;
|
||||||
if (period::num == 1 && period::den == 1 &&
|
if (period::num == 1 && period::den == 1 &&
|
||||||
!std::is_floating_point<typename Duration::rep>::value) {
|
!std::is_floating_point<typename Duration::rep>::value) {
|
||||||
return formatter<std::tm, Char>::format(localtime(val), ctx);
|
return formatter<std::tm, Char>::format(t, ctx);
|
||||||
}
|
}
|
||||||
auto epoch = val.time_since_epoch();
|
auto subsecs =
|
||||||
auto subsecs = detail::duration_cast<Duration>(
|
detail::duration_cast<Duration>(time_since_epoch - seconds_since_epoch);
|
||||||
epoch - detail::duration_cast<std::chrono::seconds>(epoch));
|
return formatter<std::tm, Char>::do_format(t, ctx, &subsecs);
|
||||||
return formatter<std::tm, Char>::do_format(localtime(val), ctx, &subsecs);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -190,11 +190,11 @@ enum class emphasis : uint8_t {
|
|||||||
// rgb is a struct for red, green and blue colors.
|
// rgb is a struct for red, green and blue colors.
|
||||||
// Using the name "rgb" makes some editors show the color in a tooltip.
|
// Using the name "rgb" makes some editors show the color in a tooltip.
|
||||||
struct rgb {
|
struct rgb {
|
||||||
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
|
constexpr rgb() : r(0), g(0), b(0) {}
|
||||||
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
constexpr rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
||||||
FMT_CONSTEXPR rgb(uint32_t hex)
|
constexpr rgb(uint32_t hex)
|
||||||
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
|
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
|
||||||
FMT_CONSTEXPR rgb(color hex)
|
constexpr rgb(color hex)
|
||||||
: r((uint32_t(hex) >> 16) & 0xFF),
|
: r((uint32_t(hex) >> 16) & 0xFF),
|
||||||
g((uint32_t(hex) >> 8) & 0xFF),
|
g((uint32_t(hex) >> 8) & 0xFF),
|
||||||
b(uint32_t(hex) & 0xFF) {}
|
b(uint32_t(hex) & 0xFF) {}
|
||||||
@@ -205,97 +205,135 @@ struct rgb {
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
// color is a struct of either a rgb color or a terminal color.
|
// A bit-packed variant of an RGB color, a terminal color, or unset color.
|
||||||
|
// see text_style for the bit-packing scheme.
|
||||||
struct color_type {
|
struct color_type {
|
||||||
FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
|
constexpr color_type() noexcept = default;
|
||||||
FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
|
constexpr color_type(color rgb_color) noexcept
|
||||||
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
: value_(static_cast<uint32_t>(rgb_color) | (1 << 24)) {}
|
||||||
|
constexpr color_type(rgb rgb_color) noexcept
|
||||||
|
: color_type(static_cast<color>(
|
||||||
|
(static_cast<uint32_t>(rgb_color.r) << 16) |
|
||||||
|
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b)) {}
|
||||||
|
constexpr color_type(terminal_color term_color) noexcept
|
||||||
|
: value_(static_cast<uint32_t>(term_color) | (3 << 24)) {}
|
||||||
|
|
||||||
|
constexpr auto is_terminal_color() const noexcept -> bool {
|
||||||
|
return (value_ & (1 << 25)) != 0;
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} {
|
|
||||||
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
|
constexpr auto value() const noexcept -> uint32_t {
|
||||||
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
return value_ & 0xFFFFFF;
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR color_type(terminal_color term_color) noexcept
|
|
||||||
: is_rgb(), value{} {
|
constexpr color_type(uint32_t value) noexcept : value_(value) {}
|
||||||
value.term_color = static_cast<uint8_t>(term_color);
|
|
||||||
}
|
uint32_t value_ = 0;
|
||||||
bool is_rgb;
|
|
||||||
union color_union {
|
|
||||||
uint8_t term_color;
|
|
||||||
uint32_t rgb_color;
|
|
||||||
} value;
|
|
||||||
};
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
/// A text style consisting of foreground and background colors and emphasis.
|
/// A text style consisting of foreground and background colors and emphasis.
|
||||||
class text_style {
|
class text_style {
|
||||||
|
// The information is packed as follows:
|
||||||
|
// ┌──┐
|
||||||
|
// │ 0│─┐
|
||||||
|
// │..│ ├── foreground color value
|
||||||
|
// │23│─┘
|
||||||
|
// ├──┤
|
||||||
|
// │24│─┬── discriminator for the above value. 00 if unset, 01 if it's
|
||||||
|
// │25│─┘ an RGB color, or 11 if it's a terminal color (10 is unused)
|
||||||
|
// ├──┤
|
||||||
|
// │26│──── overflow bit, always zero (see below)
|
||||||
|
// ├──┤
|
||||||
|
// │27│─┐
|
||||||
|
// │..│ │
|
||||||
|
// │50│ │
|
||||||
|
// ├──┤ │
|
||||||
|
// │51│ ├── background color (same format as the foreground color)
|
||||||
|
// │52│ │
|
||||||
|
// ├──┤ │
|
||||||
|
// │53│─┘
|
||||||
|
// ├──┤
|
||||||
|
// │54│─┐
|
||||||
|
// │..│ ├── emphases
|
||||||
|
// │61│─┘
|
||||||
|
// ├──┤
|
||||||
|
// │62│─┬── unused
|
||||||
|
// │63│─┘
|
||||||
|
// └──┘
|
||||||
|
// The overflow bits are there to make operator|= efficient.
|
||||||
|
// When ORing, we must throw if, for either the foreground or background,
|
||||||
|
// one style specifies a terminal color and the other specifies any color
|
||||||
|
// (terminal or RGB); in other words, if one discriminator is 11 and the
|
||||||
|
// other is 11 or 01.
|
||||||
|
//
|
||||||
|
// We do that check by adding the styles. Consider what adding does to each
|
||||||
|
// possible pair of discriminators:
|
||||||
|
// 00 + 00 = 000
|
||||||
|
// 01 + 00 = 001
|
||||||
|
// 11 + 00 = 011
|
||||||
|
// 01 + 01 = 010
|
||||||
|
// 11 + 01 = 100 (!!)
|
||||||
|
// 11 + 11 = 110 (!!)
|
||||||
|
// In the last two cases, the ones we want to catch, the third bit——the
|
||||||
|
// overflow bit——is set. Bingo.
|
||||||
|
//
|
||||||
|
// We must take into account the possible carry bit from the bits
|
||||||
|
// before the discriminator. The only potentially problematic case is
|
||||||
|
// 11 + 00 = 011 (a carry bit would make it 100, not good!), but a carry
|
||||||
|
// bit is impossible in that case, because 00 (unset color) means the
|
||||||
|
// 24 bits that precede the discriminator are all zero.
|
||||||
|
//
|
||||||
|
// This test can be applied to both colors simultaneously.
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
|
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
|
||||||
: set_foreground_color(), set_background_color(), ems(em) {}
|
: style_(static_cast<uint64_t>(em) << 54) {}
|
||||||
|
|
||||||
FMT_CONSTEXPR auto operator|=(const text_style& rhs) -> text_style& {
|
FMT_CONSTEXPR auto operator|=(text_style rhs) -> text_style& {
|
||||||
if (!set_foreground_color) {
|
if (((style_ + rhs.style_) & ((1ULL << 26) | (1ULL << 53))) != 0)
|
||||||
set_foreground_color = rhs.set_foreground_color;
|
|
||||||
foreground_color = rhs.foreground_color;
|
|
||||||
} else if (rhs.set_foreground_color) {
|
|
||||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
|
||||||
report_error("can't OR a terminal color");
|
report_error("can't OR a terminal color");
|
||||||
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
style_ |= rhs.style_;
|
||||||
}
|
|
||||||
|
|
||||||
if (!set_background_color) {
|
|
||||||
set_background_color = rhs.set_background_color;
|
|
||||||
background_color = rhs.background_color;
|
|
||||||
} else if (rhs.set_background_color) {
|
|
||||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
|
||||||
report_error("can't OR a terminal color");
|
|
||||||
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
|
||||||
static_cast<uint8_t>(rhs.ems));
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend FMT_CONSTEXPR auto operator|(text_style lhs, const text_style& rhs)
|
friend FMT_CONSTEXPR auto operator|(text_style lhs, text_style rhs)
|
||||||
-> text_style {
|
-> text_style {
|
||||||
return lhs |= rhs;
|
return lhs |= rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR auto operator==(text_style rhs) const noexcept -> bool {
|
||||||
|
return style_ == rhs.style_;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR auto operator!=(text_style rhs) const noexcept -> bool {
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR auto has_foreground() const noexcept -> bool {
|
FMT_CONSTEXPR auto has_foreground() const noexcept -> bool {
|
||||||
return set_foreground_color;
|
return (style_ & (1 << 24)) != 0;
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR auto has_background() const noexcept -> bool {
|
FMT_CONSTEXPR auto has_background() const noexcept -> bool {
|
||||||
return set_background_color;
|
return (style_ & (1ULL << 51)) != 0;
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool {
|
FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool {
|
||||||
return static_cast<uint8_t>(ems) != 0;
|
return (style_ >> 54) != 0;
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type {
|
FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type {
|
||||||
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
|
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
|
||||||
return foreground_color;
|
return style_ & 0x3FFFFFF;
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type {
|
FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type {
|
||||||
FMT_ASSERT(has_background(), "no background specified for this style");
|
FMT_ASSERT(has_background(), "no background specified for this style");
|
||||||
return background_color;
|
return (style_ >> 27) & 0x3FFFFFF;
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis {
|
FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis {
|
||||||
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
|
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
|
||||||
return ems;
|
return static_cast<emphasis>(style_ >> 54);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FMT_CONSTEXPR text_style(bool is_foreground,
|
FMT_CONSTEXPR text_style(uint64_t style) noexcept : style_(style) {}
|
||||||
detail::color_type text_color) noexcept
|
|
||||||
: set_foreground_color(), set_background_color(), ems() {
|
|
||||||
if (is_foreground) {
|
|
||||||
foreground_color = text_color;
|
|
||||||
set_foreground_color = true;
|
|
||||||
} else {
|
|
||||||
background_color = text_color;
|
|
||||||
set_background_color = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept
|
friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept
|
||||||
-> text_style;
|
-> text_style;
|
||||||
@@ -303,23 +341,19 @@ class text_style {
|
|||||||
friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept
|
friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept
|
||||||
-> text_style;
|
-> text_style;
|
||||||
|
|
||||||
detail::color_type foreground_color;
|
uint64_t style_ = 0;
|
||||||
detail::color_type background_color;
|
|
||||||
bool set_foreground_color;
|
|
||||||
bool set_background_color;
|
|
||||||
emphasis ems;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Creates a text style from the foreground (text) color.
|
/// Creates a text style from the foreground (text) color.
|
||||||
FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
|
FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
|
||||||
-> text_style {
|
-> text_style {
|
||||||
return text_style(true, foreground);
|
return foreground.value_;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a text style from the background color.
|
/// Creates a text style from the background color.
|
||||||
FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
|
FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
|
||||||
-> text_style {
|
-> text_style {
|
||||||
return text_style(false, background);
|
return static_cast<uint64_t>(background.value_) << 27;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
|
FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
|
||||||
@@ -334,9 +368,9 @@ template <typename Char> struct ansi_color_escape {
|
|||||||
const char* esc) noexcept {
|
const char* esc) noexcept {
|
||||||
// If we have a terminal color, we need to output another escape code
|
// If we have a terminal color, we need to output another escape code
|
||||||
// sequence.
|
// sequence.
|
||||||
if (!text_color.is_rgb) {
|
if (text_color.is_terminal_color()) {
|
||||||
bool is_background = esc == string_view("\x1b[48;2;");
|
bool is_background = esc == string_view("\x1b[48;2;");
|
||||||
uint32_t value = text_color.value.term_color;
|
uint32_t value = text_color.value();
|
||||||
// Background ASCII codes are the same as the foreground ones but with
|
// Background ASCII codes are the same as the foreground ones but with
|
||||||
// 10 more.
|
// 10 more.
|
||||||
if (is_background) value += 10u;
|
if (is_background) value += 10u;
|
||||||
@@ -360,7 +394,7 @@ template <typename Char> struct ansi_color_escape {
|
|||||||
for (int i = 0; i < 7; i++) {
|
for (int i = 0; i < 7; i++) {
|
||||||
buffer[i] = static_cast<Char>(esc[i]);
|
buffer[i] = static_cast<Char>(esc[i]);
|
||||||
}
|
}
|
||||||
rgb color(text_color.value.rgb_color);
|
rgb color(text_color.value());
|
||||||
to_esc(color.r, buffer + 7, ';');
|
to_esc(color.r, buffer + 7, ';');
|
||||||
to_esc(color.g, buffer + 11, ';');
|
to_esc(color.g, buffer + 11, ';');
|
||||||
to_esc(color.b, buffer + 15, 'm');
|
to_esc(color.b, buffer + 15, 'm');
|
||||||
@@ -441,32 +475,26 @@ template <typename T> struct styled_arg : view {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void vformat_to(buffer<Char>& buf, const text_style& ts,
|
void vformat_to(buffer<Char>& buf, text_style ts, basic_string_view<Char> fmt,
|
||||||
basic_string_view<Char> fmt,
|
|
||||||
basic_format_args<buffered_context<Char>> args) {
|
basic_format_args<buffered_context<Char>> args) {
|
||||||
bool has_style = false;
|
|
||||||
if (ts.has_emphasis()) {
|
if (ts.has_emphasis()) {
|
||||||
has_style = true;
|
|
||||||
auto emphasis = make_emphasis<Char>(ts.get_emphasis());
|
auto emphasis = make_emphasis<Char>(ts.get_emphasis());
|
||||||
buf.append(emphasis.begin(), emphasis.end());
|
buf.append(emphasis.begin(), emphasis.end());
|
||||||
}
|
}
|
||||||
if (ts.has_foreground()) {
|
if (ts.has_foreground()) {
|
||||||
has_style = true;
|
|
||||||
auto foreground = make_foreground_color<Char>(ts.get_foreground());
|
auto foreground = make_foreground_color<Char>(ts.get_foreground());
|
||||||
buf.append(foreground.begin(), foreground.end());
|
buf.append(foreground.begin(), foreground.end());
|
||||||
}
|
}
|
||||||
if (ts.has_background()) {
|
if (ts.has_background()) {
|
||||||
has_style = true;
|
|
||||||
auto background = make_background_color<Char>(ts.get_background());
|
auto background = make_background_color<Char>(ts.get_background());
|
||||||
buf.append(background.begin(), background.end());
|
buf.append(background.begin(), background.end());
|
||||||
}
|
}
|
||||||
vformat_to(buf, fmt, args);
|
vformat_to(buf, fmt, args);
|
||||||
if (has_style) reset_color<Char>(buf);
|
if (ts != text_style()) reset_color<Char>(buf);
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
inline void vprint(FILE* f, const text_style& ts, string_view fmt,
|
inline void vprint(FILE* f, text_style ts, string_view fmt, format_args args) {
|
||||||
format_args args) {
|
|
||||||
auto buf = memory_buffer();
|
auto buf = memory_buffer();
|
||||||
detail::vformat_to(buf, ts, fmt, args);
|
detail::vformat_to(buf, ts, fmt, args);
|
||||||
print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
|
print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
|
||||||
@@ -482,8 +510,7 @@ inline void vprint(FILE* f, const text_style& ts, string_view fmt,
|
|||||||
* "Elapsed time: {0:.2f} seconds", 1.23);
|
* "Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
*/
|
*/
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
void print(FILE* f, const text_style& ts, format_string<T...> fmt,
|
void print(FILE* f, text_style ts, format_string<T...> fmt, T&&... args) {
|
||||||
T&&... args) {
|
|
||||||
vprint(f, ts, fmt.str, vargs<T...>{{args...}});
|
vprint(f, ts, fmt.str, vargs<T...>{{args...}});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,11 +524,11 @@ void print(FILE* f, const text_style& ts, format_string<T...> fmt,
|
|||||||
* "Elapsed time: {0:.2f} seconds", 1.23);
|
* "Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
*/
|
*/
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
void print(const text_style& ts, format_string<T...> fmt, T&&... args) {
|
void print(text_style ts, format_string<T...> fmt, T&&... args) {
|
||||||
return print(stdout, ts, fmt, std::forward<T>(args)...);
|
return print(stdout, ts, fmt, std::forward<T>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto vformat(const text_style& ts, string_view fmt, format_args args)
|
inline auto vformat(text_style ts, string_view fmt, format_args args)
|
||||||
-> std::string {
|
-> std::string {
|
||||||
auto buf = memory_buffer();
|
auto buf = memory_buffer();
|
||||||
detail::vformat_to(buf, ts, fmt, args);
|
detail::vformat_to(buf, ts, fmt, args);
|
||||||
@@ -521,7 +548,7 @@ inline auto vformat(const text_style& ts, string_view fmt, format_args args)
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
inline auto format(const text_style& ts, format_string<T...> fmt, T&&... args)
|
inline auto format(text_style ts, format_string<T...> fmt, T&&... args)
|
||||||
-> std::string {
|
-> std::string {
|
||||||
return fmt::vformat(ts, fmt.str, vargs<T...>{{args...}});
|
return fmt::vformat(ts, fmt.str, vargs<T...>{{args...}});
|
||||||
}
|
}
|
||||||
@@ -529,8 +556,8 @@ inline auto format(const text_style& ts, format_string<T...> fmt, T&&... args)
|
|||||||
/// Formats a string with the given text_style and writes the output to `out`.
|
/// Formats a string with the given text_style and writes the output to `out`.
|
||||||
template <typename OutputIt,
|
template <typename OutputIt,
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
|
||||||
auto vformat_to(OutputIt out, const text_style& ts, string_view fmt,
|
auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args)
|
||||||
format_args args) -> OutputIt {
|
-> OutputIt {
|
||||||
auto&& buf = detail::get_buffer<char>(out);
|
auto&& buf = detail::get_buffer<char>(out);
|
||||||
detail::vformat_to(buf, ts, fmt, args);
|
detail::vformat_to(buf, ts, fmt, args);
|
||||||
return detail::get_iterator(buf, out);
|
return detail::get_iterator(buf, out);
|
||||||
@@ -548,8 +575,8 @@ auto vformat_to(OutputIt out, const text_style& ts, string_view fmt,
|
|||||||
*/
|
*/
|
||||||
template <typename OutputIt, typename... T,
|
template <typename OutputIt, typename... T,
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
|
||||||
inline auto format_to(OutputIt out, const text_style& ts,
|
inline auto format_to(OutputIt out, text_style ts, format_string<T...> fmt,
|
||||||
format_string<T...> fmt, T&&... args) -> OutputIt {
|
T&&... args) -> OutputIt {
|
||||||
return vformat_to(out, ts, fmt.str, vargs<T...>{{args...}});
|
return vformat_to(out, ts, fmt.str, vargs<T...>{{args...}});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
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
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
@@ -212,7 +212,7 @@ inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int {
|
|||||||
return (e * 631305 - 261663) >> 21;
|
return (e * 631305 - 261663) >> 21;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_INLINE_VARIABLE constexpr struct {
|
FMT_INLINE_VARIABLE constexpr struct div_small_pow10_infos_struct {
|
||||||
uint32_t divisor;
|
uint32_t divisor;
|
||||||
int shift_amount;
|
int shift_amount;
|
||||||
} div_small_pow10_infos[] = {{10, 16}, {100, 16}};
|
} div_small_pow10_infos[] = {{10, 16}, {100, 16}};
|
||||||
@@ -1097,7 +1097,7 @@ template <> struct cache_accessor<double> {
|
|||||||
return {r.high(), r.low() == 0};
|
return {r.high(), r.low() == 0};
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto compute_delta(cache_entry_type const& cache, int beta) noexcept
|
static auto compute_delta(const cache_entry_type& cache, int beta) noexcept
|
||||||
-> uint32_t {
|
-> uint32_t {
|
||||||
return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
|
return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
|
||||||
}
|
}
|
||||||
@@ -1526,9 +1526,8 @@ template <typename F> class glibc_file : public file_base<F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void init_buffer() {
|
void init_buffer() {
|
||||||
if (this->file_->_IO_write_ptr) return;
|
if (this->file_->_IO_write_ptr < this->file_->_IO_write_end) return;
|
||||||
// Force buffer initialization by placing and removing a char in a buffer.
|
// Force buffer initialization by placing and removing a char in a buffer.
|
||||||
assume(this->file_->_IO_write_ptr >= this->file_->_IO_write_end);
|
|
||||||
putc_unlocked(0, this->file_);
|
putc_unlocked(0, this->file_);
|
||||||
--this->file_->_IO_write_ptr;
|
--this->file_->_IO_write_ptr;
|
||||||
}
|
}
|
||||||
|
@@ -117,6 +117,7 @@
|
|||||||
# define FMT_NOINLINE
|
# define FMT_NOINLINE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// GCC 4.9 doesn't support qualified names in specializations.
|
||||||
namespace std {
|
namespace std {
|
||||||
template <typename T> struct iterator_traits<fmt::basic_appender<T>> {
|
template <typename T> struct iterator_traits<fmt::basic_appender<T>> {
|
||||||
using iterator_category = output_iterator_tag;
|
using iterator_category = output_iterator_tag;
|
||||||
@@ -705,7 +706,7 @@ using is_integer =
|
|||||||
|
|
||||||
#if defined(FMT_USE_FLOAT128)
|
#if defined(FMT_USE_FLOAT128)
|
||||||
// Use the provided definition.
|
// Use the provided definition.
|
||||||
#elif FMT_CLANG_VERSION && FMT_HAS_INCLUDE(<quadmath.h>)
|
#elif FMT_CLANG_VERSION >= 309 && FMT_HAS_INCLUDE(<quadmath.h>)
|
||||||
# define FMT_USE_FLOAT128 1
|
# define FMT_USE_FLOAT128 1
|
||||||
#elif FMT_GCC_VERSION && defined(_GLIBCXX_USE_FLOAT128) && \
|
#elif FMT_GCC_VERSION && defined(_GLIBCXX_USE_FLOAT128) && \
|
||||||
!defined(__STRICT_ANSI__)
|
!defined(__STRICT_ANSI__)
|
||||||
@@ -721,11 +722,10 @@ struct float128 {};
|
|||||||
|
|
||||||
template <typename T> using is_float128 = std::is_same<T, float128>;
|
template <typename T> using is_float128 = std::is_same<T, float128>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> struct is_floating_point : std::is_floating_point<T> {};
|
||||||
using is_floating_point =
|
template <> struct is_floating_point<float128> : std::true_type {};
|
||||||
bool_constant<std::is_floating_point<T>::value || is_float128<T>::value>;
|
|
||||||
|
|
||||||
template <typename T, bool = std::is_floating_point<T>::value>
|
template <typename T, bool = is_floating_point<T>::value>
|
||||||
struct is_fast_float : bool_constant<std::numeric_limits<T>::is_iec559 &&
|
struct is_fast_float : bool_constant<std::numeric_limits<T>::is_iec559 &&
|
||||||
sizeof(T) <= sizeof(double)> {};
|
sizeof(T) <= sizeof(double)> {};
|
||||||
template <typename T> struct is_fast_float<T, false> : std::false_type {};
|
template <typename T> struct is_fast_float<T, false> : std::false_type {};
|
||||||
@@ -1613,7 +1613,7 @@ constexpr auto convert_float(T value) -> convert_float_result<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char, typename OutputIt>
|
template <typename Char, typename OutputIt>
|
||||||
FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n,
|
FMT_CONSTEXPR FMT_NOINLINE auto fill(OutputIt it, size_t n,
|
||||||
const basic_specs& specs) -> OutputIt {
|
const basic_specs& specs) -> OutputIt {
|
||||||
auto fill_size = specs.fill_size();
|
auto fill_size = specs.fill_size();
|
||||||
if (fill_size == 1) return detail::fill_n(it, n, specs.fill_unit<Char>());
|
if (fill_size == 1) return detail::fill_n(it, n, specs.fill_unit<Char>());
|
||||||
@@ -2472,8 +2472,8 @@ template <typename T>
|
|||||||
struct has_isfinite<T, enable_if_t<sizeof(std::isfinite(T())) != 0>>
|
struct has_isfinite<T, enable_if_t<sizeof(std::isfinite(T())) != 0>>
|
||||||
: std::true_type {};
|
: std::true_type {};
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value&&
|
template <typename T,
|
||||||
has_isfinite<T>::value)>
|
FMT_ENABLE_IF(is_floating_point<T>::value&& has_isfinite<T>::value)>
|
||||||
FMT_CONSTEXPR20 auto isfinite(T value) -> bool {
|
FMT_CONSTEXPR20 auto isfinite(T value) -> bool {
|
||||||
constexpr T inf = T(std::numeric_limits<double>::infinity());
|
constexpr T inf = T(std::numeric_limits<double>::infinity());
|
||||||
if (is_constant_evaluated())
|
if (is_constant_evaluated())
|
||||||
|
@@ -158,7 +158,8 @@ void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
|
|||||||
|
|
||||||
FMT_EXPORT template <typename... T>
|
FMT_EXPORT template <typename... T>
|
||||||
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
|
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
|
||||||
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
|
fmt::print(os, FMT_STRING("{}\n"),
|
||||||
|
fmt::format(fmt, std::forward<T>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
@@ -774,13 +774,13 @@ struct formatter<
|
|||||||
: formatter<detail::all<typename T::container_type>, Char> {
|
: formatter<detail::all<typename T::container_type>, Char> {
|
||||||
using all = detail::all<typename T::container_type>;
|
using all = detail::all<typename T::container_type>;
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
|
auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||||
struct getter : T {
|
struct getter : T {
|
||||||
static auto get(const T& t) -> all {
|
static auto get(const T& v) -> all {
|
||||||
return {t.*(&getter::c)}; // Access c through the derived class.
|
return {v.*(&getter::c)}; // Access c through the derived class.
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return formatter<all>::format(getter::get(t), ctx);
|
return formatter<all>::format(getter::get(value), ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -113,7 +113,6 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
|
|||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
FMT_EXPORT
|
|
||||||
template <typename Char> struct formatter<std::filesystem::path, Char> {
|
template <typename Char> struct formatter<std::filesystem::path, Char> {
|
||||||
private:
|
private:
|
||||||
format_specs specs_;
|
format_specs specs_;
|
||||||
@@ -182,7 +181,6 @@ FMT_END_NAMESPACE
|
|||||||
#endif // FMT_CPP_LIB_FILESYSTEM
|
#endif // FMT_CPP_LIB_FILESYSTEM
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
FMT_EXPORT
|
|
||||||
template <std::size_t N, typename Char>
|
template <std::size_t N, typename Char>
|
||||||
struct formatter<std::bitset<N>, Char>
|
struct formatter<std::bitset<N>, Char>
|
||||||
: nested_formatter<basic_string_view<Char>, Char> {
|
: nested_formatter<basic_string_view<Char>, Char> {
|
||||||
@@ -209,14 +207,12 @@ struct formatter<std::bitset<N>, Char>
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_EXPORT
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
|
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#ifdef __cpp_lib_optional
|
#ifdef __cpp_lib_optional
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
FMT_EXPORT
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<std::optional<T>, Char,
|
struct formatter<std::optional<T>, Char,
|
||||||
std::enable_if_t<is_formattable<T, Char>::value>> {
|
std::enable_if_t<is_formattable<T, Char>::value>> {
|
||||||
@@ -279,7 +275,6 @@ FMT_END_NAMESPACE
|
|||||||
#ifdef __cpp_lib_expected
|
#ifdef __cpp_lib_expected
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
FMT_EXPORT
|
|
||||||
template <typename T, typename E, typename Char>
|
template <typename T, typename E, typename Char>
|
||||||
struct formatter<std::expected<T, E>, Char,
|
struct formatter<std::expected<T, E>, Char,
|
||||||
std::enable_if_t<(std::is_void<T>::value ||
|
std::enable_if_t<(std::is_void<T>::value ||
|
||||||
@@ -311,7 +306,6 @@ FMT_END_NAMESPACE
|
|||||||
|
|
||||||
#ifdef __cpp_lib_source_location
|
#ifdef __cpp_lib_source_location
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
FMT_EXPORT
|
|
||||||
template <> struct formatter<std::source_location> {
|
template <> struct formatter<std::source_location> {
|
||||||
FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
|
FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
|
||||||
|
|
||||||
@@ -367,7 +361,6 @@ template <typename T, typename C> struct is_variant_formattable {
|
|||||||
detail::is_variant_formattable_<T, C>::value;
|
detail::is_variant_formattable_<T, C>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_EXPORT
|
|
||||||
template <typename Char> struct formatter<std::monostate, Char> {
|
template <typename Char> struct formatter<std::monostate, Char> {
|
||||||
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
|
||||||
return ctx.begin();
|
return ctx.begin();
|
||||||
@@ -380,7 +373,6 @@ template <typename Char> struct formatter<std::monostate, Char> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_EXPORT
|
|
||||||
template <typename Variant, typename Char>
|
template <typename Variant, typename Char>
|
||||||
struct formatter<
|
struct formatter<
|
||||||
Variant, Char,
|
Variant, Char,
|
||||||
@@ -414,11 +406,11 @@ FMT_END_NAMESPACE
|
|||||||
#endif // FMT_CPP_LIB_VARIANT
|
#endif // FMT_CPP_LIB_VARIANT
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
FMT_EXPORT
|
|
||||||
template <> struct formatter<std::error_code> {
|
template <> struct formatter<std::error_code> {
|
||||||
private:
|
private:
|
||||||
format_specs specs_;
|
format_specs specs_;
|
||||||
detail::arg_ref<char> width_ref_;
|
detail::arg_ref<char> width_ref_;
|
||||||
|
bool debug_ = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
|
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
|
||||||
@@ -426,11 +418,19 @@ template <> struct formatter<std::error_code> {
|
|||||||
if (it == end) return it;
|
if (it == end) return it;
|
||||||
|
|
||||||
it = detail::parse_align(it, end, specs_);
|
it = detail::parse_align(it, end, specs_);
|
||||||
if (it == end) return it;
|
|
||||||
|
|
||||||
char c = *it;
|
char c = *it;
|
||||||
if ((c >= '0' && c <= '9') || c == '{')
|
if (it != end && ((c >= '0' && c <= '9') || c == '{'))
|
||||||
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
|
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
|
||||||
|
|
||||||
|
if (it != end && *it == '?') {
|
||||||
|
debug_ = true;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
if (it != end && *it == 's') {
|
||||||
|
specs_.set_type(presentation_type::string);
|
||||||
|
++it;
|
||||||
|
}
|
||||||
return it;
|
return it;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -440,12 +440,21 @@ template <> struct formatter<std::error_code> {
|
|||||||
auto specs = specs_;
|
auto specs = specs_;
|
||||||
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
|
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
|
||||||
ctx);
|
ctx);
|
||||||
memory_buffer buf;
|
auto buf = memory_buffer();
|
||||||
|
if (specs_.type() == presentation_type::string) {
|
||||||
|
buf.append(ec.message());
|
||||||
|
} else {
|
||||||
buf.append(string_view(ec.category().name()));
|
buf.append(string_view(ec.category().name()));
|
||||||
buf.push_back(':');
|
buf.push_back(':');
|
||||||
detail::write<char>(appender(buf), ec.value());
|
detail::write<char>(appender(buf), ec.value());
|
||||||
return detail::write<char>(ctx.out(), string_view(buf.data(), buf.size()),
|
}
|
||||||
specs);
|
auto quoted = memory_buffer();
|
||||||
|
auto str = string_view(buf.data(), buf.size());
|
||||||
|
if (debug_) {
|
||||||
|
detail::write_escaped_string<char>(std::back_inserter(quoted), str);
|
||||||
|
str = string_view(quoted.data(), quoted.size());
|
||||||
|
}
|
||||||
|
return detail::write<char>(ctx.out(), str, specs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -520,7 +529,6 @@ auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
|
|||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
FMT_EXPORT
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
|
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
|
||||||
> {
|
> {
|
||||||
@@ -537,7 +545,6 @@ struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FMT_EXPORT
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<
|
struct formatter<
|
||||||
T, Char, // DEPRECATED! Mixing code unit types.
|
T, Char, // DEPRECATED! Mixing code unit types.
|
||||||
@@ -603,7 +610,6 @@ struct is_bit_reference_like<std::__bit_const_reference<C>> {
|
|||||||
// We can't use std::vector<bool, Allocator>::reference and
|
// We can't use std::vector<bool, Allocator>::reference and
|
||||||
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
|
// std::bitset<N>::reference because the compiler can't deduce Allocator and N
|
||||||
// in partial specialization.
|
// in partial specialization.
|
||||||
FMT_EXPORT
|
|
||||||
template <typename BitRef, typename Char>
|
template <typename BitRef, typename Char>
|
||||||
struct formatter<BitRef, Char,
|
struct formatter<BitRef, Char,
|
||||||
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
|
enable_if_t<detail::is_bit_reference_like<BitRef>::value>>
|
||||||
@@ -623,7 +629,6 @@ template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
|
|||||||
return p.get();
|
return p.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_EXPORT
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<std::atomic<T>, Char,
|
struct formatter<std::atomic<T>, Char,
|
||||||
enable_if_t<is_formattable<T, Char>::value>>
|
enable_if_t<is_formattable<T, Char>::value>>
|
||||||
@@ -636,7 +641,6 @@ struct formatter<std::atomic<T>, Char,
|
|||||||
};
|
};
|
||||||
|
|
||||||
#ifdef __cpp_lib_atomic_flag_test
|
#ifdef __cpp_lib_atomic_flag_test
|
||||||
FMT_EXPORT
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
|
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
@@ -647,7 +651,6 @@ struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
|
|||||||
};
|
};
|
||||||
#endif // __cpp_lib_atomic_flag_test
|
#endif // __cpp_lib_atomic_flag_test
|
||||||
|
|
||||||
FMT_EXPORT
|
|
||||||
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
|
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
|
||||||
private:
|
private:
|
||||||
detail::dynamic_format_specs<Char> specs_;
|
detail::dynamic_format_specs<Char> specs_;
|
||||||
@@ -710,7 +713,6 @@ template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_EXPORT
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<std::reference_wrapper<T>, Char,
|
struct formatter<std::reference_wrapper<T>, Char,
|
||||||
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
|
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
|
||||||
|
@@ -112,10 +112,6 @@ inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
|
|||||||
return {{s}};
|
return {{s}};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> struct is_char<wchar_t> : std::true_type {};
|
|
||||||
template <> struct is_char<char16_t> : std::true_type {};
|
|
||||||
template <> struct is_char<char32_t> : std::true_type {};
|
|
||||||
|
|
||||||
#ifdef __cpp_char8_t
|
#ifdef __cpp_char8_t
|
||||||
template <> struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled> {};
|
template <> struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled> {};
|
||||||
#endif
|
#endif
|
||||||
@@ -322,7 +318,7 @@ template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
|
|||||||
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
|
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args)
|
inline auto vformat(text_style ts, wstring_view fmt, wformat_args args)
|
||||||
-> std::wstring {
|
-> std::wstring {
|
||||||
auto buf = wmemory_buffer();
|
auto buf = wmemory_buffer();
|
||||||
detail::vformat_to(buf, ts, fmt, args);
|
detail::vformat_to(buf, ts, fmt, args);
|
||||||
@@ -330,19 +326,19 @@ inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
inline auto format(const text_style& ts, wformat_string<T...> fmt, T&&... args)
|
inline auto format(text_style ts, wformat_string<T...> fmt, T&&... args)
|
||||||
-> std::wstring {
|
-> std::wstring {
|
||||||
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
|
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
FMT_DEPRECATED void print(std::FILE* f, const text_style& ts,
|
FMT_DEPRECATED void print(std::FILE* f, text_style ts, wformat_string<T...> fmt,
|
||||||
wformat_string<T...> fmt, const T&... args) {
|
const T&... args) {
|
||||||
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
|
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... T>
|
template <typename... T>
|
||||||
FMT_DEPRECATED void print(const text_style& ts, wformat_string<T...> fmt,
|
FMT_DEPRECATED void print(text_style ts, wformat_string<T...> fmt,
|
||||||
const T&... args) {
|
const T&... args) {
|
||||||
return print(stdout, ts, fmt, args...);
|
return print(stdout, ts, fmt, args...);
|
||||||
}
|
}
|
||||||
|
@@ -21,10 +21,10 @@
|
|||||||
#define FMT_USE_WINDOWS_H 0
|
#define FMT_USE_WINDOWS_H 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <spdlog/fmt/bundled/core.h>
|
#include <spdlog/fmt/bundled/base.h>
|
||||||
#include <spdlog/fmt/bundled/format.h>
|
#include <spdlog/fmt/bundled/format.h>
|
||||||
|
|
||||||
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
|
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
|
||||||
#include <fmt/core.h>
|
#include <fmt/base.h>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#endif
|
#endif
|
||||||
|
@@ -12,12 +12,14 @@
|
|||||||
|
|
||||||
#include <spdlog/common.h>
|
#include <spdlog/common.h>
|
||||||
|
|
||||||
// MDC is a simple map of key->string values stored in thread local storage whose content will be printed by the loggers.
|
// MDC is a simple map of key->string values stored in thread local storage whose content will be
|
||||||
// Note: Not supported in async mode (thread local storage - so the async thread pool have different copy).
|
// printed by the loggers. Note: Not supported in async mode (thread local storage - so the async
|
||||||
|
// thread pool have different copy).
|
||||||
//
|
//
|
||||||
// Usage example:
|
// Usage example:
|
||||||
// spdlog::mdc::put("mdc_key_1", "mdc_value_1");
|
// spdlog::mdc::put("mdc_key_1", "mdc_value_1");
|
||||||
// spdlog::info("Hello, {}", "World!"); // => [2024-04-26 02:08:05.040] [info] [mdc_key_1:mdc_value_1] Hello, World!
|
// spdlog::info("Hello, {}", "World!"); // => [2024-04-26 02:08:05.040] [info]
|
||||||
|
// [mdc_key_1:mdc_value_1] Hello, World!
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
class SPDLOG_API mdc {
|
class SPDLOG_API mdc {
|
||||||
|
@@ -111,7 +111,8 @@ SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color_mode_(color_mode mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename ConsoleMutex>
|
template <typename ConsoleMutex>
|
||||||
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_ccode_(const string_view_t &color_code) const {
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_ccode_(
|
||||||
|
const string_view_t &color_code) const {
|
||||||
details::os::fwrite_bytes(color_code.data(), color_code.size(), target_file_);
|
details::os::fwrite_bytes(color_code.data(), color_code.size(), target_file_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,7 +27,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void sink_it_(const details::log_msg &msg) override { callback_(msg); }
|
void sink_it_(const details::log_msg &msg) override { callback_(msg); }
|
||||||
void flush_() override{}
|
void flush_() override {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
custom_log_callback callback_;
|
custom_log_callback callback_;
|
||||||
|
@@ -40,22 +40,21 @@ template <typename Mutex>
|
|||||||
class dup_filter_sink : public dist_sink<Mutex> {
|
class dup_filter_sink : public dist_sink<Mutex> {
|
||||||
public:
|
public:
|
||||||
template <class Rep, class Period>
|
template <class Rep, class Period>
|
||||||
explicit dup_filter_sink(std::chrono::duration<Rep, Period> max_skip_duration,
|
explicit dup_filter_sink(std::chrono::duration<Rep, Period> max_skip_duration)
|
||||||
level::level_enum notification_level = level::info)
|
: max_skip_duration_{max_skip_duration} {}
|
||||||
: max_skip_duration_{max_skip_duration},
|
|
||||||
log_level_{notification_level} {}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::chrono::microseconds max_skip_duration_;
|
std::chrono::microseconds max_skip_duration_;
|
||||||
log_clock::time_point last_msg_time_;
|
log_clock::time_point last_msg_time_;
|
||||||
std::string last_msg_payload_;
|
std::string last_msg_payload_;
|
||||||
size_t skip_counter_ = 0;
|
size_t skip_counter_ = 0;
|
||||||
level::level_enum log_level_;
|
level::level_enum skipped_msg_log_level_ = spdlog::level::level_enum::off;
|
||||||
|
|
||||||
void sink_it_(const details::log_msg &msg) override {
|
void sink_it_(const details::log_msg &msg) override {
|
||||||
bool filtered = filter_(msg);
|
bool filtered = filter_(msg);
|
||||||
if (!filtered) {
|
if (!filtered) {
|
||||||
skip_counter_ += 1;
|
skip_counter_ += 1;
|
||||||
|
skipped_msg_log_level_ = msg.level;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +64,7 @@ protected:
|
|||||||
auto msg_size = ::snprintf(buf, sizeof(buf), "Skipped %u duplicate messages..",
|
auto msg_size = ::snprintf(buf, sizeof(buf), "Skipped %u duplicate messages..",
|
||||||
static_cast<unsigned>(skip_counter_));
|
static_cast<unsigned>(skip_counter_));
|
||||||
if (msg_size > 0 && static_cast<size_t>(msg_size) < sizeof(buf)) {
|
if (msg_size > 0 && static_cast<size_t>(msg_size) < sizeof(buf)) {
|
||||||
details::log_msg skipped_msg{msg.source, msg.logger_name, log_level_,
|
details::log_msg skipped_msg{msg.source, msg.logger_name, skipped_msg_log_level_,
|
||||||
string_view_t{buf, static_cast<size_t>(msg_size)}};
|
string_view_t{buf, static_cast<size_t>(msg_size)}};
|
||||||
dist_sink<Mutex>::sink_it_(skipped_msg);
|
dist_sink<Mutex>::sink_it_(skipped_msg);
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,6 @@
|
|||||||
#include <spdlog/fmt/fmt.h>
|
#include <spdlog/fmt/fmt.h>
|
||||||
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <chrono>
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -38,8 +37,8 @@ SPDLOG_INLINE rotating_file_sink<Mutex>::rotating_file_sink(
|
|||||||
throw_spdlog_ex("rotating sink constructor: max_size arg cannot be zero");
|
throw_spdlog_ex("rotating sink constructor: max_size arg cannot be zero");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (max_files > 200000) {
|
if (max_files > MaxFiles) {
|
||||||
throw_spdlog_ex("rotating sink constructor: max_files arg cannot exceed 200000");
|
throw_spdlog_ex("rotating sink constructor: max_files arg cannot exceed MaxFiles");
|
||||||
}
|
}
|
||||||
file_helper_.open(calc_filename(base_filename_, 0));
|
file_helper_.open(calc_filename(base_filename_, 0));
|
||||||
current_size_ = file_helper_.size(); // expensive. called only once
|
current_size_ = file_helper_.size(); // expensive. called only once
|
||||||
@@ -54,11 +53,12 @@ SPDLOG_INLINE rotating_file_sink<Mutex>::rotating_file_sink(
|
|||||||
template <typename Mutex>
|
template <typename Mutex>
|
||||||
SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::calc_filename(const filename_t &filename,
|
SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::calc_filename(const filename_t &filename,
|
||||||
std::size_t index) {
|
std::size_t index) {
|
||||||
if (index == 0u) {
|
if (index == 0U) {
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
filename_t basename, ext;
|
filename_t basename;
|
||||||
|
filename_t ext;
|
||||||
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
|
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
|
||||||
return fmt_lib::format(SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{}.{}{}")), basename, index, ext);
|
return fmt_lib::format(SPDLOG_FMT_STRING(SPDLOG_FILENAME_T("{}.{}{}")), basename, index, ext);
|
||||||
}
|
}
|
||||||
@@ -74,6 +74,35 @@ SPDLOG_INLINE void rotating_file_sink<Mutex>::rotate_now() {
|
|||||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
rotate_();
|
rotate_();
|
||||||
}
|
}
|
||||||
|
template <typename Mutex>
|
||||||
|
SPDLOG_INLINE void rotating_file_sink<Mutex>::set_max_size(std::size_t max_size) {
|
||||||
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
|
if (max_size == 0) {
|
||||||
|
throw_spdlog_ex("rotating sink set_max_size: max_size arg cannot be zero");
|
||||||
|
}
|
||||||
|
max_size_ = max_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Mutex>
|
||||||
|
SPDLOG_INLINE std::size_t rotating_file_sink<Mutex>::get_max_size() {
|
||||||
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
|
return max_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Mutex>
|
||||||
|
SPDLOG_INLINE void rotating_file_sink<Mutex>::set_max_files(std::size_t max_files) {
|
||||||
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
|
if (max_files > MaxFiles) {
|
||||||
|
throw_spdlog_ex("rotating sink set_max_files: max_files arg cannot exceed 200000");
|
||||||
|
}
|
||||||
|
max_files_ = max_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Mutex>
|
||||||
|
std::size_t rotating_file_sink<Mutex>::get_max_files() {
|
||||||
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
|
return max_files_;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Mutex>
|
template <typename Mutex>
|
||||||
SPDLOG_INLINE void rotating_file_sink<Mutex>::sink_it_(const details::log_msg &msg) {
|
SPDLOG_INLINE void rotating_file_sink<Mutex>::sink_it_(const details::log_msg &msg) {
|
||||||
|
@@ -8,7 +8,6 @@
|
|||||||
#include <spdlog/details/synchronous_factory.h>
|
#include <spdlog/details/synchronous_factory.h>
|
||||||
#include <spdlog/sinks/base_sink.h>
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -21,6 +20,7 @@ namespace sinks {
|
|||||||
template <typename Mutex>
|
template <typename Mutex>
|
||||||
class rotating_file_sink final : public base_sink<Mutex> {
|
class rotating_file_sink final : public base_sink<Mutex> {
|
||||||
public:
|
public:
|
||||||
|
static constexpr size_t MaxFiles = 200000;
|
||||||
rotating_file_sink(filename_t base_filename,
|
rotating_file_sink(filename_t base_filename,
|
||||||
std::size_t max_size,
|
std::size_t max_size,
|
||||||
std::size_t max_files,
|
std::size_t max_files,
|
||||||
@@ -29,6 +29,10 @@ public:
|
|||||||
static filename_t calc_filename(const filename_t &filename, std::size_t index);
|
static filename_t calc_filename(const filename_t &filename, std::size_t index);
|
||||||
filename_t filename();
|
filename_t filename();
|
||||||
void rotate_now();
|
void rotate_now();
|
||||||
|
void set_max_size(std::size_t max_size);
|
||||||
|
std::size_t get_max_size();
|
||||||
|
void set_max_files(std::size_t max_files);
|
||||||
|
std::size_t get_max_files();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void sink_it_(const details::log_msg &msg) override;
|
void sink_it_(const details::log_msg &msg) override;
|
||||||
@@ -61,9 +65,8 @@ using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
|
|||||||
//
|
//
|
||||||
// factory functions
|
// factory functions
|
||||||
//
|
//
|
||||||
|
|
||||||
template <typename Factory = spdlog::synchronous_factory>
|
template <typename Factory = spdlog::synchronous_factory>
|
||||||
inline std::shared_ptr<logger> rotating_logger_mt(const std::string &logger_name,
|
std::shared_ptr<logger> rotating_logger_mt(const std::string &logger_name,
|
||||||
const filename_t &filename,
|
const filename_t &filename,
|
||||||
size_t max_file_size,
|
size_t max_file_size,
|
||||||
size_t max_files,
|
size_t max_files,
|
||||||
@@ -74,7 +77,7 @@ inline std::shared_ptr<logger> rotating_logger_mt(const std::string &logger_name
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Factory = spdlog::synchronous_factory>
|
template <typename Factory = spdlog::synchronous_factory>
|
||||||
inline std::shared_ptr<logger> rotating_logger_st(const std::string &logger_name,
|
std::shared_ptr<logger> rotating_logger_st(const std::string &logger_name,
|
||||||
const filename_t &filename,
|
const filename_t &filename,
|
||||||
size_t max_file_size,
|
size_t max_file_size,
|
||||||
size_t max_files,
|
size_t max_files,
|
||||||
|
@@ -59,6 +59,10 @@ SPDLOG_INLINE void register_logger(std::shared_ptr<logger> logger) {
|
|||||||
details::registry::instance().register_logger(std::move(logger));
|
details::registry::instance().register_logger(std::move(logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void register_or_replace(std::shared_ptr<logger> logger) {
|
||||||
|
details::registry::instance().register_or_replace(std::move(logger));
|
||||||
|
}
|
||||||
|
|
||||||
SPDLOG_INLINE void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun) {
|
SPDLOG_INLINE void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun) {
|
||||||
details::registry::instance().apply_all(fun);
|
details::registry::instance().apply_all(fun);
|
||||||
}
|
}
|
||||||
|
@@ -25,7 +25,7 @@ namespace spdlog {
|
|||||||
using default_factory = synchronous_factory;
|
using default_factory = synchronous_factory;
|
||||||
|
|
||||||
// Create and register a logger with a templated sink type
|
// Create and register a logger with a templated sink type
|
||||||
// The logger's level, formatter and flush level will be set according the
|
// The logger's level, formatter and flush level will be set according to the
|
||||||
// global settings.
|
// global settings.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
@@ -46,7 +46,7 @@ inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs
|
|||||||
// spdlog::initialize_logger(mylogger);
|
// spdlog::initialize_logger(mylogger);
|
||||||
SPDLOG_API void initialize_logger(std::shared_ptr<logger> logger);
|
SPDLOG_API void initialize_logger(std::shared_ptr<logger> logger);
|
||||||
|
|
||||||
// Return an existing logger or nullptr if a logger with such name doesn't
|
// Return an existing logger or nullptr if a logger with such a name doesn't
|
||||||
// exist.
|
// exist.
|
||||||
// example: spdlog::get("my_logger")->info("hello {}", "world");
|
// example: spdlog::get("my_logger")->info("hello {}", "world");
|
||||||
SPDLOG_API std::shared_ptr<logger> get(const std::string &name);
|
SPDLOG_API std::shared_ptr<logger> get(const std::string &name);
|
||||||
@@ -71,13 +71,13 @@ SPDLOG_API void dump_backtrace();
|
|||||||
// Get global logging level
|
// Get global logging level
|
||||||
SPDLOG_API level::level_enum get_level();
|
SPDLOG_API level::level_enum get_level();
|
||||||
|
|
||||||
// Set global logging level
|
// Set the global logging level
|
||||||
SPDLOG_API void set_level(level::level_enum log_level);
|
SPDLOG_API void set_level(level::level_enum log_level);
|
||||||
|
|
||||||
// Determine whether the default logger should log messages with a certain level
|
// Determine whether the default logger should log messages with a certain level
|
||||||
SPDLOG_API bool should_log(level::level_enum lvl);
|
SPDLOG_API bool should_log(level::level_enum lvl);
|
||||||
|
|
||||||
// Set global flush level
|
// Set a global flush level
|
||||||
SPDLOG_API void flush_on(level::level_enum log_level);
|
SPDLOG_API void flush_on(level::level_enum log_level);
|
||||||
|
|
||||||
// Start/Restart a periodic flusher thread
|
// Start/Restart a periodic flusher thread
|
||||||
@@ -91,9 +91,14 @@ inline void flush_every(std::chrono::duration<Rep, Period> interval) {
|
|||||||
SPDLOG_API void set_error_handler(void (*handler)(const std::string &msg));
|
SPDLOG_API void set_error_handler(void (*handler)(const std::string &msg));
|
||||||
|
|
||||||
// Register the given logger with the given name
|
// Register the given logger with the given name
|
||||||
|
// Will throw if a logger with the same name already exists.
|
||||||
SPDLOG_API void register_logger(std::shared_ptr<logger> logger);
|
SPDLOG_API void register_logger(std::shared_ptr<logger> logger);
|
||||||
|
|
||||||
// Apply a user defined function on all registered loggers
|
// Register the given logger with the given name
|
||||||
|
// Will replace any the existing logger with the same name if exists.
|
||||||
|
SPDLOG_API void register_or_replace(std::shared_ptr<logger> logger);
|
||||||
|
|
||||||
|
// Apply a user-defined function on all registered loggers
|
||||||
// Example:
|
// Example:
|
||||||
// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
|
// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
|
||||||
SPDLOG_API void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun);
|
SPDLOG_API void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun);
|
||||||
@@ -111,19 +116,19 @@ SPDLOG_API void shutdown();
|
|||||||
SPDLOG_API void set_automatic_registration(bool automatic_registration);
|
SPDLOG_API void set_automatic_registration(bool automatic_registration);
|
||||||
|
|
||||||
// API for using default logger (stdout_color_mt),
|
// API for using default logger (stdout_color_mt),
|
||||||
// e.g: spdlog::info("Message {}", 1);
|
// e.g.: spdlog::info("Message {}", 1);
|
||||||
//
|
//
|
||||||
// The default logger object can be accessed using the spdlog::default_logger():
|
// The default logger object can be accessed using the spdlog::default_logger():
|
||||||
// For example, to add another sink to it:
|
// For example, to add another sink to it:
|
||||||
// spdlog::default_logger()->sinks().push_back(some_sink);
|
// spdlog::default_logger()->sinks().push_back(some_sink);
|
||||||
//
|
//
|
||||||
// The default logger can replaced using spdlog::set_default_logger(new_logger).
|
// The default logger can be replaced using spdlog::set_default_logger(new_logger).
|
||||||
// For example, to replace it with a file logger.
|
// For example, to replace it with a file logger.
|
||||||
//
|
//
|
||||||
// IMPORTANT:
|
// IMPORTANT:
|
||||||
// The default API is thread safe (for _mt loggers), but:
|
// The default API is thread safe (for _mt loggers), but:
|
||||||
// set_default_logger() *should not* be used concurrently with the default API.
|
// set_default_logger() *should not* be used concurrently with the default API.
|
||||||
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
// e.g., do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
||||||
|
|
||||||
SPDLOG_API std::shared_ptr<spdlog::logger> default_logger();
|
SPDLOG_API std::shared_ptr<spdlog::logger> default_logger();
|
||||||
|
|
||||||
|
@@ -109,8 +109,8 @@
|
|||||||
//
|
//
|
||||||
// #include <string_view>
|
// #include <string_view>
|
||||||
// using namespace std::string_view_literals;
|
// using namespace std::string_view_literals;
|
||||||
// #define SPDLOG_LEVEL_NAMES { "MY TRACE"sv, "MY DEBUG"sv, "MY INFO"sv, "MY WARNING"sv, "MY ERROR"sv, "MY
|
// #define SPDLOG_LEVEL_NAMES { "MY TRACE"sv, "MY DEBUG"sv, "MY INFO"sv, "MY WARNING"sv, "MY
|
||||||
// CRITICAL"sv, "OFF"sv }
|
// ERROR"sv, "MY CRITICAL"sv, "OFF"sv }
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
@@ -13,34 +13,32 @@
|
|||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template FMT_API auto dragonbox::to_decimal(float x) noexcept
|
template FMT_API auto dragonbox::to_decimal(float x) noexcept -> dragonbox::decimal_fp<float>;
|
||||||
-> dragonbox::decimal_fp<float>;
|
template FMT_API auto dragonbox::to_decimal(double x) noexcept -> dragonbox::decimal_fp<double>;
|
||||||
template FMT_API auto dragonbox::to_decimal(double x) noexcept
|
|
||||||
-> dragonbox::decimal_fp<double>;
|
|
||||||
|
|
||||||
#if FMT_USE_LOCALE
|
#if FMT_USE_LOCALE
|
||||||
// DEPRECATED! locale_ref in the detail namespace
|
// DEPRECATED! locale_ref in the detail namespace
|
||||||
template FMT_API locale_ref::locale_ref(const std::locale& loc);
|
template FMT_API locale_ref::locale_ref(const std::locale& loc);
|
||||||
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
|
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Explicit instantiations for char.
|
// Explicit instantiations for char.
|
||||||
|
|
||||||
template FMT_API auto thousands_sep_impl(locale_ref)
|
template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result<char>;
|
||||||
-> thousands_sep_result<char>;
|
|
||||||
template FMT_API auto decimal_point_impl(locale_ref) -> char;
|
template FMT_API auto decimal_point_impl(locale_ref) -> char;
|
||||||
|
|
||||||
// DEPRECATED!
|
// DEPRECATED!
|
||||||
template FMT_API void buffer<char>::append(const char*, const char*);
|
template FMT_API void buffer<char>::append(const char*, const char*);
|
||||||
|
|
||||||
// DEPRECATED!
|
// DEPRECATED!
|
||||||
template FMT_API void vformat_to(buffer<char>&, string_view,
|
template FMT_API void vformat_to(buffer<char>&,
|
||||||
typename vformat_args<>::type, locale_ref);
|
string_view,
|
||||||
|
typename vformat_args<>::type,
|
||||||
|
locale_ref);
|
||||||
|
|
||||||
// Explicit instantiations for wchar_t.
|
// Explicit instantiations for wchar_t.
|
||||||
|
|
||||||
template FMT_API auto thousands_sep_impl(locale_ref)
|
template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result<wchar_t>;
|
||||||
-> thousands_sep_result<wchar_t>;
|
|
||||||
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
|
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
|
||||||
|
|
||||||
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
|
template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
|
||||||
@@ -48,5 +46,4 @@ template FMT_API void buffer<wchar_t>::append(const wchar_t*, const wchar_t*);
|
|||||||
} // namespace detail
|
} // namespace detail
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
|
||||||
#endif // !SPDLOG_FMT_EXTERNAL
|
#endif // !SPDLOG_FMT_EXTERNAL
|
||||||
|
@@ -19,8 +19,8 @@ if(Catch2_FOUND)
|
|||||||
else()
|
else()
|
||||||
message(STATUS "Bundled version of Catch will be downloaded and used.")
|
message(STATUS "Bundled version of Catch will be downloaded and used.")
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
FetchContent_Declare(Catch2
|
FetchContent_Declare(
|
||||||
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
||||||
GIT_TAG 53d0d913a422d356b23dd927547febdf69ee9081 # v3.5.0
|
GIT_TAG 53d0d913a422d356b23dd927547febdf69ee9081 # v3.5.0
|
||||||
)
|
)
|
||||||
FetchContent_MakeAvailable(Catch2)
|
FetchContent_MakeAvailable(Catch2)
|
||||||
@@ -71,9 +71,9 @@ function(spdlog_prepare_test test_target spdlog_lib)
|
|||||||
target_link_libraries(${test_target} PRIVATE Catch2::Catch2WithMain)
|
target_link_libraries(${test_target} PRIVATE Catch2::Catch2WithMain)
|
||||||
if(SPDLOG_SANITIZE_ADDRESS)
|
if(SPDLOG_SANITIZE_ADDRESS)
|
||||||
spdlog_enable_addr_sanitizer(${test_target})
|
spdlog_enable_addr_sanitizer(${test_target})
|
||||||
elseif (SPDLOG_SANITIZE_THREAD)
|
elseif(SPDLOG_SANITIZE_THREAD)
|
||||||
spdlog_enable_thread_sanitizer(${test_target})
|
spdlog_enable_thread_sanitizer(${test_target})
|
||||||
endif ()
|
endif()
|
||||||
add_test(NAME ${test_target} COMMAND ${test_target})
|
add_test(NAME ${test_target} COMMAND ${test_target})
|
||||||
set_tests_properties(${test_target} PROPERTIES RUN_SERIAL ON)
|
set_tests_properties(${test_target} PROPERTIES RUN_SERIAL ON)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
@@ -16,7 +16,8 @@ TEST_CASE("custom_callback_logger", "[custom_callback_logger]") {
|
|||||||
spdlog::memory_buf_t formatted;
|
spdlog::memory_buf_t formatted;
|
||||||
formatter.format(msg, formatted);
|
formatter.format(msg, formatted);
|
||||||
auto eol_len = strlen(spdlog::details::os::default_eol);
|
auto eol_len = strlen(spdlog::details::os::default_eol);
|
||||||
using diff_t = typename std::iterator_traits<decltype(formatted.end())>::difference_type;
|
using diff_t =
|
||||||
|
typename std::iterator_traits<decltype(formatted.end())>::difference_type;
|
||||||
lines.emplace_back(formatted.begin(), formatted.end() - static_cast<diff_t>(eol_len));
|
lines.emplace_back(formatted.begin(), formatted.end() - static_cast<diff_t>(eol_len));
|
||||||
});
|
});
|
||||||
std::shared_ptr<spdlog::sinks::test_sink_st> test_sink(new spdlog::sinks::test_sink_st);
|
std::shared_ptr<spdlog::sinks::test_sink_st> test_sink(new spdlog::sinks::test_sink_st);
|
||||||
|
@@ -46,10 +46,8 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]") {
|
|||||||
|
|
||||||
struct custom_daily_file_name_calculator {
|
struct custom_daily_file_name_calculator {
|
||||||
static spdlog::filename_t calc_filename(const spdlog::filename_t &basename, const tm &now_tm) {
|
static spdlog::filename_t calc_filename(const spdlog::filename_t &basename, const tm &now_tm) {
|
||||||
|
return spdlog::fmt_lib::format(SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename,
|
||||||
return spdlog::fmt_lib::format(SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"),
|
now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday);
|
||||||
basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1,
|
|
||||||
now_tm.tm_mday);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -94,14 +94,11 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]") {
|
|||||||
// next logger can rename the first output file.
|
// next logger can rename the first output file.
|
||||||
spdlog::drop(logger->name());
|
spdlog::drop(logger->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 2, true);
|
auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 2, true);
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
logger->info("Test message {}", i);
|
logger->info("Test message {}", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger->flush();
|
logger->flush();
|
||||||
|
|
||||||
require_message_count(ROTATING_LOG, 10);
|
require_message_count(ROTATING_LOG, 10);
|
||||||
|
|
||||||
for (int i = 0; i < 1000; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
@@ -141,3 +138,50 @@ TEST_CASE("rotating_file_logger4", "[rotating_logger]") {
|
|||||||
REQUIRE(get_filesize(ROTATING_LOG) > 0);
|
REQUIRE(get_filesize(ROTATING_LOG) > 0);
|
||||||
REQUIRE(get_filesize(ROTATING_LOG ".1") > 0);
|
REQUIRE(get_filesize(ROTATING_LOG ".1") > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// test changing the max size of the rotating file sink
|
||||||
|
TEST_CASE("rotating_file_logger5", "[rotating_logger]") {
|
||||||
|
prepare_logdir();
|
||||||
|
size_t max_size = 5 * 1024;
|
||||||
|
size_t max_files = 2;
|
||||||
|
spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
|
||||||
|
auto sink =
|
||||||
|
std::make_shared<spdlog::sinks::rotating_file_sink_st>(basename, max_size, max_files);
|
||||||
|
auto logger = std::make_shared<spdlog::logger>("rotating_sink_logger", sink);
|
||||||
|
logger->set_pattern("%v");
|
||||||
|
|
||||||
|
REQUIRE(sink->get_max_size() == max_size);
|
||||||
|
REQUIRE(sink->get_max_files() == max_files);
|
||||||
|
max_size = 7 * 1024;
|
||||||
|
max_files = 3;
|
||||||
|
|
||||||
|
sink->set_max_size(max_size);
|
||||||
|
sink->set_max_files(max_files);
|
||||||
|
REQUIRE(sink->get_max_size() == max_size);
|
||||||
|
REQUIRE(sink->get_max_files() == max_files);
|
||||||
|
|
||||||
|
const auto message = std::string(200, 'x');
|
||||||
|
assert(message.size() < max_size);
|
||||||
|
const auto n_messages = max_files * max_size / message.size();
|
||||||
|
for (size_t i = 0; i < n_messages; ++i) {
|
||||||
|
logger->info(message);
|
||||||
|
}
|
||||||
|
logger.reset(); // force flush and close the file
|
||||||
|
|
||||||
|
// validate that the files were rotated correctly with the new max size and max files
|
||||||
|
for (size_t i = 0; i <= max_files; i++) {
|
||||||
|
// calc filenames
|
||||||
|
// e.g. rotating_log, rotating_log.0 rotating_log.1, rotating_log.2, etc.
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << ROTATING_LOG;
|
||||||
|
if (i > 0) {
|
||||||
|
oss << '.' << i;
|
||||||
|
}
|
||||||
|
const auto filename = oss.str();
|
||||||
|
const auto filesize = get_filesize(filename);
|
||||||
|
REQUIRE(filesize <= max_size);
|
||||||
|
if (i > 0) {
|
||||||
|
REQUIRE(filesize >= max_size - message.size() - 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
#ifdef _WIN32 // to prevent fopen warning on windows
|
#ifdef _WIN32 // to prevent fopen warning on windows
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
#include "test_sink.h"
|
#include "test_sink.h"
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
std::string log_info(const T &what, spdlog::level::level_enum logger_level = spdlog::level::info) {
|
std::string log_info(const T& what, spdlog::level::level_enum logger_level = spdlog::level::info) {
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
|
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
|
||||||
|
|
||||||
@@ -185,14 +185,16 @@ TEST_CASE("utf8 to utf16 conversion using windows api", "[windows utf]") {
|
|||||||
spdlog::details::os::utf8_to_wstrbuf("\xc3\x28", buffer); // Invalid UTF-8 sequence.
|
spdlog::details::os::utf8_to_wstrbuf("\xc3\x28", buffer); // Invalid UTF-8 sequence.
|
||||||
REQUIRE(std::wstring(buffer.data(), buffer.size()) == std::wstring(L"\xfffd("));
|
REQUIRE(std::wstring(buffer.data(), buffer.size()) == std::wstring(L"\xfffd("));
|
||||||
|
|
||||||
spdlog::details::os::utf8_to_wstrbuf("\xe3\x81\xad\xe3\x81\x93", buffer); // "Neko" in hiragana.
|
spdlog::details::os::utf8_to_wstrbuf("\xe3\x81\xad\xe3\x81\x93",
|
||||||
|
buffer); // "Neko" in hiragana.
|
||||||
REQUIRE(std::wstring(buffer.data(), buffer.size()) == std::wstring(L"\x306d\x3053"));
|
REQUIRE(std::wstring(buffer.data(), buffer.size()) == std::wstring(L"\x306d\x3053"));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct auto_closer {
|
struct auto_closer {
|
||||||
FILE* fp = nullptr;
|
FILE* fp = nullptr;
|
||||||
explicit auto_closer(FILE* f) : fp(f) {}
|
explicit auto_closer(FILE* f)
|
||||||
|
: fp(f) {}
|
||||||
auto_closer(const auto_closer&) = delete;
|
auto_closer(const auto_closer&) = delete;
|
||||||
auto_closer& operator=(const auto_closer&) = delete;
|
auto_closer& operator=(const auto_closer&) = delete;
|
||||||
~auto_closer() {
|
~auto_closer() {
|
||||||
@@ -201,10 +203,10 @@ struct auto_closer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
TEST_CASE("os::fwrite_bytes", "[os]") {
|
TEST_CASE("os::fwrite_bytes", "[os]") {
|
||||||
using spdlog::details::os::fwrite_bytes;
|
|
||||||
using spdlog::details::os::create_dir;
|
using spdlog::details::os::create_dir;
|
||||||
|
using spdlog::details::os::fwrite_bytes;
|
||||||
const char* filename = "log_tests/test_fwrite_bytes.txt";
|
const char* filename = "log_tests/test_fwrite_bytes.txt";
|
||||||
const char *msg = "hello";
|
const char* msg = "hello";
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
REQUIRE(create_dir(SPDLOG_FILENAME_T("log_tests")) == true);
|
REQUIRE(create_dir(SPDLOG_FILENAME_T("log_tests")) == true);
|
||||||
{
|
{
|
||||||
|
@@ -23,7 +23,9 @@ static std::string log_to_str(const std::string &msg, const Args &...args) {
|
|||||||
|
|
||||||
// log to str and return it with time
|
// log to str and return it with time
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
static std::string log_to_str_with_time(spdlog::log_clock::time_point log_time, const std::string &msg, const Args &...args) {
|
static std::string log_to_str_with_time(spdlog::log_clock::time_point log_time,
|
||||||
|
const std::string &msg,
|
||||||
|
const Args &...args) {
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
|
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
|
||||||
spdlog::logger oss_logger("pattern_tester", oss_sink);
|
spdlog::logger oss_logger("pattern_tester", oss_sink);
|
||||||
@@ -80,8 +82,8 @@ TEST_CASE("GMT offset ", "[pattern_formatter]") {
|
|||||||
const auto now = std::chrono::system_clock::now();
|
const auto now = std::chrono::system_clock::now();
|
||||||
const auto yesterday = now - 24h;
|
const auto yesterday = now - 24h;
|
||||||
|
|
||||||
REQUIRE(log_to_str_with_time(yesterday, "Some message", "%z", spdlog::pattern_time_type::utc, "\n") ==
|
REQUIRE(log_to_str_with_time(yesterday, "Some message", "%z", spdlog::pattern_time_type::utc,
|
||||||
"+00:00\n");
|
"\n") == "+00:00\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("color range test1", "[pattern_formatter]") {
|
TEST_CASE("color range test1", "[pattern_formatter]") {
|
||||||
|
@@ -25,6 +25,19 @@ TEST_CASE("explicit register", "[registry]") {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
TEST_CASE("register_or_replace", "[registry]") {
|
||||||
|
spdlog::drop_all();
|
||||||
|
auto logger1 = std::make_shared<spdlog::logger>(
|
||||||
|
tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
|
||||||
|
spdlog::register_logger(logger1);
|
||||||
|
REQUIRE(spdlog::get(tested_logger_name) == logger1);
|
||||||
|
|
||||||
|
auto logger2 = std::make_shared<spdlog::logger>(
|
||||||
|
tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
|
||||||
|
spdlog::register_or_replace(logger2);
|
||||||
|
REQUIRE(spdlog::get(tested_logger_name) == logger2);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("apply_all", "[registry]") {
|
TEST_CASE("apply_all", "[registry]") {
|
||||||
spdlog::drop_all();
|
spdlog::drop_all();
|
||||||
auto logger = std::make_shared<spdlog::logger>(tested_logger_name,
|
auto logger = std::make_shared<spdlog::logger>(tested_logger_name,
|
||||||
|
@@ -50,9 +50,8 @@ void require_message_count(const std::string &filename, const std::size_t messag
|
|||||||
std::size_t get_filesize(const std::string &filename) {
|
std::size_t get_filesize(const std::string &filename) {
|
||||||
std::ifstream ifs(filename, std::ifstream::ate | std::ifstream::binary);
|
std::ifstream ifs(filename, std::ifstream::ate | std::ifstream::binary);
|
||||||
if (!ifs) {
|
if (!ifs) {
|
||||||
throw std::runtime_error("Failed open file ");
|
throw std::runtime_error("Failed open file " + filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
return static_cast<std::size_t>(ifs.tellg());
|
return static_cast<std::size_t>(ifs.tellg());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user