mirror of
https://github.com/gabime/spdlog.git
synced 2026-01-02 09:57:55 +08:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de0dbfa359 | ||
|
|
f93459579f | ||
|
|
2b81c40b90 | ||
|
|
233e97c5e4 | ||
|
|
fc1ce48dc7 | ||
|
|
fd5562eebe | ||
|
|
0695d9cb5f | ||
|
|
456b24134d | ||
|
|
f8ba24afee | ||
|
|
eebb921c9f | ||
|
|
e17ee87f38 | ||
|
|
18e3f07f7d | ||
|
|
9ce39a470f | ||
|
|
23572369fc | ||
|
|
01b350de96 | ||
|
|
365e470a32 | ||
|
|
a42b40656c | ||
|
|
40160f2a57 | ||
|
|
90b33b1552 | ||
|
|
5567ed01e5 | ||
|
|
cbe9448650 | ||
|
|
5b345534dc | ||
|
|
c8dc318fb3 | ||
|
|
23cb1a1080 | ||
|
|
21cf8d7d3c | ||
|
|
3cf4d34094 | ||
|
|
16d78ae5db | ||
|
|
62b4b7af83 | ||
|
|
9799ecac6a | ||
|
|
dccb766095 | ||
|
|
c97983a91c | ||
|
|
680fb07fd5 | ||
|
|
cfd0ea197c | ||
|
|
48d4ed9bc0 | ||
|
|
3bed78356e | ||
|
|
8923922f30 | ||
|
|
7542e42e4f | ||
|
|
7a9b23e4f4 | ||
|
|
47253ba2a1 | ||
|
|
69b54dd9e4 | ||
|
|
36138617fc | ||
|
|
231ca50700 | ||
|
|
c7613f3e91 | ||
|
|
05d5546eb1 | ||
|
|
cefe67726e | ||
|
|
1ac2dcc537 | ||
|
|
3a68eecb28 | ||
|
|
54a8259b42 | ||
|
|
32b6f1619f | ||
|
|
99b8c5d379 | ||
|
|
5deb7c55e1 | ||
|
|
9cd25dd216 |
158
CMakeLists.txt
158
CMakeLists.txt
@@ -18,35 +18,35 @@ include(GNUInstallDirs)
|
|||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Set default build to release
|
# Set default build to release
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
if (NOT CMAKE_BUILD_TYPE)
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
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 (NOT CMAKE_CXX_STANDARD)
|
if(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")
|
if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS")
|
||||||
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)
|
||||||
|
|
||||||
@@ -79,38 +79,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()
|
||||||
|
|
||||||
# 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)
|
||||||
endif ()
|
endif()
|
||||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
option(SPDLOG_CLOCK_COARSE
|
option(SPDLOG_CLOCK_COARSE
|
||||||
"Use the much faster (but much less accurate) CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
|
"Use the much faster (but much less accurate) CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
|
||||||
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)
|
||||||
option(SPDLOG_NO_TLS "prevent spdlog from using thread local storage" OFF)
|
option(SPDLOG_NO_TLS "prevent spdlog from using thread local storage" OFF)
|
||||||
option(
|
option(
|
||||||
SPDLOG_NO_ATOMIC_LEVELS
|
SPDLOG_NO_ATOMIC_LEVELS
|
||||||
"prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently"
|
"prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently"
|
||||||
OFF)
|
OFF)
|
||||||
|
|
||||||
# clang-tidy
|
# clang-tidy
|
||||||
if (${CMAKE_VERSION} VERSION_GREATER "3.5")
|
if(${CMAKE_VERSION} VERSION_GREATER "3.5")
|
||||||
option(SPDLOG_TIDY "run clang-tidy" OFF)
|
option(SPDLOG_TIDY "run clang-tidy" OFF)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
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()
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
|
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
|
||||||
@@ -119,42 +119,42 @@ 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_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
||||||
list(APPEND SPDLOG_SRCS src/fmt.cpp)
|
list(APPEND SPDLOG_SRCS src/fmt.cpp)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
if (SPDLOG_BUILD_SHARED)
|
if(SPDLOG_BUILD_SHARED)
|
||||||
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 (MSVC)
|
if(MSVC)
|
||||||
target_compile_options(spdlog PUBLIC /wd4251 /wd4275)
|
target_compile_options(spdlog PUBLIC /wd4251 /wd4275)
|
||||||
endif ()
|
endif()
|
||||||
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 PRIVATE FMT_EXPORT PUBLIC FMT_SHARED)
|
target_compile_definitions(spdlog PRIVATE FMT_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)
|
||||||
|
|
||||||
target_compile_definitions(spdlog PUBLIC SPDLOG_COMPILED_LIB)
|
target_compile_definitions(spdlog PUBLIC SPDLOG_COMPILED_LIB)
|
||||||
target_include_directories(spdlog PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
target_include_directories(spdlog PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
||||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
|
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
|
||||||
target_link_libraries(spdlog PUBLIC Threads::Threads)
|
target_link_libraries(spdlog PUBLIC Threads::Threads)
|
||||||
spdlog_enable_warnings(spdlog)
|
spdlog_enable_warnings(spdlog)
|
||||||
|
|
||||||
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION ${SPDLOG_VERSION_MAJOR})
|
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION ${SPDLOG_VERSION_MAJOR})
|
||||||
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()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Header only version
|
# Header only version
|
||||||
@@ -163,82 +163,82 @@ add_library(spdlog_header_only INTERFACE)
|
|||||||
add_library(spdlog::spdlog_header_only ALIAS spdlog_header_only)
|
add_library(spdlog::spdlog_header_only ALIAS spdlog_header_only)
|
||||||
|
|
||||||
target_include_directories(spdlog_header_only INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
target_include_directories(spdlog_header_only INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
||||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
|
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
|
||||||
target_link_libraries(spdlog_header_only INTERFACE Threads::Threads)
|
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 5.3.0 REQUIRED)
|
find_package(fmt 5.3.0 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-nly
|
# use external fmt-header-nly
|
||||||
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()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# 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})
|
||||||
foreach (
|
foreach(
|
||||||
SPDLOG_OPTION
|
SPDLOG_OPTION
|
||||||
SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
SPDLOG_WCHAR_FILENAMES
|
SPDLOG_WCHAR_FILENAMES
|
||||||
SPDLOG_NO_EXCEPTIONS
|
SPDLOG_NO_EXCEPTIONS
|
||||||
SPDLOG_CLOCK_COARSE
|
SPDLOG_CLOCK_COARSE
|
||||||
SPDLOG_PREVENT_CHILD_FD
|
SPDLOG_PREVENT_CHILD_FD
|
||||||
SPDLOG_NO_THREAD_ID
|
SPDLOG_NO_THREAD_ID
|
||||||
SPDLOG_NO_TLS
|
SPDLOG_NO_TLS
|
||||||
SPDLOG_NO_ATOMIC_LEVELS)
|
SPDLOG_NO_ATOMIC_LEVELS)
|
||||||
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 (SPDLOG_NO_EXCEPTIONS AND NOT MSVC)
|
if(SPDLOG_NO_EXCEPTIONS AND NOT MSVC)
|
||||||
target_compile_options(spdlog PRIVATE -fno-exceptions)
|
target_compile_options(spdlog PRIVATE -fno-exceptions)
|
||||||
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")
|
||||||
@@ -253,16 +253,16 @@ if (SPDLOG_INSTALL)
|
|||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" PATTERN "fmt/bundled" EXCLUDE)
|
install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" PATTERN "fmt/bundled" EXCLUDE)
|
||||||
install(
|
install(
|
||||||
TARGETS spdlog spdlog_header_only
|
TARGETS spdlog spdlog_header_only
|
||||||
EXPORT spdlog
|
EXPORT spdlog
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
|
||||||
if (NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
if(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
|
||||||
@@ -288,4 +288,4 @@ if (SPDLOG_INSTALL)
|
|||||||
# Support creation of installable packages
|
# Support creation of installable packages
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
include(cmake/spdlogCPack.cmake)
|
include(cmake/spdlogCPack.cmake)
|
||||||
endif ()
|
endif()
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ void stopwatch_example()
|
|||||||
// {:s} - don't separate each byte with space.
|
// {:s} - don't separate each byte with space.
|
||||||
// {:p} - don't print the position on each line start.
|
// {:p} - don't print the position on each line start.
|
||||||
// {:n} - don't split the output to lines.
|
// {:n} - don't split the output to lines.
|
||||||
|
// {:a} - show ASCII if :n is not set.
|
||||||
|
|
||||||
#include "spdlog/fmt/bin_to_hex.h"
|
#include "spdlog/fmt/bin_to_hex.h"
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,24 @@ if(NOT TARGET spdlog)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
find_package(benchmark CONFIG REQUIRED)
|
find_package(benchmark CONFIG)
|
||||||
|
if (NOT benchmark_FOUND)
|
||||||
|
message(STATUS "Using CMake Version ${CMAKE_VERSION}")
|
||||||
|
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.11.0")
|
||||||
|
# User can fetch googlebenchmark
|
||||||
|
message(STATUS "Downloading GoogleBenchmark")
|
||||||
|
include(FetchContent)
|
||||||
|
set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE INTERNAL "")
|
||||||
|
# Do not build and run googlebenchmark tests
|
||||||
|
FetchContent_Declare(googlebenchmark
|
||||||
|
GIT_REPOSITORY https://github.com/google/benchmark.git
|
||||||
|
GIT_TAG v1.5.2)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(googlebenchmark)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "GoogleBenchmark is missing. Use CMake >= 3.11 or download it")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(bench bench.cpp)
|
add_executable(bench bench.cpp)
|
||||||
spdlog_enable_warnings(bench)
|
spdlog_enable_warnings(bench)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
|
|||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable : 4996) // disable fopen warning under msvc
|
#pragma warning(disable : 4996) // disable fopen warning under msvc
|
||||||
#endif // _MSC_VER
|
#endif // _MSC_VER
|
||||||
|
|
||||||
int count_lines(const char *filename)
|
int count_lines(const char *filename)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,23 +18,37 @@ set(CPACK_PACKAGE_RELOCATABLE ON CACHE BOOL "Build relocatable package")
|
|||||||
|
|
||||||
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
|
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
|
||||||
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
|
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_SECTION "libs")
|
||||||
set(CPACK_RPM_PACKAGE_URL ${CPACK_PROJECT_URL})
|
set(CPACK_RPM_PACKAGE_URL ${CPACK_PROJECT_URL})
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${CPACK_PROJECT_URL})
|
||||||
set(CPACK_RPM_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
|
set(CPACK_RPM_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
|
||||||
|
|
||||||
if(CPACK_PACKAGE_NAME)
|
if(CPACK_PACKAGE_NAME)
|
||||||
set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
|
set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||||
|
set(CPACK_DEBIAN_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||||
else()
|
else()
|
||||||
set(CPACK_RPM_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
|
set(CPACK_RPM_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||||
|
set(CPACK_DEBIAN_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||||
|
set(CPACK_RPM_PACKAGE_NAME "${PROJECT_NAME}")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CPACK_RPM_PACKAGE_RELEASE)
|
if(CPACK_RPM_PACKAGE_RELEASE)
|
||||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}-${CPACK_RPM_PACKAGE_RELEASE}")
|
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}-${CPACK_RPM_PACKAGE_RELEASE}")
|
||||||
endif()
|
endif()
|
||||||
|
if(CPACK_DEBIAN_PACKAGE_RELEASE)
|
||||||
|
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}-${CPACK_DEBIAN_PACKAGE_RELEASE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(CPACK_RPM_PACKAGE_ARCHITECTURE)
|
if(CPACK_RPM_PACKAGE_ARCHITECTURE)
|
||||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.${CPACK_RPM_PACKAGE_ARCHITECTURE}")
|
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.${CPACK_RPM_PACKAGE_ARCHITECTURE}")
|
||||||
endif()
|
endif()
|
||||||
|
if(CPACK_DEBIAN_PACKAGE_ARCHITECTURE)
|
||||||
|
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
|
||||||
|
endif()
|
||||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm")
|
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm")
|
||||||
|
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.deb")
|
||||||
|
|
||||||
if(NOT CPACK_PACKAGE_RELOCATABLE)
|
if(NOT CPACK_PACKAGE_RELOCATABLE)
|
||||||
# Depend on pkgconfig rpm to create the system pkgconfig folder
|
# Depend on pkgconfig rpm to create the system pkgconfig folder
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
|
|||||||
struct async_factory_impl
|
struct async_factory_impl
|
||||||
{
|
{
|
||||||
template<typename Sink, typename... SinkArgs>
|
template<typename Sink, typename... SinkArgs>
|
||||||
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
|
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&...args)
|
||||||
{
|
{
|
||||||
auto ®istry_inst = details::registry::instance();
|
auto ®istry_inst = details::registry::instance();
|
||||||
|
|
||||||
@@ -61,13 +61,13 @@ using async_factory = async_factory_impl<async_overflow_policy::block>;
|
|||||||
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
||||||
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
template<typename Sink, typename... SinkArgs>
|
||||||
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
|
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&...sink_args)
|
||||||
{
|
{
|
||||||
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
template<typename Sink, typename... SinkArgs>
|
||||||
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
|
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&...sink_args)
|
||||||
{
|
{
|
||||||
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,7 @@ inline void load_argv_levels(int argc, const char **argv)
|
|||||||
if (arg.find(spdlog_level_prefix) == 0)
|
if (arg.find(spdlog_level_prefix) == 0)
|
||||||
{
|
{
|
||||||
auto levels_string = arg.substr(spdlog_level_prefix.size());
|
auto levels_string = arg.substr(spdlog_level_prefix.size());
|
||||||
auto levels = helpers::extract_levels(levels_string);
|
helpers::load_levels(levels_string);
|
||||||
details::registry::instance().update_levels(std::move(levels));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
// export SPDLOG_LEVEL=debug
|
// export SPDLOG_LEVEL=debug
|
||||||
//
|
//
|
||||||
// turn off all logging except for logger1:
|
// turn off all logging except for logger1:
|
||||||
// export SPDLOG_LEVEL="off,logger1=debug"
|
// export SPDLOG_LEVEL="*=off,logger1=debug"
|
||||||
//
|
//
|
||||||
|
|
||||||
// turn off all logging except for logger1 and logger2:
|
// turn off all logging except for logger1 and logger2:
|
||||||
@@ -28,8 +28,10 @@ namespace cfg {
|
|||||||
inline void load_env_levels()
|
inline void load_env_levels()
|
||||||
{
|
{
|
||||||
auto env_val = details::os::getenv("SPDLOG_LEVEL");
|
auto env_val = details::os::getenv("SPDLOG_LEVEL");
|
||||||
auto levels = helpers::extract_levels(env_val);
|
if (!env_val.empty())
|
||||||
details::registry::instance().update_levels(std::move(levels));
|
{
|
||||||
|
helpers::load_levels(env_val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace cfg
|
} // namespace cfg
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <spdlog/details/os.h>
|
#include <spdlog/details/os.h>
|
||||||
#include <spdlog/details/registry.h>
|
#include <spdlog/details/registry.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -78,24 +79,40 @@ inline std::unordered_map<std::string, std::string> extract_key_vals_(const std:
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
SPDLOG_INLINE log_levels extract_levels(const std::string &input)
|
SPDLOG_INLINE void load_levels(const std::string &input)
|
||||||
{
|
{
|
||||||
|
if (input.empty() || input.size() > 512)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto key_vals = extract_key_vals_(input);
|
auto key_vals = extract_key_vals_(input);
|
||||||
log_levels rv;
|
std::unordered_map<std::string, level::level_enum> levels;
|
||||||
|
level::level_enum global_level = level::info;
|
||||||
|
bool global_level_found = false;
|
||||||
|
|
||||||
for (auto &name_level : key_vals)
|
for (auto &name_level : key_vals)
|
||||||
{
|
{
|
||||||
auto &logger_name = name_level.first;
|
auto &logger_name = name_level.first;
|
||||||
auto level_name = to_lower_(name_level.second);
|
auto level_name = to_lower_(name_level.second);
|
||||||
auto level = level::from_str(level_name);
|
auto level = level::from_str(level_name);
|
||||||
// fallback to "info" if unrecognized level name
|
// ignore unrecognized level names
|
||||||
if (level == level::off && level_name != "off")
|
if (level == level::off && level_name != "off")
|
||||||
{
|
{
|
||||||
level = level::info;
|
continue;
|
||||||
|
}
|
||||||
|
if (logger_name.empty()) // no logger name indicate global level
|
||||||
|
{
|
||||||
|
global_level_found = true;
|
||||||
|
global_level = level;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
levels[logger_name] = level;
|
||||||
}
|
}
|
||||||
rv.set(logger_name, level);
|
|
||||||
}
|
}
|
||||||
return rv;
|
|
||||||
|
details::registry::instance().set_levels(std::move(levels), global_level_found ? &global_level : nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace helpers
|
} // namespace helpers
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <spdlog/cfg/log_levels.h>
|
#include <spdlog/common.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace cfg {
|
namespace cfg {
|
||||||
@@ -17,7 +18,7 @@ namespace helpers {
|
|||||||
// turn off all logging except for logger1: "off,logger1=debug"
|
// turn off all logging except for logger1: "off,logger1=debug"
|
||||||
// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
|
// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
|
||||||
//
|
//
|
||||||
SPDLOG_API log_levels extract_levels(const std::string &txt);
|
SPDLOG_API void load_levels(const std::string &txt);
|
||||||
} // namespace helpers
|
} // namespace helpers
|
||||||
|
|
||||||
} // namespace cfg
|
} // namespace cfg
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <string>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace cfg {
|
|
||||||
class log_levels
|
|
||||||
{
|
|
||||||
std::unordered_map<std::string, spdlog::level::level_enum> levels_;
|
|
||||||
spdlog::level::level_enum default_level_ = level::info;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void set(const std::string &logger_name, level::level_enum lvl)
|
|
||||||
{
|
|
||||||
if (logger_name.empty())
|
|
||||||
{
|
|
||||||
default_level_ = lvl;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
levels_[logger_name] = lvl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_default(level::level_enum lvl)
|
|
||||||
{
|
|
||||||
default_level_ = lvl;
|
|
||||||
}
|
|
||||||
|
|
||||||
level::level_enum get(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
auto it = levels_.find(logger_name);
|
|
||||||
return it != levels_.end() ? it->second : default_level_;
|
|
||||||
}
|
|
||||||
|
|
||||||
level::level_enum default_level()
|
|
||||||
{
|
|
||||||
return default_level_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace cfg
|
|
||||||
} // namespace spdlog
|
|
||||||
@@ -167,7 +167,6 @@ SPDLOG_API string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOE
|
|||||||
SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||||
SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
|
SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
using level_hasher = std::hash<int>;
|
|
||||||
} // namespace level
|
} // namespace level
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -232,7 +231,7 @@ namespace details {
|
|||||||
using std::make_unique;
|
using std::make_unique;
|
||||||
#else
|
#else
|
||||||
template<typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
std::unique_ptr<T> make_unique(Args &&... args)
|
std::unique_ptr<T> make_unique(Args &&...args)
|
||||||
{
|
{
|
||||||
static_assert(!std::is_array<T>::value, "arrays not supported");
|
static_assert(!std::is_array<T>::value, "arrays not supported");
|
||||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||||
|
|||||||
@@ -110,6 +110,12 @@ public:
|
|||||||
return q_.overrun_counter();
|
return q_.overrun_counter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t size()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
return q_.size();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::mutex queue_mutex_;
|
std::mutex queue_mutex_;
|
||||||
std::condition_variable push_cv_;
|
std::condition_variable push_cv_;
|
||||||
|
|||||||
@@ -67,7 +67,11 @@ SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logge
|
|||||||
new_logger->set_error_handler(err_handler_);
|
new_logger->set_error_handler(err_handler_);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_logger->set_level(levels_.get(new_logger->name()));
|
// set new level according to previously configured level or default level
|
||||||
|
auto it = log_levels_.find(new_logger->name());
|
||||||
|
auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
|
||||||
|
new_logger->set_level(new_level);
|
||||||
|
|
||||||
new_logger->flush_on(flush_level_);
|
new_logger->flush_on(flush_level_);
|
||||||
|
|
||||||
if (backtrace_n_messages_ > 0)
|
if (backtrace_n_messages_ > 0)
|
||||||
@@ -171,7 +175,7 @@ SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
|
|||||||
{
|
{
|
||||||
l.second->set_level(log_level);
|
l.second->set_level(log_level);
|
||||||
}
|
}
|
||||||
levels_.set_default(log_level);
|
global_log_level_ = log_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
|
SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
|
||||||
@@ -263,14 +267,24 @@ SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registrat
|
|||||||
automatic_registration_ = automatic_registration;
|
automatic_registration_ = automatic_registration;
|
||||||
}
|
}
|
||||||
|
|
||||||
SPDLOG_INLINE void registry::update_levels(cfg::log_levels levels)
|
SPDLOG_INLINE void registry::set_levels(log_levels levels, level::level_enum *global_level)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
levels_ = std::move(levels);
|
log_levels_ = std::move(levels);
|
||||||
for (auto &l : loggers_)
|
auto global_level_requested = global_level != nullptr;
|
||||||
|
global_log_level_ = global_level_requested ? *global_level : global_log_level_;
|
||||||
|
|
||||||
|
for (auto &logger : loggers_)
|
||||||
{
|
{
|
||||||
auto &logger = l.second;
|
auto logger_entry = log_levels_.find(logger.first);
|
||||||
logger->set_level(levels_.get(logger->name()));
|
if (logger_entry != log_levels_.end())
|
||||||
|
{
|
||||||
|
logger.second->set_level(logger_entry->second);
|
||||||
|
}
|
||||||
|
else if (global_level_requested)
|
||||||
|
{
|
||||||
|
logger.second->set_level(*global_level);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
// This class is thread safe
|
// This class is thread safe
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
#include <spdlog/common.h>
|
||||||
#include <spdlog/cfg/log_levels.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -28,6 +27,7 @@ class periodic_worker;
|
|||||||
class SPDLOG_API registry
|
class SPDLOG_API registry
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using log_levels = std::unordered_map<std::string, level::level_enum>;
|
||||||
registry(const registry &) = delete;
|
registry(const registry &) = delete;
|
||||||
registry &operator=(const registry &) = delete;
|
registry &operator=(const registry &) = delete;
|
||||||
|
|
||||||
@@ -80,7 +80,8 @@ public:
|
|||||||
|
|
||||||
void set_automatic_registration(bool automatic_registration);
|
void set_automatic_registration(bool automatic_registration);
|
||||||
|
|
||||||
void update_levels(cfg::log_levels levels);
|
// set levels for all existing/future loggers. global_level can be null if should not set.
|
||||||
|
void set_levels(log_levels levels, level::level_enum *global_level);
|
||||||
|
|
||||||
static registry &instance();
|
static registry &instance();
|
||||||
|
|
||||||
@@ -90,11 +91,13 @@ 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);
|
||||||
|
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_;
|
||||||
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
|
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
|
||||||
cfg::log_levels levels_;
|
log_levels log_levels_;
|
||||||
std::unique_ptr<formatter> formatter_;
|
std::unique_ptr<formatter> formatter_;
|
||||||
|
spdlog::level::level_enum global_log_level_ = level::info;
|
||||||
level::level_enum flush_level_ = level::off;
|
level::level_enum flush_level_ = level::off;
|
||||||
void (*err_handler_)(const std::string &msg);
|
void (*err_handler_)(const std::string &msg);
|
||||||
std::shared_ptr<thread_pool> tp_;
|
std::shared_ptr<thread_pool> tp_;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ class logger;
|
|||||||
struct synchronous_factory
|
struct synchronous_factory
|
||||||
{
|
{
|
||||||
template<typename Sink, typename... SinkArgs>
|
template<typename Sink, typename... SinkArgs>
|
||||||
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
|
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...args)
|
||||||
{
|
{
|
||||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||||
auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));
|
auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));
|
||||||
|
|||||||
@@ -68,6 +68,11 @@ size_t SPDLOG_INLINE thread_pool::overrun_counter()
|
|||||||
return q_.overrun_counter();
|
return q_.overrun_counter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t SPDLOG_INLINE thread_pool::queue_size()
|
||||||
|
{
|
||||||
|
return q_.size();
|
||||||
|
}
|
||||||
|
|
||||||
void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
|
void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
|
||||||
{
|
{
|
||||||
if (overflow_policy == async_overflow_policy::block)
|
if (overflow_policy == async_overflow_policy::block)
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ public:
|
|||||||
void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
|
void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
|
||||||
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
|
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
|
||||||
size_t overrun_counter();
|
size_t overrun_counter();
|
||||||
|
size_t queue_size();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
q_type q_;
|
q_type q_;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
//
|
//
|
||||||
// Support for logging binary data as hex
|
// Support for logging binary data as hex
|
||||||
// format flags:
|
// format flags, any combination of the followng:
|
||||||
// {:X} - print in uppercase.
|
// {:X} - print in uppercase.
|
||||||
// {:s} - don't separate each byte with space.
|
// {:s} - don't separate each byte with space.
|
||||||
// {:p} - don't print the position on each line start.
|
// {:p} - don't print the position on each line start.
|
||||||
|
|||||||
@@ -72,43 +72,27 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
|||||||
static_assert(F::is_integer, "From must be integral");
|
static_assert(F::is_integer, "From must be integral");
|
||||||
static_assert(T::is_integer, "To must be integral");
|
static_assert(T::is_integer, "To must be integral");
|
||||||
|
|
||||||
if (F::is_signed && !T::is_signed) {
|
if (detail::const_check(F::is_signed && !T::is_signed)) {
|
||||||
// From may be negative, not allowed!
|
// From may be negative, not allowed!
|
||||||
if (fmt::detail::is_negative(from)) {
|
if (fmt::detail::is_negative(from)) {
|
||||||
ec = 1;
|
ec = 1;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// From is positive. Can it always fit in To?
|
// From is positive. Can it always fit in To?
|
||||||
if (F::digits <= T::digits) {
|
if (F::digits > T::digits &&
|
||||||
// yes, From always fits in To.
|
from > static_cast<From>(detail::max_value<To>())) {
|
||||||
} else {
|
ec = 1;
|
||||||
// from may not fit in To, we have to do a dynamic check
|
return {};
|
||||||
if (from > static_cast<From>((T::max)())) {
|
|
||||||
ec = 1;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!F::is_signed && T::is_signed) {
|
if (!F::is_signed && T::is_signed && F::digits >= T::digits &&
|
||||||
// can from be held in To?
|
from > static_cast<From>(detail::max_value<To>())) {
|
||||||
if (F::digits < T::digits) {
|
ec = 1;
|
||||||
// yes, From always fits in To.
|
return {};
|
||||||
} else {
|
|
||||||
// from may not fit in To, we have to do a dynamic check
|
|
||||||
if (from > static_cast<From>((T::max)())) {
|
|
||||||
// outside range.
|
|
||||||
ec = 1;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return static_cast<To>(from); // Lossless conversion.
|
||||||
// reaching here means all is ok for lossless conversion.
|
}
|
||||||
return static_cast<To>(from);
|
|
||||||
|
|
||||||
} // function
|
|
||||||
|
|
||||||
template <typename To, typename From,
|
template <typename To, typename From,
|
||||||
FMT_ENABLE_IF(std::is_same<From, To>::value)>
|
FMT_ENABLE_IF(std::is_same<From, To>::value)>
|
||||||
@@ -190,11 +174,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|||||||
// safe conversion to IntermediateRep
|
// safe conversion to IntermediateRep
|
||||||
IntermediateRep count =
|
IntermediateRep count =
|
||||||
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
|
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
|
||||||
if (ec) {
|
if (ec) return {};
|
||||||
return {};
|
|
||||||
}
|
|
||||||
// multiply with Factor::num without overflow or underflow
|
// multiply with Factor::num without overflow or underflow
|
||||||
if (Factor::num != 1) {
|
if (detail::const_check(Factor::num != 1)) {
|
||||||
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
|
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
|
||||||
if (count > max1) {
|
if (count > max1) {
|
||||||
ec = 1;
|
ec = 1;
|
||||||
@@ -209,17 +191,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|||||||
count *= Factor::num;
|
count *= Factor::num;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this can't go wrong, right? den>0 is checked earlier.
|
if (detail::const_check(Factor::den != 1)) count /= Factor::den;
|
||||||
if (Factor::den != 1) {
|
auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
|
||||||
count /= Factor::den;
|
return ec ? To() : To(tocount);
|
||||||
}
|
|
||||||
// convert to the to type, safely
|
|
||||||
using ToRep = typename To::rep;
|
|
||||||
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
|
|
||||||
if (ec) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return To{tocount};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -351,6 +325,11 @@ inline std::tm localtime(std::time_t time) {
|
|||||||
return lt.tm_;
|
return lt.tm_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::tm localtime(
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> time_point) {
|
||||||
|
return localtime(std::chrono::system_clock::to_time_t(time_point));
|
||||||
|
}
|
||||||
|
|
||||||
// Thread-safe replacement for std::gmtime
|
// Thread-safe replacement for std::gmtime
|
||||||
inline std::tm gmtime(std::time_t time) {
|
inline std::tm gmtime(std::time_t time) {
|
||||||
struct dispatcher {
|
struct dispatcher {
|
||||||
@@ -387,6 +366,11 @@ inline std::tm gmtime(std::time_t time) {
|
|||||||
return gt.tm_;
|
return gt.tm_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::tm gmtime(
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> time_point) {
|
||||||
|
return gmtime(std::chrono::system_clock::to_time_t(time_point));
|
||||||
|
}
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
inline size_t strftime(char* str, size_t count, const char* format,
|
inline size_t strftime(char* str, size_t count, const char* format,
|
||||||
const std::tm* time) {
|
const std::tm* time) {
|
||||||
@@ -399,6 +383,17 @@ inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
|
|||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct formatter<std::chrono::time_point<std::chrono::system_clock>, Char>
|
||||||
|
: formatter<std::tm, Char> {
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(std::chrono::time_point<std::chrono::system_clock> val,
|
||||||
|
FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
|
std::tm time = localtime(val);
|
||||||
|
return formatter<std::tm, Char>::format(time, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Char> struct formatter<std::tm, Char> {
|
template <typename Char> struct formatter<std::tm, Char> {
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
|||||||
@@ -463,16 +463,16 @@ template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
|
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||||
const char* begin = data::reset_color;
|
const char* begin = data::reset_color;
|
||||||
const char* end = begin + sizeof(data::reset_color) - 1;
|
const char* end = begin + sizeof(data::reset_color) - 1;
|
||||||
buffer.append(begin, end);
|
buffer.append(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
void vformat_to(buffer<Char>& buf, const text_style& ts,
|
||||||
basic_string_view<Char> format_str,
|
basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
bool has_style = false;
|
bool has_style = false;
|
||||||
if (ts.has_emphasis()) {
|
if (ts.has_emphasis()) {
|
||||||
has_style = true;
|
has_style = true;
|
||||||
@@ -496,7 +496,7 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
|||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buf;
|
basic_memory_buffer<Char> buf;
|
||||||
detail::vformat_to(buf, ts, to_string_view(format), args);
|
detail::vformat_to(buf, ts, to_string_view(format), args);
|
||||||
buf.push_back(Char(0));
|
buf.push_back(Char(0));
|
||||||
@@ -504,20 +504,22 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
\rst
|
||||||
Formats a string and prints it to the specified file stream using ANSI
|
Formats a string and prints it to the specified file stream using ANSI
|
||||||
escape sequences to specify text formatting.
|
escape sequences to specify text formatting.
|
||||||
Example:
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||||
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
detail::check_format_string<Args...>(format_str);
|
vprint(f, ts, format_str,
|
||||||
using context = buffer_context<char_t<S>>;
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
format_arg_store<context, Args...> as{args...};
|
|
||||||
vprint(f, ts, format_str, basic_format_args<context>(as));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -558,7 +560,42 @@ template <typename S, typename... Args, typename Char = char_t<S>>
|
|||||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return vformat(ts, to_string_view(format_str),
|
return vformat(ts, to_string_view(format_str),
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string with the given text_style and writes the output to ``out``.
|
||||||
|
*/
|
||||||
|
template <typename OutputIt, typename Char,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
||||||
|
OutputIt vformat_to(
|
||||||
|
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
||||||
|
detail::vformat_to(buf, ts, format_str, args);
|
||||||
|
return detail::get_iterator(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments with the given text_style, writes the result to the output
|
||||||
|
iterator ``out`` and returns the iterator past the end of the output range.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
std::vector<char> out;
|
||||||
|
fmt::format_to(std::back_inserter(out),
|
||||||
|
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
|
||||||
|
detail::is_string<S>::value>
|
||||||
|
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
|
||||||
|
Args&&... args) ->
|
||||||
|
typename std::enable_if<enable, OutputIt>::type {
|
||||||
|
return vformat_to(out, ts, to_string_view(format_str),
|
||||||
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|||||||
@@ -368,7 +368,8 @@ template <typename... Args> struct type_list {};
|
|||||||
|
|
||||||
// Returns a reference to the argument at index N from [first, rest...].
|
// Returns a reference to the argument at index N from [first, rest...].
|
||||||
template <int N, typename T, typename... Args>
|
template <int N, typename T, typename... Args>
|
||||||
constexpr const auto& get(const T& first, const Args&... rest) {
|
constexpr const auto& get([[maybe_unused]] const T& first,
|
||||||
|
[[maybe_unused]] const Args&... rest) {
|
||||||
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
||||||
if constexpr (N == 0)
|
if constexpr (N == 0)
|
||||||
return first;
|
return first;
|
||||||
@@ -406,6 +407,19 @@ constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
|||||||
return {{&s[pos], size}};
|
return {{&s[pos], size}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char> struct code_unit {
|
||||||
|
Char value;
|
||||||
|
using char_type = Char;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename... Args>
|
||||||
|
OutputIt format(OutputIt out, const Args&...) const {
|
||||||
|
return write<Char>(out, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct is_compiled_format<code_unit<Char>> : std::true_type {};
|
||||||
|
|
||||||
// A replacement field that refers to argument N.
|
// A replacement field that refers to argument N.
|
||||||
template <typename Char, typename T, int N> struct field {
|
template <typename Char, typename T, int N> struct field {
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
@@ -430,7 +444,9 @@ template <typename Char, typename T, int N> struct spec_field {
|
|||||||
OutputIt format(OutputIt out, const Args&... args) const {
|
OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
// This ensures that the argument type is convertile to `const T&`.
|
// This ensures that the argument type is convertile to `const T&`.
|
||||||
const T& arg = get<N>(args...);
|
const T& arg = get<N>(args...);
|
||||||
basic_format_context<OutputIt, Char> ctx(out, {});
|
const auto& vargs =
|
||||||
|
make_format_args<basic_format_context<OutputIt, Char>>(args...);
|
||||||
|
basic_format_context<OutputIt, Char> ctx(out, vargs);
|
||||||
return fmt.format(arg, ctx);
|
return fmt.format(arg, ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -489,16 +505,17 @@ constexpr auto parse_tail(T head, S format_str) {
|
|||||||
template <typename T, typename Char> struct parse_specs_result {
|
template <typename T, typename Char> struct parse_specs_result {
|
||||||
formatter<T, Char> fmt;
|
formatter<T, Char> fmt;
|
||||||
size_t end;
|
size_t end;
|
||||||
|
int next_arg_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||||
size_t pos) {
|
size_t pos, int arg_id) {
|
||||||
str.remove_prefix(pos);
|
str.remove_prefix(pos);
|
||||||
auto ctx = basic_format_parse_context<Char>(str);
|
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1);
|
||||||
auto f = formatter<T, Char>();
|
auto f = formatter<T, Char>();
|
||||||
auto end = f.parse(ctx);
|
auto end = f.parse(ctx);
|
||||||
return {f, pos + (end - str.data()) + 1};
|
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compiles a non-empty format string and returns the compiled representation
|
// Compiles a non-empty format string and returns the compiled representation
|
||||||
@@ -518,8 +535,8 @@ constexpr auto compile_format_string(S format_str) {
|
|||||||
format_str);
|
format_str);
|
||||||
} else if constexpr (str[POS + 1] == ':') {
|
} else if constexpr (str[POS + 1] == ':') {
|
||||||
using type = get_type<ID, Args>;
|
using type = get_type<ID, Args>;
|
||||||
constexpr auto result = parse_specs<type>(str, POS + 2);
|
constexpr auto result = parse_specs<type>(str, POS + 2, ID);
|
||||||
return parse_tail<Args, result.end, ID + 1>(
|
return parse_tail<Args, result.end, result.next_arg_id>(
|
||||||
spec_field<char_type, type, ID>{result.fmt}, format_str);
|
spec_field<char_type, type, ID>{result.fmt}, format_str);
|
||||||
} else {
|
} else {
|
||||||
return unknown_format();
|
return unknown_format();
|
||||||
@@ -530,8 +547,13 @@ constexpr auto compile_format_string(S format_str) {
|
|||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
} else {
|
} else {
|
||||||
constexpr auto end = parse_text(str, POS + 1);
|
constexpr auto end = parse_text(str, POS + 1);
|
||||||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
if constexpr (end - POS > 1) {
|
||||||
format_str);
|
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
||||||
|
format_str);
|
||||||
|
} else {
|
||||||
|
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
|
||||||
|
format_str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,8 +609,7 @@ template <typename CompiledFormat, typename... Args,
|
|||||||
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
detail::buffer<Char>& base = buffer;
|
cf.format(detail::buffer_appender<Char>(buffer), args...);
|
||||||
cf.format(std::back_inserter(base), args...);
|
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,8 +629,7 @@ template <typename CompiledFormat, typename... Args,
|
|||||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
using context = buffer_context<Char>;
|
using context = buffer_context<Char>;
|
||||||
detail::buffer<Char>& base = buffer;
|
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
|
||||||
detail::cf::vformat_to<context>(std::back_inserter(base), cf,
|
|
||||||
make_format_args<context>(args...));
|
make_format_args<context>(args...));
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
@@ -618,9 +638,13 @@ template <typename S, typename... Args,
|
|||||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||||
Args&&... args) {
|
Args&&... args) {
|
||||||
constexpr basic_string_view<typename S::char_type> str = S();
|
#ifdef __cpp_if_constexpr
|
||||||
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
|
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||||
return fmt::to_string(detail::first(args...));
|
constexpr basic_string_view<typename S::char_type> str = S();
|
||||||
|
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
|
||||||
|
return fmt::to_string(detail::first(args...));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
constexpr auto compiled = detail::compile<Args...>(S());
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
return format(compiled, std::forward<Args>(args)...);
|
return format(compiled, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
@@ -643,18 +667,30 @@ OutputIt format_to(OutputIt out, const S&, const Args&... args) {
|
|||||||
return format_to(out, compiled, args...);
|
return format_to(out, compiled, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
template <typename OutputIt, typename CompiledFormat, typename... Args>
|
||||||
typename OutputIt, typename CompiledFormat, typename... Args,
|
auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf,
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& std::is_base_of<
|
const Args&... args) ->
|
||||||
detail::basic_compiled_format, CompiledFormat>::value)>
|
typename std::enable_if<
|
||||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
detail::is_output_iterator<OutputIt,
|
||||||
const CompiledFormat& cf,
|
typename CompiledFormat::char_type>::value &&
|
||||||
const Args&... args) {
|
std::is_base_of<detail::basic_compiled_format,
|
||||||
|
CompiledFormat>::value,
|
||||||
|
format_to_n_result<OutputIt>>::type {
|
||||||
auto it =
|
auto it =
|
||||||
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
|
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
|
||||||
return {it.base(), it.count()};
|
return {it.base(), it.count()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&,
|
||||||
|
const Args&... args) {
|
||||||
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
|
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled,
|
||||||
|
args...);
|
||||||
|
return {it.base(), it.count()};
|
||||||
|
}
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args>
|
template <typename CompiledFormat, typename... Args>
|
||||||
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
||||||
return format_to(detail::counting_iterator(), cf, args...).count();
|
return format_to(detail::counting_iterator(), cf, args...).count();
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// 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 70003
|
#define FMT_VERSION 70103
|
||||||
|
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
|
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
|
||||||
@@ -57,6 +57,7 @@
|
|||||||
# define FMT_MSC_VER 0
|
# define FMT_MSC_VER 0
|
||||||
# define FMT_SUPPRESS_MSC_WARNING(n)
|
# define FMT_SUPPRESS_MSC_WARNING(n)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __has_feature
|
#ifdef __has_feature
|
||||||
# define FMT_HAS_FEATURE(x) __has_feature(x)
|
# define FMT_HAS_FEATURE(x) __has_feature(x)
|
||||||
#else
|
#else
|
||||||
@@ -64,7 +65,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__has_include) && !defined(__INTELLISENSE__) && \
|
#if defined(__has_include) && !defined(__INTELLISENSE__) && \
|
||||||
!(FMT_ICC_VERSION && FMT_ICC_VERSION < 1600)
|
(!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600)
|
||||||
# define FMT_HAS_INCLUDE(x) __has_include(x)
|
# define FMT_HAS_INCLUDE(x) __has_include(x)
|
||||||
#else
|
#else
|
||||||
# define FMT_HAS_INCLUDE(x) 0
|
# define FMT_HAS_INCLUDE(x) 0
|
||||||
@@ -99,7 +100,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_OVERRIDE
|
#ifndef FMT_OVERRIDE
|
||||||
# if FMT_HAS_FEATURE(cxx_override) || \
|
# if FMT_HAS_FEATURE(cxx_override_control) || \
|
||||||
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
|
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
|
||||||
# define FMT_OVERRIDE override
|
# define FMT_OVERRIDE override
|
||||||
# else
|
# else
|
||||||
@@ -152,7 +153,7 @@
|
|||||||
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
|
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
|
||||||
# define FMT_DEPRECATED [[deprecated]]
|
# define FMT_DEPRECATED [[deprecated]]
|
||||||
# else
|
# else
|
||||||
# if defined(__GNUC__) || defined(__clang__)
|
# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
|
||||||
# define FMT_DEPRECATED __attribute__((deprecated))
|
# define FMT_DEPRECATED __attribute__((deprecated))
|
||||||
# elif FMT_MSC_VER
|
# elif FMT_MSC_VER
|
||||||
# define FMT_DEPRECATED __declspec(deprecated)
|
# define FMT_DEPRECATED __declspec(deprecated)
|
||||||
@@ -177,9 +178,17 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_BEGIN_NAMESPACE
|
#ifndef FMT_USE_INLINE_NAMESPACES
|
||||||
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
|
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
|
||||||
FMT_MSC_VER >= 1900
|
(FMT_MSC_VER >= 1900 && !_MANAGED)
|
||||||
|
# define FMT_USE_INLINE_NAMESPACES 1
|
||||||
|
# else
|
||||||
|
# define FMT_USE_INLINE_NAMESPACES 0
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FMT_BEGIN_NAMESPACE
|
||||||
|
# if FMT_USE_INLINE_NAMESPACES
|
||||||
# define FMT_INLINE_NAMESPACE inline namespace
|
# define FMT_INLINE_NAMESPACE inline namespace
|
||||||
# define FMT_END_NAMESPACE \
|
# define FMT_END_NAMESPACE \
|
||||||
} \
|
} \
|
||||||
@@ -269,8 +278,7 @@ struct monostate {};
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
// A helper function to suppress bogus "conditional expression is constant"
|
// A helper function to suppress "conditional expression is constant" warnings.
|
||||||
// warnings.
|
|
||||||
template <typename T> constexpr T const_check(T value) { return value; }
|
template <typename T> constexpr T const_check(T value) { return value; }
|
||||||
|
|
||||||
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
|
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
|
||||||
@@ -299,7 +307,8 @@ template <typename T> struct std_string_view {};
|
|||||||
|
|
||||||
#ifdef FMT_USE_INT128
|
#ifdef FMT_USE_INT128
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && !(FMT_CLANG_VERSION && FMT_MSC_VER)
|
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \
|
||||||
|
!(FMT_CLANG_VERSION && FMT_MSC_VER)
|
||||||
# define FMT_USE_INT128 1
|
# define FMT_USE_INT128 1
|
||||||
using int128_t = __int128_t;
|
using int128_t = __int128_t;
|
||||||
using uint128_t = __uint128_t;
|
using uint128_t = __uint128_t;
|
||||||
@@ -506,6 +515,18 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
|
|||||||
using type = typename result::value_type;
|
using type = typename result::value_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Reports a compile-time error if S is not a valid format string.
|
||||||
|
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
|
||||||
|
FMT_INLINE void check_format_string(const S&) {
|
||||||
|
#ifdef FMT_ENFORCE_COMPILE_STRING
|
||||||
|
static_assert(is_compile_string<S>::value,
|
||||||
|
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
|
||||||
|
"FMT_STRING.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||||
|
void check_format_string(S);
|
||||||
|
|
||||||
struct error_handler {
|
struct error_handler {
|
||||||
constexpr error_handler() = default;
|
constexpr error_handler() = default;
|
||||||
constexpr error_handler(const error_handler&) = default;
|
constexpr error_handler(const error_handler&) = default;
|
||||||
@@ -545,8 +566,9 @@ class basic_format_parse_context : private ErrorHandler {
|
|||||||
using iterator = typename basic_string_view<Char>::iterator;
|
using iterator = typename basic_string_view<Char>::iterator;
|
||||||
|
|
||||||
explicit constexpr basic_format_parse_context(
|
explicit constexpr basic_format_parse_context(
|
||||||
basic_string_view<Char> format_str, ErrorHandler eh = {})
|
basic_string_view<Char> format_str, ErrorHandler eh = {},
|
||||||
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
|
int next_arg_id = 0)
|
||||||
|
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns an iterator to the beginning of the format string range being
|
Returns an iterator to the beginning of the format string range being
|
||||||
@@ -616,8 +638,24 @@ template <typename T, typename Context>
|
|||||||
using has_formatter =
|
using has_formatter =
|
||||||
std::is_constructible<typename Context::template formatter_type<T>>;
|
std::is_constructible<typename Context::template formatter_type<T>>;
|
||||||
|
|
||||||
|
// Checks whether T is a container with contiguous storage.
|
||||||
|
template <typename T> struct is_contiguous : std::false_type {};
|
||||||
|
template <typename Char>
|
||||||
|
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
// Extracts a reference to the container from back_insert_iterator.
|
||||||
|
template <typename Container>
|
||||||
|
inline Container& get_container(std::back_insert_iterator<Container> it) {
|
||||||
|
using bi_iterator = std::back_insert_iterator<Container>;
|
||||||
|
struct accessor : bi_iterator {
|
||||||
|
accessor(bi_iterator iter) : bi_iterator(iter) {}
|
||||||
|
using bi_iterator::container;
|
||||||
|
};
|
||||||
|
return *accessor(it).container;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
A contiguous memory buffer with an optional growing ability. It is an internal
|
A contiguous memory buffer with an optional growing ability. It is an internal
|
||||||
@@ -640,6 +678,8 @@ template <typename T> class buffer {
|
|||||||
size_(sz),
|
size_(sz),
|
||||||
capacity_(cap) {}
|
capacity_(cap) {}
|
||||||
|
|
||||||
|
~buffer() = default;
|
||||||
|
|
||||||
/** Sets the buffer data and capacity. */
|
/** Sets the buffer data and capacity. */
|
||||||
void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
|
void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
|
||||||
ptr_ = buf_data;
|
ptr_ = buf_data;
|
||||||
@@ -655,7 +695,6 @@ template <typename T> class buffer {
|
|||||||
|
|
||||||
buffer(const buffer&) = delete;
|
buffer(const buffer&) = delete;
|
||||||
void operator=(const buffer&) = delete;
|
void operator=(const buffer&) = delete;
|
||||||
virtual ~buffer() = default;
|
|
||||||
|
|
||||||
T* begin() FMT_NOEXCEPT { return ptr_; }
|
T* begin() FMT_NOEXCEPT { return ptr_; }
|
||||||
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
|
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
|
||||||
@@ -675,24 +714,26 @@ template <typename T> class buffer {
|
|||||||
/** Returns a pointer to the buffer data. */
|
/** Returns a pointer to the buffer data. */
|
||||||
const T* data() const FMT_NOEXCEPT { return ptr_; }
|
const T* data() const FMT_NOEXCEPT { return ptr_; }
|
||||||
|
|
||||||
/**
|
|
||||||
Resizes the buffer. If T is a POD type new elements may not be initialized.
|
|
||||||
*/
|
|
||||||
void resize(size_t new_size) {
|
|
||||||
reserve(new_size);
|
|
||||||
size_ = new_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Clears this buffer. */
|
/** Clears this buffer. */
|
||||||
void clear() { size_ = 0; }
|
void clear() { size_ = 0; }
|
||||||
|
|
||||||
/** Reserves space to store at least *capacity* elements. */
|
// Tries resizing the buffer to contain *count* elements. If T is a POD type
|
||||||
void reserve(size_t new_capacity) {
|
// the new elements may not be initialized.
|
||||||
|
void try_resize(size_t count) {
|
||||||
|
try_reserve(count);
|
||||||
|
size_ = count <= capacity_ ? count : capacity_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tries increasing the buffer capacity to *new_capacity*. It can increase the
|
||||||
|
// capacity by a smaller amount than requested but guarantees there is space
|
||||||
|
// for at least one additional element either by increasing the capacity or by
|
||||||
|
// flushing the buffer if it is full.
|
||||||
|
void try_reserve(size_t new_capacity) {
|
||||||
if (new_capacity > capacity_) grow(new_capacity);
|
if (new_capacity > capacity_) grow(new_capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_back(const T& value) {
|
void push_back(const T& value) {
|
||||||
reserve(size_ + 1);
|
try_reserve(size_ + 1);
|
||||||
ptr_[size_++] = value;
|
ptr_[size_++] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,32 +746,150 @@ template <typename T> class buffer {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// A container-backed buffer.
|
struct buffer_traits {
|
||||||
|
explicit buffer_traits(size_t) {}
|
||||||
|
size_t count() const { return 0; }
|
||||||
|
size_t limit(size_t size) { return size; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class fixed_buffer_traits {
|
||||||
|
private:
|
||||||
|
size_t count_ = 0;
|
||||||
|
size_t limit_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
|
||||||
|
size_t count() const { return count_; }
|
||||||
|
size_t limit(size_t size) {
|
||||||
|
size_t n = limit_ > count_ ? limit_ - count_ : 0;
|
||||||
|
count_ += size;
|
||||||
|
return size < n ? size : n;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A buffer that writes to an output iterator when flushed.
|
||||||
|
template <typename OutputIt, typename T, typename Traits = buffer_traits>
|
||||||
|
class iterator_buffer final : public Traits, public buffer<T> {
|
||||||
|
private:
|
||||||
|
OutputIt out_;
|
||||||
|
enum { buffer_size = 256 };
|
||||||
|
T data_[buffer_size];
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void grow(size_t) final FMT_OVERRIDE {
|
||||||
|
if (this->size() == buffer_size) flush();
|
||||||
|
}
|
||||||
|
void flush();
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
|
||||||
|
: Traits(n),
|
||||||
|
buffer<T>(data_, 0, buffer_size),
|
||||||
|
out_(out) {}
|
||||||
|
~iterator_buffer() { flush(); }
|
||||||
|
|
||||||
|
OutputIt out() {
|
||||||
|
flush();
|
||||||
|
return out_;
|
||||||
|
}
|
||||||
|
size_t count() const { return Traits::count() + this->size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
|
||||||
|
protected:
|
||||||
|
void grow(size_t) final FMT_OVERRIDE {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
|
||||||
|
|
||||||
|
T* out() { return &*this->end(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// A buffer that writes to a container with the contiguous storage.
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
class container_buffer : public buffer<typename Container::value_type> {
|
class iterator_buffer<std::back_insert_iterator<Container>,
|
||||||
|
enable_if_t<is_contiguous<Container>::value,
|
||||||
|
typename Container::value_type>>
|
||||||
|
final : public buffer<typename Container::value_type> {
|
||||||
private:
|
private:
|
||||||
Container& container_;
|
Container& container_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void grow(size_t capacity) FMT_OVERRIDE {
|
void grow(size_t capacity) final FMT_OVERRIDE {
|
||||||
container_.resize(capacity);
|
container_.resize(capacity);
|
||||||
this->set(&container_[0], capacity);
|
this->set(&container_[0], capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit container_buffer(Container& c)
|
explicit iterator_buffer(Container& c)
|
||||||
: buffer<typename Container::value_type>(c.size()), container_(c) {}
|
: buffer<typename Container::value_type>(c.size()), container_(c) {}
|
||||||
|
explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
|
||||||
|
: iterator_buffer(get_container(out)) {}
|
||||||
|
std::back_insert_iterator<Container> out() {
|
||||||
|
return std::back_inserter(container_);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Extracts a reference to the container from back_insert_iterator.
|
// A buffer that counts the number of code units written discarding the output.
|
||||||
template <typename Container>
|
template <typename T = char> class counting_buffer final : public buffer<T> {
|
||||||
inline Container& get_container(std::back_insert_iterator<Container> it) {
|
private:
|
||||||
using bi_iterator = std::back_insert_iterator<Container>;
|
enum { buffer_size = 256 };
|
||||||
struct accessor : bi_iterator {
|
T data_[buffer_size];
|
||||||
accessor(bi_iterator iter) : bi_iterator(iter) {}
|
size_t count_ = 0;
|
||||||
using bi_iterator::container;
|
|
||||||
};
|
protected:
|
||||||
return *accessor(it).container;
|
void grow(size_t) final FMT_OVERRIDE {
|
||||||
|
if (this->size() != buffer_size) return;
|
||||||
|
count_ += this->size();
|
||||||
|
this->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
|
||||||
|
|
||||||
|
size_t count() { return count_ + this->size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// An output iterator that appends to the buffer.
|
||||||
|
// It is used to reduce symbol sizes for the common case.
|
||||||
|
template <typename T>
|
||||||
|
class buffer_appender : public std::back_insert_iterator<buffer<T>> {
|
||||||
|
using base = std::back_insert_iterator<buffer<T>>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit buffer_appender(buffer<T>& buf) : base(buf) {}
|
||||||
|
buffer_appender(base it) : base(it) {}
|
||||||
|
|
||||||
|
buffer_appender& operator++() {
|
||||||
|
base::operator++();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_appender operator++(int) {
|
||||||
|
buffer_appender tmp = *this;
|
||||||
|
++*this;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maps an output iterator into a buffer.
|
||||||
|
template <typename T, typename OutputIt>
|
||||||
|
iterator_buffer<OutputIt, T> get_buffer(OutputIt);
|
||||||
|
template <typename T> buffer<T>& get_buffer(buffer_appender<T>);
|
||||||
|
|
||||||
|
template <typename OutputIt> OutputIt get_buffer_init(OutputIt out) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
template <typename T> buffer<T>& get_buffer_init(buffer_appender<T> out) {
|
||||||
|
return get_container(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
auto get_iterator(Buffer& buf) -> decltype(buf.out()) {
|
||||||
|
return buf.out();
|
||||||
|
}
|
||||||
|
template <typename T> buffer_appender<T> get_iterator(buffer<T>& buf) {
|
||||||
|
return buffer_appender<T>(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Char = char, typename Enable = void>
|
template <typename T, typename Char = char, typename Enable = void>
|
||||||
@@ -759,7 +918,8 @@ template <typename Char> struct named_arg_info {
|
|||||||
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
|
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
|
||||||
struct arg_data {
|
struct arg_data {
|
||||||
// args_[0].named_args points to named_args_ to avoid bloating format_args.
|
// args_[0].named_args points to named_args_ to avoid bloating format_args.
|
||||||
T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : 1)];
|
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
|
||||||
|
T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
|
||||||
named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
|
named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
|
||||||
|
|
||||||
template <typename... U>
|
template <typename... U>
|
||||||
@@ -771,7 +931,8 @@ struct arg_data {
|
|||||||
|
|
||||||
template <typename T, typename Char, size_t NUM_ARGS>
|
template <typename T, typename Char, size_t NUM_ARGS>
|
||||||
struct arg_data<T, Char, NUM_ARGS, 0> {
|
struct arg_data<T, Char, NUM_ARGS, 0> {
|
||||||
T args_[NUM_ARGS != 0 ? NUM_ARGS : 1];
|
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
|
||||||
|
T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
|
||||||
|
|
||||||
template <typename... U>
|
template <typename... U>
|
||||||
FMT_INLINE arg_data(const U&... init) : args_{init...} {}
|
FMT_INLINE arg_data(const U&... init) : args_{init...} {}
|
||||||
@@ -959,6 +1120,8 @@ enum { long_short = sizeof(long) == sizeof(int) };
|
|||||||
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>;
|
||||||
|
|
||||||
|
struct unformattable {};
|
||||||
|
|
||||||
// Maps formatting arguments to core types.
|
// Maps formatting arguments to core types.
|
||||||
template <typename Context> struct arg_mapper {
|
template <typename Context> struct arg_mapper {
|
||||||
using char_type = typename Context::char_type;
|
using char_type = typename Context::char_type;
|
||||||
@@ -1067,15 +1230,7 @@ template <typename Context> struct arg_mapper {
|
|||||||
return map(val.value);
|
return map(val.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int map(...) {
|
unformattable map(...) { return {}; }
|
||||||
constexpr bool formattable = sizeof(Context) == 0;
|
|
||||||
static_assert(
|
|
||||||
formattable,
|
|
||||||
"Cannot format argument. To make type T formattable provide a "
|
|
||||||
"formatter<T> specialization: "
|
|
||||||
"https://fmt.dev/latest/api.html#formatting-user-defined-types");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A type constant after applying arg_mapper<Context>.
|
// A type constant after applying arg_mapper<Context>.
|
||||||
@@ -1199,15 +1354,25 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg(
|
|||||||
return vis(monostate());
|
return vis(monostate());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks whether T is a container with contiguous storage.
|
template <typename T> struct formattable : std::false_type {};
|
||||||
template <typename T> struct is_contiguous : std::false_type {};
|
|
||||||
template <typename Char>
|
|
||||||
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
|
|
||||||
template <typename Char>
|
|
||||||
struct is_contiguous<detail::buffer<Char>> : std::true_type {};
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
|
||||||
|
template <typename... Ts> struct void_t_impl { using type = void; };
|
||||||
|
template <typename... Ts>
|
||||||
|
using void_t = typename detail::void_t_impl<Ts...>::type;
|
||||||
|
|
||||||
|
template <typename It, typename T, typename Enable = void>
|
||||||
|
struct is_output_iterator : std::false_type {};
|
||||||
|
|
||||||
|
template <typename It, typename T>
|
||||||
|
struct is_output_iterator<
|
||||||
|
It, T,
|
||||||
|
void_t<typename std::iterator_traits<It>::iterator_category,
|
||||||
|
decltype(*std::declval<It>() = std::declval<T>())>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
template <typename OutputIt>
|
template <typename OutputIt>
|
||||||
struct is_back_insert_iterator : std::false_type {};
|
struct is_back_insert_iterator : std::false_type {};
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
@@ -1219,6 +1384,9 @@ struct is_contiguous_back_insert_iterator : std::false_type {};
|
|||||||
template <typename Container>
|
template <typename Container>
|
||||||
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
|
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
|
||||||
: is_contiguous<Container> {};
|
: is_contiguous<Container> {};
|
||||||
|
template <typename Char>
|
||||||
|
struct is_contiguous_back_insert_iterator<buffer_appender<Char>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
// A type-erased reference to an std::locale to avoid heavy <locale> include.
|
// A type-erased reference to an std::locale to avoid heavy <locale> include.
|
||||||
class locale_ref {
|
class locale_ref {
|
||||||
@@ -1250,13 +1418,24 @@ FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value) {
|
|||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> int check(unformattable) {
|
||||||
|
static_assert(
|
||||||
|
formattable<T>(),
|
||||||
|
"Cannot format an argument. To make type T formattable provide a "
|
||||||
|
"formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
template <typename T, typename U> inline const U& check(const U& val) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
// The type template parameter is there to avoid an ODR violation when using
|
// The type template parameter is there to avoid an ODR violation when using
|
||||||
// a fallback formatter in one translation unit and an implicit conversion in
|
// a fallback formatter in one translation unit and an implicit conversion in
|
||||||
// another (not recommended).
|
// another (not recommended).
|
||||||
template <bool IS_PACKED, typename Context, type, typename T,
|
template <bool IS_PACKED, typename Context, type, typename T,
|
||||||
FMT_ENABLE_IF(IS_PACKED)>
|
FMT_ENABLE_IF(IS_PACKED)>
|
||||||
inline value<Context> make_arg(const T& val) {
|
inline value<Context> make_arg(const T& val) {
|
||||||
return arg_mapper<Context>().map(val);
|
return check<T>(arg_mapper<Context>().map(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool IS_PACKED, typename Context, type, typename T,
|
template <bool IS_PACKED, typename Context, type, typename T,
|
||||||
@@ -1356,13 +1535,13 @@ template <typename OutputIt, typename Char> class basic_format_context {
|
|||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
using buffer_context =
|
using buffer_context =
|
||||||
basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
|
basic_format_context<detail::buffer_appender<Char>, Char>;
|
||||||
using format_context = buffer_context<char>;
|
using format_context = buffer_context<char>;
|
||||||
using wformat_context = buffer_context<wchar_t>;
|
using wformat_context = buffer_context<wchar_t>;
|
||||||
|
|
||||||
// Workaround a bug in gcc: https://stackoverflow.com/q/62767544/471164.
|
// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164.
|
||||||
#define FMT_BUFFER_CONTEXT(Char) \
|
#define FMT_BUFFER_CONTEXT(Char) \
|
||||||
basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>
|
basic_format_context<detail::buffer_appender<Char>, Char>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
@@ -1414,7 +1593,7 @@ class format_arg_store
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Constructs an `~fmt::format_arg_store` object that contains references to
|
Constructs a `~fmt::format_arg_store` object that contains references to
|
||||||
arguments and can be implicitly converted to `~fmt::format_args`. `Context`
|
arguments and can be implicitly converted to `~fmt::format_args`. `Context`
|
||||||
can be omitted in which case it defaults to `~fmt::context`.
|
can be omitted in which case it defaults to `~fmt::context`.
|
||||||
See `~fmt::arg` for lifetime considerations.
|
See `~fmt::arg` for lifetime considerations.
|
||||||
@@ -1426,6 +1605,27 @@ inline format_arg_store<Context, Args...> make_format_args(
|
|||||||
return {args...};
|
return {args...};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a `~fmt::format_arg_store` object that contains references
|
||||||
|
to arguments and can be implicitly converted to `~fmt::format_args`.
|
||||||
|
If ``format_str`` is a compile-time string then `make_args_checked` checks
|
||||||
|
its validity at compile time.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args, typename S, typename Char = char_t<S>>
|
||||||
|
inline auto make_args_checked(const S& format_str,
|
||||||
|
const remove_reference_t<Args>&... args)
|
||||||
|
-> format_arg_store<buffer_context<Char>, remove_reference_t<Args>...> {
|
||||||
|
static_assert(
|
||||||
|
detail::count<(
|
||||||
|
std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
|
||||||
|
std::is_reference<Args>::value)...>() == 0,
|
||||||
|
"passing views as lvalues is disallowed");
|
||||||
|
detail::check_format_string<Args...>(format_str);
|
||||||
|
return {args...};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Returns a named argument to be used in a formatting function. It should only
|
Returns a named argument to be used in a formatting function. It should only
|
||||||
@@ -1729,7 +1929,14 @@ template <typename Context> class basic_format_args {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** An alias to ``basic_format_args<context>``. */
|
#ifdef FMT_ARM_ABI_COMPATIBILITY
|
||||||
|
/** An alias to ``basic_format_args<format_context>``. */
|
||||||
|
// Separate types would result in shorter symbols but break ABI compatibility
|
||||||
|
// between clang and gcc on ARM (#1919).
|
||||||
|
using format_args = basic_format_args<format_context>;
|
||||||
|
using wformat_args = basic_format_args<wformat_context>;
|
||||||
|
#else
|
||||||
|
// DEPRECATED! These are kept for ABI compatibility.
|
||||||
// It is a separate type rather than an alias to make symbols readable.
|
// It is a separate type rather than an alias to make symbols readable.
|
||||||
struct format_args : basic_format_args<format_context> {
|
struct format_args : basic_format_args<format_context> {
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
@@ -1738,32 +1945,10 @@ struct format_args : basic_format_args<format_context> {
|
|||||||
struct wformat_args : basic_format_args<wformat_context> {
|
struct wformat_args : basic_format_args<wformat_context> {
|
||||||
using basic_format_args::basic_format_args;
|
using basic_format_args::basic_format_args;
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
// Reports a compile-time error if S is not a valid format string.
|
|
||||||
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
|
|
||||||
FMT_INLINE void check_format_string(const S&) {
|
|
||||||
#ifdef FMT_ENFORCE_COMPILE_STRING
|
|
||||||
static_assert(is_compile_string<S>::value,
|
|
||||||
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
|
|
||||||
"FMT_STRING.");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
|
|
||||||
void check_format_string(S);
|
|
||||||
|
|
||||||
template <typename... Args, typename S, typename Char = char_t<S>>
|
|
||||||
inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
|
|
||||||
make_args_checked(const S& format_str,
|
|
||||||
const remove_reference_t<Args>&... args) {
|
|
||||||
static_assert(count<(std::is_base_of<view, remove_reference_t<Args>>::value &&
|
|
||||||
std::is_reference<Args>::value)...>() == 0,
|
|
||||||
"passing views as lvalues is disallowed");
|
|
||||||
check_format_string<Args...>(format_str);
|
|
||||||
return {args...};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||||
std::basic_string<Char> vformat(
|
std::basic_string<Char> vformat(
|
||||||
basic_string_view<Char> format_str,
|
basic_string_view<Char> format_str,
|
||||||
@@ -1772,9 +1957,10 @@ std::basic_string<Char> vformat(
|
|||||||
FMT_API std::string vformat(string_view format_str, format_args args);
|
FMT_API std::string vformat(string_view format_str, format_args args);
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to(
|
void vformat_to(
|
||||||
buffer<Char>& buf, basic_string_view<Char> format_str,
|
buffer<Char>& buf, basic_string_view<Char> format_str,
|
||||||
basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args);
|
basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
|
||||||
|
detail::locale_ref loc = {});
|
||||||
|
|
||||||
template <typename Char, typename Args,
|
template <typename Char, typename Args,
|
||||||
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||||
@@ -1789,26 +1975,80 @@ inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
|
|||||||
/** Formats a string and writes the output to ``out``. */
|
/** Formats a string and writes the output to ``out``. */
|
||||||
// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
|
// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
|
||||||
// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
|
// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
|
||||||
template <
|
template <typename OutputIt, typename S, typename Char = char_t<S>,
|
||||||
typename OutputIt, typename S, typename Char = char_t<S>,
|
bool enable = detail::is_output_iterator<OutputIt, Char>::value>
|
||||||
FMT_ENABLE_IF(detail::is_contiguous_back_insert_iterator<OutputIt>::value)>
|
auto vformat_to(OutputIt out, const S& format_str,
|
||||||
OutputIt vformat_to(
|
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||||
OutputIt out, const S& format_str,
|
-> typename std::enable_if<enable, OutputIt>::type {
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
||||||
auto& c = detail::get_container(out);
|
|
||||||
detail::container_buffer<remove_reference_t<decltype(c)>> buf(c);
|
|
||||||
detail::vformat_to(buf, to_string_view(format_str), args);
|
detail::vformat_to(buf, to_string_view(format_str), args);
|
||||||
return out;
|
return detail::get_iterator(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Container, typename S, typename... Args,
|
/**
|
||||||
FMT_ENABLE_IF(
|
\rst
|
||||||
is_contiguous<Container>::value&& detail::is_string<S>::value)>
|
Formats arguments, writes the result to the output iterator ``out`` and returns
|
||||||
inline std::back_insert_iterator<Container> format_to(
|
the iterator past the end of the output range.
|
||||||
std::back_insert_iterator<Container> out, const S& format_str,
|
|
||||||
Args&&... args) {
|
**Example**::
|
||||||
return vformat_to(out, to_string_view(format_str),
|
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
std::vector<char> out;
|
||||||
|
fmt::format_to(std::back_inserter(out), "{}", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
// We cannot use FMT_ENABLE_IF because of a bug in gcc 8.3.
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
|
||||||
|
inline auto format_to(OutputIt out, const S& format_str, Args&&... args) ->
|
||||||
|
typename std::enable_if<enable, OutputIt>::type {
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
|
return vformat_to(out, to_string_view(format_str), vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt> struct format_to_n_result {
|
||||||
|
/** Iterator past the end of the output range. */
|
||||||
|
OutputIt out;
|
||||||
|
/** Total (not truncated) output size. */
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
||||||
|
inline format_to_n_result<OutputIt> vformat_to_n(
|
||||||
|
OutputIt out, size_t n, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
|
||||||
|
n);
|
||||||
|
detail::vformat_to(buf, format_str, args);
|
||||||
|
return {buf.out(), buf.count()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments, writes up to ``n`` characters of the result to the output
|
||||||
|
iterator ``out`` and returns the total output size and the iterator past the
|
||||||
|
end of the output range.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
|
||||||
|
inline auto format_to_n(OutputIt out, size_t n, const S& format_str,
|
||||||
|
const Args&... args) ->
|
||||||
|
typename std::enable_if<enable, format_to_n_result<OutputIt>>::type {
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
|
return vformat_to_n(out, n, to_string_view(format_str), vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the number of characters in the output of
|
||||||
|
``format(format_str, args...)``.
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline size_t formatted_size(string_view format_str, Args&&... args) {
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
|
detail::counting_buffer<> buf;
|
||||||
|
detail::vformat_to(buf, format_str, vargs);
|
||||||
|
return buf.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
@@ -1832,7 +2072,7 @@ FMT_INLINE std::basic_string<Char> vformat(
|
|||||||
// std::basic_string<char_t<S>> to reduce the symbol size.
|
// std::basic_string<char_t<S>> to reduce the symbol size.
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) {
|
FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) {
|
||||||
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
return detail::vformat(to_string_view(format_str), vargs);
|
return detail::vformat(to_string_view(format_str), vargs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1852,7 +2092,7 @@ FMT_API void vprint(std::FILE*, string_view, format_args);
|
|||||||
*/
|
*/
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
|
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
|
||||||
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
return detail::is_unicode<Char>()
|
return detail::is_unicode<Char>()
|
||||||
? vprint(f, to_string_view(format_str), vargs)
|
? vprint(f, to_string_view(format_str), vargs)
|
||||||
: detail::vprint_mojibake(f, to_string_view(format_str), vargs);
|
: detail::vprint_mojibake(f, to_string_view(format_str), vargs);
|
||||||
@@ -1871,7 +2111,7 @@ inline void print(std::FILE* f, const S& format_str, Args&&... args) {
|
|||||||
*/
|
*/
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
inline void print(const S& format_str, Args&&... args) {
|
inline void print(const S& format_str, Args&&... args) {
|
||||||
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
return detail::is_unicode<Char>()
|
return detail::is_unicode<Char>()
|
||||||
? vprint(to_string_view(format_str), vargs)
|
? vprint(to_string_view(format_str), vargs)
|
||||||
: detail::vprint_mojibake(stdout, to_string_view(format_str),
|
: detail::vprint_mojibake(stdout, to_string_view(format_str),
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -15,22 +15,12 @@
|
|||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template <typename Char>
|
|
||||||
typename buffer_context<Char>::iterator vformat_to(
|
|
||||||
const std::locale& loc, buffer<Char>& buf,
|
|
||||||
basic_string_view<Char> format_str,
|
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
||||||
using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
|
|
||||||
return vformat_to<af>(std::back_inserter(buf), to_string_view(format_str),
|
|
||||||
args, detail::locale_ref(loc));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
std::basic_string<Char> vformat(
|
std::basic_string<Char> vformat(
|
||||||
const std::locale& loc, basic_string_view<Char> format_str,
|
const std::locale& loc, basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
detail::vformat_to(loc, buffer, format_str, args);
|
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
|
||||||
return fmt::to_string(buffer);
|
return fmt::to_string(buffer);
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
@@ -45,32 +35,28 @@ inline std::basic_string<Char> vformat(
|
|||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
inline std::basic_string<Char> format(const std::locale& loc,
|
inline std::basic_string<Char> format(const std::locale& loc,
|
||||||
const S& format_str, Args&&... args) {
|
const S& format_str, Args&&... args) {
|
||||||
return detail::vformat(
|
return detail::vformat(loc, to_string_view(format_str),
|
||||||
loc, to_string_view(format_str),
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename OutputIt, typename... Args,
|
template <typename S, typename OutputIt, typename... Args,
|
||||||
typename Char = enable_if_t<
|
typename Char = char_t<S>,
|
||||||
detail::is_output_iterator<OutputIt>::value, char_t<S>>>
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
||||||
inline OutputIt vformat_to(
|
inline OutputIt vformat_to(
|
||||||
OutputIt out, const std::locale& loc, const S& format_str,
|
OutputIt out, const std::locale& loc, const S& format_str,
|
||||||
format_args_t<type_identity_t<OutputIt>, Char> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
using af = detail::arg_formatter<OutputIt, Char>;
|
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
||||||
return vformat_to<af>(out, to_string_view(format_str), args,
|
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
|
||||||
detail::locale_ref(loc));
|
return detail::get_iterator(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&&
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
|
||||||
detail::is_string<S>::value)>
|
inline auto format_to(OutputIt out, const std::locale& loc,
|
||||||
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
const S& format_str, Args&&... args) ->
|
||||||
const S& format_str, Args&&... args) {
|
typename std::enable_if<enable, OutputIt>::type {
|
||||||
detail::check_format_string<Args...>(format_str);
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
using context = format_context_t<OutputIt, char_t<S>>;
|
return vformat_to(out, loc, to_string_view(format_str), vargs);
|
||||||
format_arg_store<context, Args...> as{args...};
|
|
||||||
return vformat_to(out, loc, to_string_view(format_str),
|
|
||||||
basic_format_args<context>(as));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|||||||
@@ -29,7 +29,8 @@
|
|||||||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
#if FMT_HAS_INCLUDE("winapifamily.h")
|
||||||
# include <winapifamily.h>
|
# include <winapifamily.h>
|
||||||
#endif
|
#endif
|
||||||
#if FMT_HAS_INCLUDE("fcntl.h") && \
|
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||||
|
defined(__linux__)) && \
|
||||||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||||
# include <fcntl.h> // for O_RDONLY
|
# include <fcntl.h> // for O_RDONLY
|
||||||
# define FMT_USE_FCNTL 1
|
# define FMT_USE_FCNTL 1
|
||||||
@@ -278,7 +279,8 @@ class file {
|
|||||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||||
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
|
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
|
||||||
CREATE = FMT_POSIX(O_CREAT) // Create if the file doesn't exist.
|
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
|
||||||
|
APPEND = FMT_POSIX(O_APPEND) // Open in append mode.
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constructs a file object which doesn't represent any file.
|
// Constructs a file object which doesn't represent any file.
|
||||||
@@ -343,36 +345,69 @@ class file {
|
|||||||
// Returns the memory page size.
|
// Returns the memory page size.
|
||||||
long getpagesize();
|
long getpagesize();
|
||||||
|
|
||||||
class direct_buffered_file;
|
namespace detail {
|
||||||
|
|
||||||
template <typename S, typename... Args>
|
struct buffer_size {
|
||||||
void print(direct_buffered_file& f, const S& format_str,
|
size_t value = 0;
|
||||||
const Args&... args);
|
buffer_size operator=(size_t val) const {
|
||||||
|
auto bs = buffer_size();
|
||||||
|
bs.value = val;
|
||||||
|
return bs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// A buffered file with a direct buffer access and no synchronization.
|
struct ostream_params {
|
||||||
class direct_buffered_file {
|
int oflag = file::WRONLY | file::CREATE;
|
||||||
|
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
|
||||||
|
|
||||||
|
ostream_params() {}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
ostream_params(T... params, int oflag) : ostream_params(params...) {
|
||||||
|
this->oflag = oflag;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
ostream_params(T... params, detail::buffer_size bs)
|
||||||
|
: ostream_params(params...) {
|
||||||
|
this->buffer_size = bs.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
static constexpr detail::buffer_size buffer_size;
|
||||||
|
|
||||||
|
// A fast output stream which is not thread-safe.
|
||||||
|
class ostream final : private detail::buffer<char> {
|
||||||
private:
|
private:
|
||||||
file file_;
|
file file_;
|
||||||
|
|
||||||
enum { buffer_size = 4096 };
|
|
||||||
char buffer_[buffer_size];
|
|
||||||
int pos_;
|
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
if (pos_ == 0) return;
|
if (size() == 0) return;
|
||||||
file_.write(buffer_, pos_);
|
file_.write(data(), size());
|
||||||
pos_ = 0;
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
int free_capacity() const { return buffer_size - pos_; }
|
FMT_API void grow(size_t) override final;
|
||||||
|
|
||||||
|
ostream(cstring_view path, const detail::ostream_params& params)
|
||||||
|
: file_(path, params.oflag) {
|
||||||
|
set(new char[params.buffer_size], params.buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
direct_buffered_file(cstring_view path, int oflag)
|
ostream(ostream&& other)
|
||||||
: file_(path, oflag), pos_(0) {}
|
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
|
||||||
|
file_(std::move(other.file_)) {
|
||||||
~direct_buffered_file() {
|
other.set(nullptr, 0);
|
||||||
flush();
|
|
||||||
}
|
}
|
||||||
|
~ostream() {
|
||||||
|
flush();
|
||||||
|
delete[] data();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
friend ostream output_file(cstring_view path, T... params);
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
flush();
|
flush();
|
||||||
@@ -380,25 +415,20 @@ class direct_buffered_file {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename... Args>
|
template <typename S, typename... Args>
|
||||||
friend void print(direct_buffered_file& f, const S& format_str,
|
void print(const S& format_str, const Args&... args) {
|
||||||
const Args&... args) {
|
format_to(detail::buffer_appender<char>(*this), format_str, args...);
|
||||||
// We could avoid double buffering.
|
|
||||||
auto buf = fmt::memory_buffer();
|
|
||||||
fmt::format_to(std::back_inserter(buf), format_str, args...);
|
|
||||||
auto remaining_pos = 0;
|
|
||||||
auto remaining_size = buf.size();
|
|
||||||
while (remaining_size > detail::to_unsigned(f.free_capacity())) {
|
|
||||||
auto size = f.free_capacity();
|
|
||||||
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, size);
|
|
||||||
f.pos_ += size;
|
|
||||||
f.flush();
|
|
||||||
remaining_pos += size;
|
|
||||||
remaining_size -= size;
|
|
||||||
}
|
|
||||||
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, remaining_size);
|
|
||||||
f.pos_ += static_cast<int>(remaining_size);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Opens a file for writing. Supported parameters passed in `params`:
|
||||||
|
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
|
||||||
|
* ``buffer_size=<integer>``: Output buffer size
|
||||||
|
*/
|
||||||
|
template <typename... T>
|
||||||
|
inline ostream output_file(cstring_view path, T... params) {
|
||||||
|
return {path, detail::ostream_params(params...)};
|
||||||
|
}
|
||||||
#endif // FMT_USE_FCNTL
|
#endif // FMT_USE_FCNTL
|
||||||
|
|
||||||
#ifdef FMT_LOCALE
|
#ifdef FMT_LOCALE
|
||||||
|
|||||||
@@ -49,17 +49,27 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct converter {
|
||||||
|
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
||||||
private:
|
private:
|
||||||
// Hide all operator<< from std::basic_ostream<Char>.
|
void_t<> operator<<(converter);
|
||||||
void_t<> operator<<(null<>);
|
|
||||||
void_t<> operator<<(const Char*);
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
|
|
||||||
!std::is_enum<T>::value)>
|
|
||||||
void_t<> operator<<(T);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Hide insertion operators for built-in types.
|
||||||
|
template <typename Char, typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
|
||||||
|
template <typename Char, typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
|
||||||
|
template <typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
|
||||||
|
template <typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
|
||||||
|
template <typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
|
||||||
|
|
||||||
// Checks if T has a user-defined operator<< (e.g. not a member of
|
// Checks if T has a user-defined operator<< (e.g. not a member of
|
||||||
// std::ostream).
|
// std::ostream).
|
||||||
template <typename T, typename Char> class is_streamable {
|
template <typename T, typename Char> class is_streamable {
|
||||||
@@ -103,7 +113,7 @@ void format_value(buffer<Char>& buf, const T& value,
|
|||||||
#endif
|
#endif
|
||||||
output << value;
|
output << value;
|
||||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
buf.resize(buf.size());
|
buf.try_resize(buf.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||||
@@ -160,7 +170,7 @@ template <typename S, typename... Args,
|
|||||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||||
vprint(os, to_string_view(format_str),
|
vprint(os, to_string_view(format_str),
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ template <typename Char> class printf_width_handler {
|
|||||||
template <typename Char, typename Context>
|
template <typename Char, typename Context>
|
||||||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||||
basic_format_args<Context> args) {
|
basic_format_args<Context> args) {
|
||||||
Context(std::back_inserter(buf), format, args).format();
|
Context(buffer_appender<Char>(buf), format, args).format();
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
@@ -598,7 +598,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
|||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
using basic_printf_context_t =
|
using basic_printf_context_t =
|
||||||
basic_printf_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
|
basic_printf_context<detail::buffer_appender<Char>, Char>;
|
||||||
|
|
||||||
using printf_context = basic_printf_context_t<char>;
|
using printf_context = basic_printf_context_t<char>;
|
||||||
using wprintf_context = basic_printf_context_t<wchar_t>;
|
using wprintf_context = basic_printf_context_t<wchar_t>;
|
||||||
|
|||||||
@@ -157,6 +157,9 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
|||||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Range>
|
||||||
|
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
|
||||||
|
|
||||||
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
||||||
typename std::decay<Arg>::type>::value)>
|
typename std::decay<Arg>::type>::value)>
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||||
@@ -182,7 +185,6 @@ FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
|||||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
||||||
return add_space ? L" '{}'" : L"'{}'";
|
return add_space ? L" '{}'" : L"'{}'";
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template <typename T> struct is_tuple_like {
|
template <typename T> struct is_tuple_like {
|
||||||
@@ -246,9 +248,18 @@ template <typename T, typename Char> struct is_range {
|
|||||||
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename RangeT, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<RangeT, Char,
|
struct formatter<
|
||||||
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
|
T, Char,
|
||||||
|
enable_if_t<fmt::is_range<T, Char>::value
|
||||||
|
// Workaround a bug in MSVC 2017 and earlier.
|
||||||
|
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
|
||||||
|
&&
|
||||||
|
(has_formatter<detail::value_type<T>, format_context>::value ||
|
||||||
|
detail::has_fallback_formatter<detail::value_type<T>,
|
||||||
|
format_context>::value)
|
||||||
|
#endif
|
||||||
|
>> {
|
||||||
formatting_range<Char> formatting;
|
formatting_range<Char> formatting;
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
@@ -257,8 +268,7 @@ struct formatter<RangeT, Char,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
typename FormatContext::iterator format(const RangeT& values,
|
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
|
||||||
FormatContext& ctx) {
|
|
||||||
auto out = detail::copy(formatting.prefix, ctx.out());
|
auto out = detail::copy(formatting.prefix, ctx.out());
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
auto it = values.begin();
|
auto it = values.begin();
|
||||||
|
|||||||
@@ -247,7 +247,11 @@ SPDLOG_INLINE void logger::err_handler_(const std::string &msg)
|
|||||||
auto tm_time = details::os::localtime(system_clock::to_time_t(now));
|
auto tm_time = details::os::localtime(system_clock::to_time_t(now));
|
||||||
char date_buf[64];
|
char date_buf[64];
|
||||||
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
||||||
fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
|
#if defined(USING_R) && defined(R_R_H) // if in R environment
|
||||||
|
REprintf("[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
|
||||||
|
#else
|
||||||
|
std::fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|||||||
@@ -75,58 +75,58 @@ public:
|
|||||||
|
|
||||||
// FormatString is a type derived from fmt::compile_string
|
// FormatString is a type derived from fmt::compile_string
|
||||||
template<typename FormatString, typename std::enable_if<fmt::is_compile_string<FormatString>::value, int>::type = 0, typename... Args>
|
template<typename FormatString, typename std::enable_if<fmt::is_compile_string<FormatString>::value, int>::type = 0, typename... Args>
|
||||||
void log(source_loc loc, level::level_enum lvl, const FormatString &fmt, const Args &... args)
|
void log(source_loc loc, level::level_enum lvl, const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log_(loc, lvl, fmt, args...);
|
log_(loc, lvl, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatString is NOT a type derived from fmt::compile_string but is a string_view_t or can be implicitly converted to one
|
// FormatString is NOT a type derived from fmt::compile_string but is a string_view_t or can be implicitly converted to one
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void log(source_loc loc, level::level_enum lvl, string_view_t fmt, const Args &... args)
|
void log(source_loc loc, level::level_enum lvl, string_view_t fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log_(loc, lvl, fmt, args...);
|
log_(loc, lvl, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void log(level::level_enum lvl, const FormatString &fmt, const Args &... args)
|
void log(level::level_enum lvl, const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log(source_loc{}, lvl, fmt, args...);
|
log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void trace(const FormatString &fmt, const Args &... args)
|
void trace(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log(level::trace, fmt, args...);
|
log(level::trace, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void debug(const FormatString &fmt, const Args &... args)
|
void debug(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log(level::debug, fmt, args...);
|
log(level::debug, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void info(const FormatString &fmt, const Args &... args)
|
void info(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log(level::info, fmt, args...);
|
log(level::info, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void warn(const FormatString &fmt, const Args &... args)
|
void warn(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log(level::warn, fmt, args...);
|
log(level::warn, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void error(const FormatString &fmt, const Args &... args)
|
void error(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log(level::err, fmt, args...);
|
log(level::err, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void critical(const FormatString &fmt, const Args &... args)
|
void critical(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log(level::critical, fmt, args...);
|
log(level::critical, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@@ -225,7 +225,7 @@ public:
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void log(source_loc loc, level::level_enum lvl, wstring_view_t fmt, const Args &... args)
|
void log(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
bool log_enabled = should_log(lvl);
|
bool log_enabled = should_log(lvl);
|
||||||
bool traceback_enabled = tracer_.enabled();
|
bool traceback_enabled = tracer_.enabled();
|
||||||
@@ -237,7 +237,7 @@ public:
|
|||||||
{
|
{
|
||||||
// format to wmemory_buffer and convert to utf8
|
// format to wmemory_buffer and convert to utf8
|
||||||
fmt::wmemory_buffer wbuf;
|
fmt::wmemory_buffer wbuf;
|
||||||
fmt::format_to(wbuf, fmt, args...);
|
fmt::format_to(wbuf, fmt, std::forward<Args>(args)...);
|
||||||
|
|
||||||
memory_buf_t buf;
|
memory_buf_t buf;
|
||||||
details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
|
details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
|
||||||
@@ -326,7 +326,7 @@ protected:
|
|||||||
|
|
||||||
// common implementation for after templated public api has been resolved
|
// common implementation for after templated public api has been resolved
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void log_(source_loc loc, level::level_enum lvl, const FormatString &fmt, const Args &... args)
|
void log_(source_loc loc, level::level_enum lvl, const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
bool log_enabled = should_log(lvl);
|
bool log_enabled = should_log(lvl);
|
||||||
bool traceback_enabled = tracer_.enabled();
|
bool traceback_enabled = tracer_.enabled();
|
||||||
@@ -337,7 +337,7 @@ protected:
|
|||||||
SPDLOG_TRY
|
SPDLOG_TRY
|
||||||
{
|
{
|
||||||
memory_buf_t buf;
|
memory_buf_t buf;
|
||||||
fmt::format_to(buf, fmt, args...);
|
fmt::format_to(buf, fmt, std::forward<Args>(args)...);
|
||||||
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
||||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,12 +45,12 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (padinfo_.side_ == padding_info::left)
|
if (padinfo_.side_ == padding_info::pad_side::left)
|
||||||
{
|
{
|
||||||
pad_it(remaining_pad_);
|
pad_it(remaining_pad_);
|
||||||
remaining_pad_ = 0;
|
remaining_pad_ = 0;
|
||||||
}
|
}
|
||||||
else if (padinfo_.side_ == padding_info::center)
|
else if (padinfo_.side_ == padding_info::pad_side::center)
|
||||||
{
|
{
|
||||||
auto half_pad = remaining_pad_ / 2;
|
auto half_pad = remaining_pad_ / 2;
|
||||||
auto reminder = remaining_pad_ & 1;
|
auto reminder = remaining_pad_ & 1;
|
||||||
@@ -1285,15 +1285,15 @@ SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::stri
|
|||||||
switch (*it)
|
switch (*it)
|
||||||
{
|
{
|
||||||
case '-':
|
case '-':
|
||||||
side = padding_info::right;
|
side = padding_info::pad_side::right;
|
||||||
++it;
|
++it;
|
||||||
break;
|
break;
|
||||||
case '=':
|
case '=':
|
||||||
side = padding_info::center;
|
side = padding_info::pad_side::center;
|
||||||
++it;
|
++it;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
side = details::padding_info::left;
|
side = details::padding_info::pad_side::left;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace details {
|
|||||||
// padding information.
|
// padding information.
|
||||||
struct padding_info
|
struct padding_info
|
||||||
{
|
{
|
||||||
enum pad_side
|
enum class pad_side
|
||||||
{
|
{
|
||||||
left,
|
left,
|
||||||
right,
|
right,
|
||||||
@@ -42,7 +42,7 @@ struct padding_info
|
|||||||
return enabled_;
|
return enabled_;
|
||||||
}
|
}
|
||||||
size_t width_ = 0;
|
size_t width_ = 0;
|
||||||
pad_side side_ = left;
|
pad_side side_ = pad_side::left;
|
||||||
bool truncate_ = false;
|
bool truncate_ = false;
|
||||||
bool enabled_ = false;
|
bool enabled_ = false;
|
||||||
};
|
};
|
||||||
@@ -92,9 +92,9 @@ public:
|
|||||||
void format(const details::log_msg &msg, memory_buf_t &dest) override;
|
void format(const details::log_msg &msg, memory_buf_t &dest) override;
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
pattern_formatter &add_flag(char flag, const Args &... args)
|
pattern_formatter &add_flag(char flag, Args&&...args)
|
||||||
{
|
{
|
||||||
custom_handlers_[flag] = details::make_unique<T>(args...);
|
custom_handlers_[flag] = details::make_unique<T>(std::forward<Args>(args)...);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
void set_pattern(std::string pattern);
|
void set_pattern(std::string pattern);
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ protected:
|
|||||||
{
|
{
|
||||||
memory_buf_t buf;
|
memory_buf_t buf;
|
||||||
fmt::format_to(buf, "Skipped {} duplicate messages..", skip_counter_);
|
fmt::format_to(buf, "Skipped {} duplicate messages..", skip_counter_);
|
||||||
details::log_msg skipped_msg{msg.logger_name, msg.level, string_view_t{buf.data(), buf.size()}};
|
details::log_msg skipped_msg{msg.logger_name, level::info, string_view_t{buf.data(), buf.size()}};
|
||||||
dist_sink<Mutex>::sink_it_(skipped_msg);
|
dist_sink<Mutex>::sink_it_(skipped_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,13 @@
|
|||||||
#include <spdlog/details/null_mutex.h>
|
#include <spdlog/details/null_mutex.h>
|
||||||
#include <spdlog/sinks/base_sink.h>
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
|
||||||
#include <spdlog/details/windows_include.h>
|
|
||||||
#include <winbase.h>
|
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
|
// Avoid including windows.h (https://stackoverflow.com/a/30741042)
|
||||||
|
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString);
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace sinks {
|
namespace sinks {
|
||||||
/*
|
/*
|
||||||
@@ -23,12 +24,11 @@ template<typename Mutex>
|
|||||||
class msvc_sink : public base_sink<Mutex>
|
class msvc_sink : public base_sink<Mutex>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit msvc_sink() {}
|
msvc_sink() = default;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void sink_it_(const details::log_msg &msg) override
|
void sink_it_(const details::log_msg &msg) override
|
||||||
{
|
{
|
||||||
|
|
||||||
memory_buf_t formatted;
|
memory_buf_t formatted;
|
||||||
base_sink<Mutex>::formatter_->format(msg, formatted);
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
OutputDebugStringA(fmt::to_string(formatted).c_str());
|
OutputDebugStringA(fmt::to_string(formatted).c_str());
|
||||||
|
|||||||
@@ -11,6 +11,15 @@
|
|||||||
#include <spdlog/pattern_formatter.h>
|
#include <spdlog/pattern_formatter.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675)
|
||||||
|
// so instead we use ::FileWrite
|
||||||
|
#include <spdlog/details/windows_include.h>
|
||||||
|
#include <fileapi.h> // WriteFile (..)
|
||||||
|
#include <io.h> // _get_osfhandle(..)
|
||||||
|
#include <stdio.h> // _fileno(..)
|
||||||
|
#endif // WIN32
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
|
|
||||||
namespace sinks {
|
namespace sinks {
|
||||||
@@ -20,7 +29,16 @@ SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file)
|
|||||||
: mutex_(ConsoleMutex::mutex())
|
: mutex_(ConsoleMutex::mutex())
|
||||||
, file_(file)
|
, file_(file)
|
||||||
, formatter_(details::make_unique<spdlog::pattern_formatter>())
|
, formatter_(details::make_unique<spdlog::pattern_formatter>())
|
||||||
{}
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
// get windows handle from the FILE* object
|
||||||
|
handle_ = (HANDLE)::_get_osfhandle(::_fileno(file_));
|
||||||
|
if (handle_ == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("spdlog::stdout_sink_base: _get_osfhandle() failed", errno);
|
||||||
|
}
|
||||||
|
#endif // WIN32
|
||||||
|
}
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
template<typename ConsoleMutex>
|
||||||
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg)
|
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg)
|
||||||
@@ -28,8 +46,19 @@ SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &m
|
|||||||
std::lock_guard<mutex_t> lock(mutex_);
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
memory_buf_t formatted;
|
memory_buf_t formatted;
|
||||||
formatter_->format(msg, formatted);
|
formatter_->format(msg, formatted);
|
||||||
fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
|
#ifdef _WIN32
|
||||||
fflush(file_); // flush every line to terminal
|
::fflush(file_); // flush in case there is somthing in this file_ already
|
||||||
|
auto size = static_cast<DWORD>(formatted.size());
|
||||||
|
DWORD bytes_written = 0;
|
||||||
|
bool ok = ::WriteFile(handle_, formatted.data(), size, &bytes_written, nullptr) != 0;
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("stdout_sink_base: WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
::fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
|
||||||
|
::fflush(file_); // flush every line to terminal
|
||||||
|
#endif // WIN32
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
template<typename ConsoleMutex>
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
#include <spdlog/sinks/sink.h>
|
#include <spdlog/sinks/sink.h>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <spdlog/details/windows_include.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
|
|
||||||
namespace sinks {
|
namespace sinks {
|
||||||
@@ -36,6 +40,9 @@ protected:
|
|||||||
mutex_t &mutex_;
|
mutex_t &mutex_;
|
||||||
FILE *file_;
|
FILE *file_;
|
||||||
std::unique_ptr<spdlog::formatter> formatter_;
|
std::unique_ptr<spdlog::formatter> formatter_;
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE handle_;
|
||||||
|
#endif // WIN32
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
template<typename ConsoleMutex>
|
||||||
|
|||||||
@@ -146,22 +146,12 @@ void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::write_to_file_(const memory_buf_
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto size = static_cast<DWORD>(formatted.size());
|
auto size = static_cast<DWORD>(formatted.size());
|
||||||
if (size == 0)
|
DWORD bytes_written = 0;
|
||||||
|
bool ok = ::WriteFile(out_handle_, formatted.data(), size, &bytes_written, nullptr) != 0;
|
||||||
|
if (!ok)
|
||||||
{
|
{
|
||||||
return;
|
throw_spdlog_ex("wincolor_sink: ::WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD total_written = 0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
DWORD bytes_written = 0;
|
|
||||||
bool ok = ::WriteFile(out_handle_, formatted.data() + total_written, size - total_written, &bytes_written, nullptr) != 0;
|
|
||||||
if (!ok || bytes_written == 0)
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("wincolor_sink: write_to_file_ failed. GetLastError(): " + std::to_string(::GetLastError()));
|
|
||||||
}
|
|
||||||
total_written += bytes_written;
|
|
||||||
} while (total_written < size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// wincolor_stdout_sink
|
// wincolor_stdout_sink
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ using default_factory = synchronous_factory;
|
|||||||
// Example:
|
// Example:
|
||||||
// spdlog::create<daily_file_sink_st>("logger_name", "dailylog_filename", 11, 59);
|
// spdlog::create<daily_file_sink_st>("logger_name", "dailylog_filename", 11, 59);
|
||||||
template<typename Sink, typename... SinkArgs>
|
template<typename Sink, typename... SinkArgs>
|
||||||
inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... sink_args)
|
inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...sink_args)
|
||||||
{
|
{
|
||||||
return default_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
return default_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ SPDLOG_API void set_automatic_registration(bool automatic_registration);
|
|||||||
//
|
//
|
||||||
// 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 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.
|
||||||
@@ -128,51 +128,51 @@ SPDLOG_API spdlog::logger *default_logger_raw();
|
|||||||
SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
|
SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void log(source_loc source, level::level_enum lvl, const FormatString &fmt, const Args &... args)
|
inline void log(source_loc source, level::level_enum lvl, const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->log(source, lvl, fmt, args...);
|
default_logger_raw()->log(source, lvl, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void log(level::level_enum lvl, const FormatString &fmt, const Args &... args)
|
inline void log(level::level_enum lvl, const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->log(source_loc{}, lvl, fmt, args...);
|
default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void trace(const FormatString &fmt, const Args &... args)
|
inline void trace(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->trace(fmt, args...);
|
default_logger_raw()->trace(fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void debug(const FormatString &fmt, const Args &... args)
|
inline void debug(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->debug(fmt, args...);
|
default_logger_raw()->debug(fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void info(const FormatString &fmt, const Args &... args)
|
inline void info(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->info(fmt, args...);
|
default_logger_raw()->info(fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void warn(const FormatString &fmt, const Args &... args)
|
inline void warn(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->warn(fmt, args...);
|
default_logger_raw()->warn(fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void error(const FormatString &fmt, const Args &... args)
|
inline void error(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->error(fmt, args...);
|
default_logger_raw()->error(fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void critical(const FormatString &fmt, const Args &... args)
|
inline void critical(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->critical(fmt, args...);
|
default_logger_raw()->critical(fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#include <spdlog/fmt/fmt.h>
|
#include <spdlog/fmt/fmt.h>
|
||||||
|
|
||||||
// Stopwatch support for spdlog (using std::chrono::high_resolution_clock).
|
// Stopwatch support for spdlog (using std::chrono::steady_clock).
|
||||||
// Displays elapsed seconds since construction as double.
|
// Displays elapsed seconds since construction as double.
|
||||||
//
|
//
|
||||||
// Usage:
|
// Usage:
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
class stopwatch
|
class stopwatch
|
||||||
{
|
{
|
||||||
using clock = std::chrono::high_resolution_clock;
|
using clock = std::chrono::steady_clock;
|
||||||
std::chrono::time_point<clock> start_tp_;
|
std::chrono::time_point<clock> start_tp_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -5,6 +5,6 @@
|
|||||||
|
|
||||||
#define SPDLOG_VER_MAJOR 1
|
#define SPDLOG_VER_MAJOR 1
|
||||||
#define SPDLOG_VER_MINOR 8
|
#define SPDLOG_VER_MINOR 8
|
||||||
#define SPDLOG_VER_PATCH 0
|
#define SPDLOG_VER_PATCH 2
|
||||||
|
|
||||||
#define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH)
|
#define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH)
|
||||||
|
|||||||
82
src/fmt.cpp
82
src/fmt.cpp
@@ -9,29 +9,63 @@
|
|||||||
#if !defined(SPDLOG_FMT_EXTERNAL)
|
#if !defined(SPDLOG_FMT_EXTERNAL)
|
||||||
#include <spdlog/fmt/bundled/format-inl.h>
|
#include <spdlog/fmt/bundled/format-inl.h>
|
||||||
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
int format_float(char *buf, std::size_t size, const char *format, int precision, T value)
|
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
||||||
{
|
T value) {
|
||||||
#ifdef FMT_FUZZ
|
#ifdef FMT_FUZZ
|
||||||
if (precision > 100000)
|
if (precision > 100000)
|
||||||
throw std::runtime_error("fuzz mode - avoid large allocation inside snprintf");
|
throw std::runtime_error(
|
||||||
|
"fuzz mode - avoid large allocation inside snprintf");
|
||||||
#endif
|
#endif
|
||||||
// Suppress the warning about nonliteral format string.
|
// Suppress the warning about nonliteral format string.
|
||||||
int (*snprintf_ptr)(char *, size_t, const char *, ...) = FMT_SNPRINTF;
|
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
|
||||||
return precision < 0 ? snprintf_ptr(buf, size, format, value) : snprintf_ptr(buf, size, format, precision, value);
|
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
||||||
|
: snprintf_ptr(buf, size, format, precision, value);
|
||||||
}
|
}
|
||||||
} // namespace detail
|
|
||||||
|
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
// DEPRECATED! This function exists for ABI compatibility.
|
||||||
|
template <typename Char>
|
||||||
|
typename basic_format_context<std::back_insert_iterator<buffer<Char>>,
|
||||||
|
Char>::iterator
|
||||||
|
vformat_to(buffer<Char>& buf, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<basic_format_context<
|
||||||
|
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
|
||||||
|
type_identity_t<Char>>>
|
||||||
|
args) {
|
||||||
|
using iterator = std::back_insert_iterator<buffer<char>>;
|
||||||
|
using context = basic_format_context<
|
||||||
|
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
|
||||||
|
type_identity_t<Char>>;
|
||||||
|
auto out = iterator(buf);
|
||||||
|
format_handler<iterator, Char, context> h(out, format_str, args, {});
|
||||||
|
parse_format_string<false>(format_str, h);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
template basic_format_context<std::back_insert_iterator<buffer<char>>,
|
||||||
|
char>::iterator
|
||||||
|
vformat_to(buffer<char>&, string_view,
|
||||||
|
basic_format_args<basic_format_context<
|
||||||
|
std::back_insert_iterator<buffer<type_identity_t<char>>>,
|
||||||
|
type_identity_t<char>>>);
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
|
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
|
||||||
|
|
||||||
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
||||||
int (*instantiate_format_float)(double, int, detail::float_specs, detail::buffer<char> &) = detail::format_float;
|
int (*instantiate_format_float)(double, int, detail::float_specs,
|
||||||
|
detail::buffer<char>&) = detail::format_float;
|
||||||
|
|
||||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
template FMT_API detail::locale_ref::locale_ref(const std::locale &loc);
|
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
|
||||||
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
|
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -41,15 +75,21 @@ template FMT_API std::string detail::grouping_impl<char>(locale_ref);
|
|||||||
template FMT_API char detail::thousands_sep_impl(locale_ref);
|
template FMT_API char detail::thousands_sep_impl(locale_ref);
|
||||||
template FMT_API char detail::decimal_point_impl(locale_ref);
|
template FMT_API char detail::decimal_point_impl(locale_ref);
|
||||||
|
|
||||||
template FMT_API void detail::buffer<char>::append(const char *, const char *);
|
template FMT_API void detail::buffer<char>::append(const char*, const char*);
|
||||||
|
|
||||||
template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to(
|
template FMT_API void detail::vformat_to(
|
||||||
detail::buffer<char> &, string_view, basic_format_args<FMT_BUFFER_CONTEXT(char)>);
|
detail::buffer<char>&, string_view,
|
||||||
|
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
|
||||||
|
|
||||||
template FMT_API int detail::snprintf_float(double, int, detail::float_specs, detail::buffer<char> &);
|
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
|
||||||
template FMT_API int detail::snprintf_float(long double, int, detail::float_specs, detail::buffer<char> &);
|
detail::buffer<char>&);
|
||||||
template FMT_API int detail::format_float(double, int, detail::float_specs, detail::buffer<char> &);
|
template FMT_API int detail::snprintf_float(long double, int,
|
||||||
template FMT_API int detail::format_float(long double, int, detail::float_specs, detail::buffer<char> &);
|
detail::float_specs,
|
||||||
|
detail::buffer<char>&);
|
||||||
|
template FMT_API int detail::format_float(double, int, detail::float_specs,
|
||||||
|
detail::buffer<char>&);
|
||||||
|
template FMT_API int detail::format_float(long double, int, detail::float_specs,
|
||||||
|
detail::buffer<char>&);
|
||||||
|
|
||||||
// Explicit instantiations for wchar_t.
|
// Explicit instantiations for wchar_t.
|
||||||
|
|
||||||
@@ -57,7 +97,7 @@ template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref);
|
|||||||
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
|
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
|
||||||
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
|
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
|
||||||
|
|
||||||
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t *, const wchar_t *);
|
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
|
||||||
|
const wchar_t*);
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
#endif // !SPDLOG_FMT_EXTERNAL
|
||||||
#endif // !SPDLOG_FMT_EXTERNAL
|
|
||||||
|
|||||||
@@ -1,47 +1,47 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(spdlog_utests CXX)
|
project(spdlog_utests CXX)
|
||||||
|
|
||||||
if (NOT TARGET spdlog)
|
if(NOT TARGET spdlog)
|
||||||
# Stand-alone build
|
# Stand-alone build
|
||||||
find_package(spdlog REQUIRED)
|
find_package(spdlog REQUIRED)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
include(../cmake/utils.cmake)
|
include(../cmake/utils.cmake)
|
||||||
|
|
||||||
find_package(PkgConfig)
|
find_package(PkgConfig)
|
||||||
if (PkgConfig_FOUND)
|
if(PkgConfig_FOUND)
|
||||||
pkg_check_modules(systemd libsystemd)
|
pkg_check_modules(systemd libsystemd)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
set(SPDLOG_UTESTS_SOURCES
|
set(SPDLOG_UTESTS_SOURCES
|
||||||
test_file_helper.cpp
|
test_file_helper.cpp
|
||||||
test_file_logging.cpp
|
test_file_logging.cpp
|
||||||
test_daily_logger.cpp
|
test_daily_logger.cpp
|
||||||
test_misc.cpp
|
test_misc.cpp
|
||||||
test_eventlog.cpp
|
test_eventlog.cpp
|
||||||
test_pattern_formatter.cpp
|
test_pattern_formatter.cpp
|
||||||
test_async.cpp
|
test_async.cpp
|
||||||
test_registry.cpp
|
test_registry.cpp
|
||||||
test_macros.cpp
|
test_macros.cpp
|
||||||
utils.cpp
|
utils.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
test_mpmc_q.cpp
|
test_mpmc_q.cpp
|
||||||
test_dup_filter.cpp
|
test_dup_filter.cpp
|
||||||
test_fmt_helper.cpp
|
test_fmt_helper.cpp
|
||||||
test_stdout_api.cpp
|
test_stdout_api.cpp
|
||||||
test_backtrace.cpp
|
test_backtrace.cpp
|
||||||
test_create_dir.cpp
|
test_create_dir.cpp
|
||||||
test_cfg.cpp
|
test_cfg.cpp
|
||||||
test_time_point.cpp
|
test_time_point.cpp
|
||||||
test_stopwatch.cpp)
|
test_stopwatch.cpp)
|
||||||
|
|
||||||
if (NOT SPDLOG_NO_EXCEPTIONS)
|
if(NOT SPDLOG_NO_EXCEPTIONS)
|
||||||
list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp)
|
list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
if (systemd_FOUND)
|
if(systemd_FOUND)
|
||||||
list(APPEND SPDLOG_UTESTS_SOURCES test_systemd.cpp)
|
list(APPEND SPDLOG_UTESTS_SOURCES test_systemd.cpp)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
||||||
@@ -49,22 +49,22 @@ function(spdlog_prepare_test test_target spdlog_lib)
|
|||||||
add_executable(${test_target} ${SPDLOG_UTESTS_SOURCES})
|
add_executable(${test_target} ${SPDLOG_UTESTS_SOURCES})
|
||||||
spdlog_enable_warnings(${test_target})
|
spdlog_enable_warnings(${test_target})
|
||||||
target_link_libraries(${test_target} PRIVATE ${spdlog_lib})
|
target_link_libraries(${test_target} PRIVATE ${spdlog_lib})
|
||||||
if (systemd_FOUND)
|
if(systemd_FOUND)
|
||||||
target_link_libraries(${test_target} PRIVATE ${systemd_LIBRARIES})
|
target_link_libraries(${test_target} PRIVATE ${systemd_LIBRARIES})
|
||||||
endif ()
|
endif()
|
||||||
if (SPDLOG_SANITIZE_ADDRESS)
|
if(SPDLOG_SANITIZE_ADDRESS)
|
||||||
spdlog_enable_sanitizer(${test_target})
|
spdlog_enable_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()
|
||||||
|
|
||||||
# The compiled library tests
|
# The compiled library tests
|
||||||
if (SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_ALL)
|
if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_ALL)
|
||||||
spdlog_prepare_test(spdlog-utests spdlog::spdlog)
|
spdlog_prepare_test(spdlog-utests spdlog::spdlog)
|
||||||
endif ()
|
endif()
|
||||||
|
|
||||||
# The header-only library version tests
|
# The header-only library version tests
|
||||||
if (SPDLOG_BUILD_TESTS_HO OR SPDLOG_BUILD_ALL)
|
if(SPDLOG_BUILD_TESTS_HO OR SPDLOG_BUILD_ALL)
|
||||||
spdlog_prepare_test(spdlog-utests-ho spdlog::spdlog_header_only)
|
spdlog_prepare_test(spdlog-utests-ho spdlog::spdlog_header_only)
|
||||||
endif ()
|
endif()
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ TEST_CASE("async periodic flush", "[async]")
|
|||||||
auto test_sink = std::static_pointer_cast<spdlog::sinks::test_sink_mt>(logger->sinks()[0]);
|
auto test_sink = std::static_pointer_cast<spdlog::sinks::test_sink_mt>(logger->sinks()[0]);
|
||||||
|
|
||||||
spdlog::flush_every(std::chrono::seconds(1));
|
spdlog::flush_every(std::chrono::seconds(1));
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1700));
|
||||||
REQUIRE(test_sink->flush_counter() == 1);
|
REQUIRE(test_sink->flush_counter() == 1);
|
||||||
spdlog::flush_every(std::chrono::seconds(0));
|
spdlog::flush_every(std::chrono::seconds(0));
|
||||||
spdlog::drop_all();
|
spdlog::drop_all();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
#include "test_sink.h"
|
#include "test_sink.h"
|
||||||
|
|
||||||
@@ -41,21 +42,23 @@ TEST_CASE("argv2", "[cfg]")
|
|||||||
auto l1 = spdlog::create<test_sink_st>("l1");
|
auto l1 = spdlog::create<test_sink_st>("l1");
|
||||||
REQUIRE(l1->level() == spdlog::level::warn);
|
REQUIRE(l1->level() == spdlog::level::warn);
|
||||||
REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace);
|
REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace);
|
||||||
spdlog::set_level(spdlog::level::info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("argv3", "[cfg]")
|
TEST_CASE("argv3", "[cfg]")
|
||||||
{
|
{
|
||||||
|
spdlog::set_level(spdlog::level::trace);
|
||||||
|
|
||||||
spdlog::drop("l1");
|
spdlog::drop("l1");
|
||||||
const char *argv[] = {"ignore", "SPDLOG_LEVEL="};
|
const char *argv[] = {"ignore", "SPDLOG_LEVEL=junk_name=warn"};
|
||||||
load_argv_levels(2, argv);
|
load_argv_levels(2, argv);
|
||||||
auto l1 = spdlog::create<test_sink_st>("l1");
|
auto l1 = spdlog::create<test_sink_st>("l1");
|
||||||
REQUIRE(l1->level() == spdlog::level::info);
|
REQUIRE(l1->level() == spdlog::level::trace);
|
||||||
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
|
REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("argv4", "[cfg]")
|
TEST_CASE("argv4", "[cfg]")
|
||||||
{
|
{
|
||||||
|
spdlog::set_level(spdlog::level::info);
|
||||||
spdlog::drop("l1");
|
spdlog::drop("l1");
|
||||||
const char *argv[] = {"ignore", "SPDLOG_LEVEL=junk"};
|
const char *argv[] = {"ignore", "SPDLOG_LEVEL=junk"};
|
||||||
load_argv_levels(2, argv);
|
load_argv_levels(2, argv);
|
||||||
@@ -65,6 +68,7 @@ TEST_CASE("argv4", "[cfg]")
|
|||||||
|
|
||||||
TEST_CASE("argv5", "[cfg]")
|
TEST_CASE("argv5", "[cfg]")
|
||||||
{
|
{
|
||||||
|
spdlog::set_level(spdlog::level::info);
|
||||||
spdlog::drop("l1");
|
spdlog::drop("l1");
|
||||||
const char *argv[] = {"ignore", "ignore", "SPDLOG_LEVEL=l1=warn,trace"};
|
const char *argv[] = {"ignore", "ignore", "SPDLOG_LEVEL=l1=warn,trace"};
|
||||||
load_argv_levels(3, argv);
|
load_argv_levels(3, argv);
|
||||||
@@ -91,3 +95,89 @@ TEST_CASE("argv7", "[cfg]")
|
|||||||
REQUIRE(spdlog::default_logger()->level() == spdlog::level::err);
|
REQUIRE(spdlog::default_logger()->level() == spdlog::level::err);
|
||||||
spdlog::set_level(spdlog::level::info);
|
spdlog::set_level(spdlog::level::info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("level-not-set-test1", "[cfg]")
|
||||||
|
{
|
||||||
|
spdlog::drop("l1");
|
||||||
|
const char *argv[] = {"ignore", ""};
|
||||||
|
load_argv_levels(2, argv);
|
||||||
|
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
|
||||||
|
l1->set_level(spdlog::level::trace);
|
||||||
|
REQUIRE(l1->level() == spdlog::level::trace);
|
||||||
|
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("level-not-set-test2", "[cfg]")
|
||||||
|
{
|
||||||
|
spdlog::drop("l1");
|
||||||
|
spdlog::drop("l2");
|
||||||
|
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace"};
|
||||||
|
|
||||||
|
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
|
||||||
|
l1->set_level(spdlog::level::warn);
|
||||||
|
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
|
||||||
|
l2->set_level(spdlog::level::warn);
|
||||||
|
|
||||||
|
load_argv_levels(2, argv);
|
||||||
|
|
||||||
|
REQUIRE(l1->level() == spdlog::level::trace);
|
||||||
|
REQUIRE(l2->level() == spdlog::level::warn);
|
||||||
|
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("level-not-set-test3", "[cfg]")
|
||||||
|
{
|
||||||
|
spdlog::drop("l1");
|
||||||
|
spdlog::drop("l2");
|
||||||
|
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace"};
|
||||||
|
|
||||||
|
load_argv_levels(2, argv);
|
||||||
|
|
||||||
|
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
|
||||||
|
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
|
||||||
|
|
||||||
|
REQUIRE(l1->level() == spdlog::level::trace);
|
||||||
|
REQUIRE(l2->level() == spdlog::level::info);
|
||||||
|
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("level-not-set-test4", "[cfg]")
|
||||||
|
{
|
||||||
|
spdlog::drop("l1");
|
||||||
|
spdlog::drop("l2");
|
||||||
|
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace,warn"};
|
||||||
|
|
||||||
|
load_argv_levels(2, argv);
|
||||||
|
|
||||||
|
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
|
||||||
|
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
|
||||||
|
|
||||||
|
REQUIRE(l1->level() == spdlog::level::trace);
|
||||||
|
REQUIRE(l2->level() == spdlog::level::warn);
|
||||||
|
REQUIRE(spdlog::default_logger()->level() == spdlog::level::warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("level-not-set-test5", "[cfg]")
|
||||||
|
{
|
||||||
|
spdlog::drop("l1");
|
||||||
|
spdlog::drop("l2");
|
||||||
|
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=junk,warn"};
|
||||||
|
|
||||||
|
load_argv_levels(2, argv);
|
||||||
|
|
||||||
|
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
|
||||||
|
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
|
||||||
|
|
||||||
|
REQUIRE(l1->level() == spdlog::level::warn);
|
||||||
|
REQUIRE(l2->level() == spdlog::level::warn);
|
||||||
|
REQUIRE(spdlog::default_logger()->level() == spdlog::level::warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("restore-to-default", "[cfg]")
|
||||||
|
{
|
||||||
|
spdlog::drop("l1");
|
||||||
|
spdlog::drop("l2");
|
||||||
|
const char *argv[] = {"ignore", "SPDLOG_LEVEL=info"};
|
||||||
|
load_argv_levels(2, argv);
|
||||||
|
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using spdlog::memory_buf_t;
|
|||||||
|
|
||||||
// log to str and return it
|
// log to str and return it
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
static std::string log_to_str(const std::string &msg, const Args &... args)
|
static std::string log_to_str(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);
|
||||||
|
|||||||
Reference in New Issue
Block a user