From acbf18d0dd793452395a9b4de26dfd413d91bf59 Mon Sep 17 00:00:00 2001 From: sunlong169 <71108420+sunlong169@users.noreply.github.com> Date: Sat, 16 Oct 2021 23:52:01 +0800 Subject: [PATCH 001/121] No need to define the Mutex mutex_ as mutable there is no const method. There's no need to define the Mutex mutex_ as mutable since class base_sink has no const method. --- include/spdlog/sinks/base_sink.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/sinks/base_sink.h b/include/spdlog/sinks/base_sink.h index a0624e94..21c5545f 100644 --- a/include/spdlog/sinks/base_sink.h +++ b/include/spdlog/sinks/base_sink.h @@ -37,7 +37,7 @@ public: protected: // sink formatter std::unique_ptr formatter_; - mutable Mutex mutex_; + Mutex mutex_; virtual void sink_it_(const details::log_msg &msg) = 0; virtual void flush_() = 0; From c47ae3b15dedc35cc62cc34ec1e411b040373776 Mon Sep 17 00:00:00 2001 From: seker <04070628@163.com> Date: Thu, 11 Nov 2021 00:08:24 +0800 Subject: [PATCH 002/121] add file event handlers --- example/example.cpp | 17 ++++++++++++++++- include/spdlog/common.h | 8 ++++++++ include/spdlog/details/file_helper-inl.h | 18 ++++++++++++++++++ include/spdlog/details/file_helper.h | 4 +++- include/spdlog/sinks/basic_file_sink-inl.h | 3 ++- include/spdlog/sinks/basic_file_sink.h | 10 +++++----- include/spdlog/sinks/daily_file_sink.h | 19 ++++++++++--------- include/spdlog/sinks/hourly_file_sink.h | 11 ++++++----- include/spdlog/sinks/rotating_file_sink-inl.h | 3 ++- include/spdlog/sinks/rotating_file_sink.h | 10 +++++----- 10 files changed, 75 insertions(+), 28 deletions(-) diff --git a/example/example.cpp b/example/example.cpp index 3fbde21a..56e28825 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -120,7 +120,22 @@ void basic_example() void rotating_example() { // Create a file rotating logger with 5mb size max and 3 rotated files. - auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3); + spdlog::file_event_handlers_t file_event_handlers; + file_event_handlers.after_open = [](spdlog::filename_t filename, std::FILE* fstream) + { + fputs("OPEN!\r\n", fstream); + spdlog::info("basic_example() : file_event_handlers.after_open : {}", filename.c_str()); + }; + file_event_handlers.before_close = [](spdlog::filename_t filename, std::FILE* fstream) + { + fputs("CLOSE!\r\n", fstream); + spdlog::info("basic_example() : file_event_handlers.before_close : {}", filename.c_str()); + }; + file_event_handlers.after_close = [](spdlog::filename_t filename) + { + spdlog::info("basic_example() : file_event_handlers.after_close : {}", filename.c_str()); + }; + auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3, false, file_event_handlers); } #include "spdlog/sinks/daily_file_sink.h" diff --git a/include/spdlog/common.h b/include/spdlog/common.h index ed0ab46c..ddaa34ce 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -14,6 +14,7 @@ #include #include #include +#include #ifdef SPDLOG_COMPILED_LIB # undef SPDLOG_HEADER_ONLY @@ -255,6 +256,13 @@ struct source_loc const char *funcname{nullptr}; }; +typedef struct +{ + std::function after_open; + std::function before_close; + std::function after_close; +} file_event_handlers_t; + namespace details { // make_unique support for pre c++14 diff --git a/include/spdlog/details/file_helper-inl.h b/include/spdlog/details/file_helper-inl.h index ac7021a9..df6cd4c1 100644 --- a/include/spdlog/details/file_helper-inl.h +++ b/include/spdlog/details/file_helper-inl.h @@ -20,6 +20,10 @@ namespace spdlog { namespace details { +SPDLOG_INLINE file_helper::file_helper(const file_event_handlers_t& event_handlers) + : event_handlers_(event_handlers) +{} + SPDLOG_INLINE file_helper::~file_helper() { close(); @@ -52,6 +56,10 @@ SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate) } if (!os::fopen_s(&fd_, fname, mode)) { + if (event_handlers_.after_open) + { + event_handlers_.after_open(filename_, fd_); + } return; } @@ -79,8 +87,18 @@ SPDLOG_INLINE void file_helper::close() { if (fd_ != nullptr) { + if (event_handlers_.before_close) + { + event_handlers_.before_close(filename_, fd_); + } + std::fclose(fd_); fd_ = nullptr; + + if (event_handlers_.after_close) + { + event_handlers_.after_close(filename_); + } } } diff --git a/include/spdlog/details/file_helper.h b/include/spdlog/details/file_helper.h index cfccaed2..60c47788 100644 --- a/include/spdlog/details/file_helper.h +++ b/include/spdlog/details/file_helper.h @@ -16,7 +16,8 @@ namespace details { class SPDLOG_API file_helper { public: - explicit file_helper() = default; + file_helper() = default; + explicit file_helper(const file_event_handlers_t& event_handlers); file_helper(const file_helper &) = delete; file_helper &operator=(const file_helper &) = delete; @@ -50,6 +51,7 @@ private: const unsigned int open_interval_ = 10; std::FILE *fd_{nullptr}; filename_t filename_; + file_event_handlers_t event_handlers_{nullptr, nullptr, nullptr}; }; } // namespace details } // namespace spdlog diff --git a/include/spdlog/sinks/basic_file_sink-inl.h b/include/spdlog/sinks/basic_file_sink-inl.h index e903dd93..463e075f 100644 --- a/include/spdlog/sinks/basic_file_sink-inl.h +++ b/include/spdlog/sinks/basic_file_sink-inl.h @@ -14,7 +14,8 @@ namespace spdlog { namespace sinks { template -SPDLOG_INLINE basic_file_sink::basic_file_sink(const filename_t &filename, bool truncate) +SPDLOG_INLINE basic_file_sink::basic_file_sink(const filename_t &filename, bool truncate, const file_event_handlers_t& event_handlers) + : file_helper_{event_handlers} { file_helper_.open(filename, truncate); } diff --git a/include/spdlog/sinks/basic_file_sink.h b/include/spdlog/sinks/basic_file_sink.h index 047bc8ce..51e26e38 100644 --- a/include/spdlog/sinks/basic_file_sink.h +++ b/include/spdlog/sinks/basic_file_sink.h @@ -20,7 +20,7 @@ template class basic_file_sink final : public base_sink { public: - explicit basic_file_sink(const filename_t &filename, bool truncate = false); + explicit basic_file_sink(const filename_t &filename, bool truncate = false, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}); const filename_t &filename() const; protected: @@ -40,15 +40,15 @@ using basic_file_sink_st = basic_file_sink; // factory functions // template -inline std::shared_ptr basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false) +inline std::shared_ptr basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) { - return Factory::template create(logger_name, filename, truncate); + return Factory::template create(logger_name, filename, truncate, event_handlers); } template -inline std::shared_ptr basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false) +inline std::shared_ptr basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) { - return Factory::template create(logger_name, filename, truncate); + return Factory::template create(logger_name, filename, truncate, event_handlers); } } // namespace spdlog diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index d6a09f6b..fc552398 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -68,10 +68,11 @@ class daily_file_sink final : public base_sink { public: // create daily file sink which rotates on given time - daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0) + daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) : base_filename_(std::move(base_filename)) , rotation_h_(rotation_hour) , rotation_m_(rotation_minute) + , file_helper_{event_handlers} , truncate_(truncate) , max_files_(max_files) , filenames_q_() @@ -214,29 +215,29 @@ using daily_file_format_sink_st = daily_file_sink inline std::shared_ptr daily_logger_mt( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0) + const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) { - return Factory::template create(logger_name, filename, hour, minute, truncate, max_files); + return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); } template inline std::shared_ptr daily_logger_format_mt( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0) + const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) { - return Factory::template create(logger_name, filename, hour, minute, truncate, max_files); + return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); } template inline std::shared_ptr daily_logger_st( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0) + const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) { - return Factory::template create(logger_name, filename, hour, minute, truncate, max_files); + return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); } template inline std::shared_ptr daily_logger_format_st( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0) + const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) { - return Factory::template create(logger_name, filename, hour, minute, truncate, max_files); + return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); } } // namespace spdlog diff --git a/include/spdlog/sinks/hourly_file_sink.h b/include/spdlog/sinks/hourly_file_sink.h index 815781d9..653faceb 100644 --- a/include/spdlog/sinks/hourly_file_sink.h +++ b/include/spdlog/sinks/hourly_file_sink.h @@ -46,8 +46,9 @@ class hourly_file_sink final : public base_sink { public: // create hourly file sink which rotates on given time - hourly_file_sink(filename_t base_filename, bool truncate = false, uint16_t max_files = 0) + hourly_file_sink(filename_t base_filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) : base_filename_(std::move(base_filename)) + , file_helper_{event_handlers} , truncate_(truncate) , max_files_(max_files) , filenames_q_() @@ -180,15 +181,15 @@ using hourly_file_sink_st = hourly_file_sink; // template inline std::shared_ptr hourly_logger_mt( - const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0) + const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) { - return Factory::template create(logger_name, filename, truncate, max_files); + return Factory::template create(logger_name, filename, truncate, max_files, event_handlers); } template inline std::shared_ptr hourly_logger_st( - const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0) + const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) { - return Factory::template create(logger_name, filename, truncate, max_files); + return Factory::template create(logger_name, filename, truncate, max_files, event_handlers); } } // namespace spdlog diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h index f31f5319..2e3087af 100644 --- a/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -25,10 +25,11 @@ namespace sinks { template SPDLOG_INLINE rotating_file_sink::rotating_file_sink( - filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open) + filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open, const file_event_handlers_t& event_handlers) : base_filename_(std::move(base_filename)) , max_size_(max_size) , max_files_(max_files) + , file_helper_{event_handlers} { file_helper_.open(calc_filename(base_filename_, 0)); current_size_ = file_helper_.size(); // expensive. called only once diff --git a/include/spdlog/sinks/rotating_file_sink.h b/include/spdlog/sinks/rotating_file_sink.h index 842527ef..8455227f 100644 --- a/include/spdlog/sinks/rotating_file_sink.h +++ b/include/spdlog/sinks/rotating_file_sink.h @@ -22,7 +22,7 @@ template class rotating_file_sink final : public base_sink { public: - rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false); + rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}); static filename_t calc_filename(const filename_t &filename, std::size_t index); filename_t filename(); @@ -60,16 +60,16 @@ using rotating_file_sink_st = rotating_file_sink; template inline std::shared_ptr rotating_logger_mt( - const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false) + const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) { - return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open); + return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers); } template inline std::shared_ptr rotating_logger_st( - const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false) + const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) { - return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open); + return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers); } } // namespace spdlog From 44a4517e2b1189bde950b86cbf6c2c5943a5a55a Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Sat, 13 Nov 2021 11:29:05 -0500 Subject: [PATCH 003/121] Support C++20 std::format as an alternative to fmtlib --- CMakeLists.txt | 15 ++- appveyor.yml | 19 +++- bench/async_bench.cpp | 4 +- bench/bench.cpp | 14 +-- include/spdlog/common-inl.h | 4 + include/spdlog/common.h | 48 +++++++-- include/spdlog/details/fmt_helper.h | 54 ++++++++++- include/spdlog/details/os-inl.h | 16 ++- include/spdlog/details/tcp_client-windows.h | 4 +- include/spdlog/details/tcp_client.h | 3 +- include/spdlog/details/udp_client-windows.h | 4 +- include/spdlog/fmt/bin_to_hex.h | 12 +-- include/spdlog/fmt/chrono.h | 16 +-- include/spdlog/fmt/compile.h | 18 ++-- include/spdlog/fmt/fmt.h | 4 +- include/spdlog/fmt/ostr.h | 16 +-- include/spdlog/fmt/xchar.h | 18 ++-- include/spdlog/logger.h | 97 +++++++++++++------ include/spdlog/sinks/daily_file_sink.h | 8 +- include/spdlog/sinks/hourly_file_sink.h | 2 +- include/spdlog/sinks/msvc_sink.h | 4 + include/spdlog/sinks/ringbuffer_sink.h | 4 + include/spdlog/sinks/rotating_file_sink-inl.h | 2 +- include/spdlog/sinks/win_eventlog_sink.h | 33 +++++-- include/spdlog/spdlog.h | 32 +++--- include/spdlog/tweakme.h | 7 ++ src/fmt.cpp | 2 +- tests/test_async.cpp | 2 +- tests/test_daily_logger.cpp | 30 +++++- tests/test_errors.cpp | 2 +- tests/test_file_helper.cpp | 2 +- tests/test_fmt_helper.cpp | 20 ++++ tests/test_macros.cpp | 4 +- tests/test_pattern_formatter.cpp | 62 +++++++++++- 34 files changed, 442 insertions(+), 140 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 58e76f52..2d9bc279 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ option(SPDLOG_BUILD_WARNINGS "Enable compiler warnings" OFF) # install options option(SPDLOG_INSTALL "Generate the install target" ${SPDLOG_MASTER_PROJECT}) +option(SPDLOG_USE_STD_FORMAT "Use std::format instead of fmt library. No compile-time format string checking." OFF) 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_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF) @@ -88,6 +89,14 @@ if(SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO) message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive") endif() +if(SPDLOG_USE_STD_FORMAT AND SPDLOG_FMT_EXTERNAL_HO) + message(FATAL_ERROR "SPDLOG_USE_STD_FORMAT and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive") +endif() + +if(SPDLOG_USE_STD_FORMAT AND SPDLOG_FMT_EXTERNAL) + message(FATAL_ERROR "SPDLOG_USE_STD_FORMAT and SPDLOG_FMT_EXTERNAL are mutually exclusive") +endif() + # misc tweakme options if(WIN32) option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF) @@ -130,7 +139,7 @@ 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) -if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) +if(NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) list(APPEND SPDLOG_SRCS src/fmt.cpp) endif() @@ -145,7 +154,7 @@ if(SPDLOG_BUILD_SHARED OR BUILD_SHARED_LIBS) target_compile_options(spdlog PUBLIC $<$,$>>:/wd4251 /wd4275>) endif() - if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) + if(NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) target_compile_definitions(spdlog PRIVATE FMT_EXPORT PUBLIC FMT_SHARED) endif() else() @@ -280,7 +289,7 @@ if(SPDLOG_INSTALL) ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) + if(NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) install(DIRECTORY include/${PROJECT_NAME}/fmt/bundled/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/fmt/bundled/") endif() diff --git a/appveyor.yml b/appveyor.yml index b4574034..42d928e3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,55 +8,72 @@ environment: WCHAR: 'OFF' WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'ON' + USE_STD_FORMAT: 'OFF' - GENERATOR: '"Visual Studio 14 2015"' BUILD_TYPE: Release BUILD_SHARED: 'OFF' WCHAR: 'ON' WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'ON' + USE_STD_FORMAT: 'OFF' - GENERATOR: '"Visual Studio 14 2015 Win64"' BUILD_TYPE: Debug BUILD_SHARED: 'OFF' WCHAR: 'ON' WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'ON' + USE_STD_FORMAT: 'OFF' - GENERATOR: '"Visual Studio 14 2015 Win64"' BUILD_TYPE: Release BUILD_SHARED: 'OFF' WCHAR: 'ON' WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'ON' + USE_STD_FORMAT: 'OFF' - GENERATOR: '"Visual Studio 15 2017 Win64"' BUILD_TYPE: Debug BUILD_SHARED: 'OFF' WCHAR: 'ON' WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'ON' + USE_STD_FORMAT: 'OFF' - GENERATOR: '"Visual Studio 15 2017 Win64"' BUILD_TYPE: Release BUILD_SHARED: 'OFF' WCHAR: 'OFF' WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'ON' + USE_STD_FORMAT: 'OFF' - GENERATOR: '"Visual Studio 15 2017 Win64"' BUILD_TYPE: Release BUILD_SHARED: 'ON' WCHAR: 'OFF' WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'ON' + USE_STD_FORMAT: 'OFF' - GENERATOR: '"Visual Studio 15 2017 Win64"' BUILD_TYPE: Release BUILD_SHARED: 'ON' WCHAR: 'ON' WCHAR_FILES: 'ON' BUILD_EXAMPLE: 'OFF' + USE_STD_FORMAT: 'OFF' - GENERATOR: '"Visual Studio 16 2019" -A x64' BUILD_TYPE: Release BUILD_SHARED: 'ON' WCHAR: 'OFF' WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'OFF' + USE_STD_FORMAT: 'OFF' APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + - GENERATOR: '"Visual Studio 16 2022" -A x64' + BUILD_TYPE: Release + BUILD_SHARED: 'ON' + WCHAR: 'OFF' + WCHAR_FILES: 'OFF' + BUILD_EXAMPLE: 'OFF' + USE_STD_FORMAT: 'ON' + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 build_script: - cmd: >- set @@ -67,7 +84,7 @@ build_script: set PATH=%PATH%;C:\Program Files\Git\usr\bin - cmake -G %GENERATOR% -D CMAKE_BUILD_TYPE=%BUILD_TYPE% -D BUILD_SHARED_LIBS=%BUILD_SHARED% -D SPDLOG_WCHAR_SUPPORT=%WCHAR% -D SPDLOG_WCHAR_FILENAMES=%WCHAR_FILES% -D SPDLOG_BUILD_EXAMPLE=%BUILD_EXAMPLE% -D SPDLOG_BUILD_EXAMPLE_HO=%BUILD_EXAMPLE% -D SPDLOG_BUILD_TESTS=ON -D SPDLOG_BUILD_TESTS_HO=OFF -D SPDLOG_BUILD_WARNINGS=ON .. + cmake -G %GENERATOR% -D CMAKE_BUILD_TYPE=%BUILD_TYPE% -D BUILD_SHARED_LIBS=%BUILD_SHARED% -D SPDLOG_WCHAR_SUPPORT=%WCHAR% -D SPDLOG_WCHAR_FILENAMES=%WCHAR_FILES% -D SPDLOG_BUILD_EXAMPLE=%BUILD_EXAMPLE% -D SPDLOG_BUILD_EXAMPLE_HO=%BUILD_EXAMPLE% -D SPDLOG_BUILD_TESTS=ON -D SPDLOG_BUILD_TESTS_HO=OFF -D SPDLOG_BUILD_WARNINGS=ON -D SPDLOG_USE_STD_FORMAT=%USE_STD_FORMAT% .. cmake --build . --config %BUILD_TYPE% diff --git a/bench/async_bench.cpp b/bench/async_bench.cpp index 99d8ecb2..ce2c9281 100644 --- a/bench/async_bench.cpp +++ b/bench/async_bench.cpp @@ -10,7 +10,9 @@ #include "spdlog/async.h" #include "spdlog/sinks/basic_file_sink.h" -#ifdef SPDLOG_FMT_EXTERNAL +#if defined(SPDLOG_USE_STD_FORMAT) +# include +#elif defined(SPDLOG_FMT_EXTERNAL) # include #else # include "spdlog/fmt/bundled/format.h" diff --git a/bench/bench.cpp b/bench/bench.cpp index 5a4fc39b..af08c7ac 100644 --- a/bench/bench.cpp +++ b/bench/bench.cpp @@ -12,7 +12,9 @@ #include "spdlog/sinks/null_sink.h" #include "spdlog/sinks/rotating_file_sink.h" -#ifdef SPDLOG_FMT_EXTERNAL +#if defined(SPDLOG_USE_STD_FORMAT) +# include +#elif defined(SPDLOG_FMT_EXTERNAL) # include #else # include "spdlog/fmt/bundled/format.h" @@ -38,7 +40,7 @@ static const int max_threads = 1000; void bench_threaded_logging(size_t threads, int iters) { spdlog::info("**************************************************************"); - spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Multi threaded: {:L} threads, {:L} messages", threads, iters)); + spdlog::info(spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), "Multi threaded: {:L} threads, {:L} messages", threads, iters)); spdlog::info("**************************************************************"); auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true); @@ -74,7 +76,7 @@ void bench_threaded_logging(size_t threads, int iters) void bench_single_threaded(int iters) { spdlog::info("**************************************************************"); - spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Single threaded: {} messages", iters)); + spdlog::info(spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), "Single threaded: {} messages", iters)); spdlog::info("**************************************************************"); auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true); @@ -128,7 +130,7 @@ int main(int argc, char *argv[]) if (threads > max_threads) { - throw std::runtime_error(fmt::format("Number of threads exceeds maximum({})", max_threads)); + throw std::runtime_error(spdlog::fmt_lib::format("Number of threads exceeds maximum({})", max_threads)); } bench_single_threaded(iters); @@ -159,7 +161,7 @@ void bench(int howmany, std::shared_ptr log) auto delta_d = duration_cast>(delta).count(); spdlog::info( - fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d))); + spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d))); spdlog::drop(log->name()); } @@ -190,7 +192,7 @@ void bench_mt(int howmany, std::shared_ptr log, size_t thread_co auto delta = high_resolution_clock::now() - start; auto delta_d = duration_cast>(delta).count(); spdlog::info( - fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d))); + spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d))); spdlog::drop(log->name()); } diff --git a/include/spdlog/common-inl.h b/include/spdlog/common-inl.h index 97cbbddc..9fd2bcb5 100644 --- a/include/spdlog/common-inl.h +++ b/include/spdlog/common-inl.h @@ -55,9 +55,13 @@ SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg) SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno) { +#ifdef SPDLOG_USE_STD_FORMAT + msg_ = std::system_error(std::error_code(last_errno, std::generic_category()), msg).what(); +#else memory_buf_t outbuf; fmt::format_system_error(outbuf, last_errno, msg.c_str()); msg_ = fmt::to_string(outbuf); +#endif } SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT diff --git a/include/spdlog/common.h b/include/spdlog/common.h index ddaa34ce..fb45d2a2 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -16,6 +16,10 @@ #include #include +#ifdef SPDLOG_USE_STD_FORMAT +#include +#endif + #ifdef SPDLOG_COMPILED_LIB # undef SPDLOG_HEADER_ONLY # if defined(_WIN32) && defined(SPDLOG_SHARED_LIB) @@ -36,14 +40,15 @@ #include -// backward compatibility with fmt versions older than 8 -#if FMT_VERSION >= 80000 -# define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string) -# if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) -# include +#ifndef SPDLOG_USE_STD_FORMAT +# if FMT_VERSION >= 80000 // backward compatibility with fmt versions older than 8 +# define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string) +# if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) +# include +# endif +# else +# define SPDLOG_FMT_RUNTIME(format_string) format_string # endif -#else -# define SPDLOG_FMT_RUNTIME(format_string) format_string #endif // visual studio upto 2013 does not support noexcept nor constexpr @@ -112,11 +117,39 @@ using log_clock = std::chrono::system_clock; using sink_ptr = std::shared_ptr; using sinks_init_list = std::initializer_list; using err_handler = std::function; +#ifdef SPDLOG_USE_STD_FORMAT +namespace fmt_lib = std; + +using string_view_t = std::string_view; +using wstring_view_t = std::wstring_view; +using memory_buf_t = std::string; +using wmemory_buf_t = std::wstring; + +template +using format_string_t = std::string_view; + +template +using wformat_string_t = std::wstring_view; + +template +struct is_convertible_to_basic_format_string + : std::integral_constant>::value> +{}; +#else +namespace fmt_lib = fmt; + using string_view_t = fmt::basic_string_view; using wstring_view_t = fmt::basic_string_view; using memory_buf_t = fmt::basic_memory_buffer; using wmemory_buf_t = fmt::basic_memory_buffer; +template +using format_string_t = fmt::format_string; + +template +using wformat_string_t = fmt::wformat_string; + template using remove_cvref_t = typename std::remove_cv::type>::type; @@ -127,6 +160,7 @@ struct is_convertible_to_basic_format_string : std::integral_constant>::value || std::is_same, fmt::basic_runtime>::value> {}; +#endif #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT # ifndef _WIN32 diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h index 01b2aa4d..31ed64fd 100644 --- a/include/spdlog/details/fmt_helper.h +++ b/include/spdlog/details/fmt_helper.h @@ -8,6 +8,11 @@ #include #include +#ifdef SPDLOG_USE_STD_FORMAT +#include +#include +#endif + // Some fmt helpers to efficiently format and pad ints and strings namespace spdlog { namespace details { @@ -24,17 +29,63 @@ inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest) dest.append(buf_ptr, buf_ptr + view.size()); } +#ifdef SPDLOG_USE_STD_FORMAT +template +inline void append_int(T n, memory_buf_t &dest) +{ + // Buffer should be large enough to hold all digits (digits10 + 1) and a sign + SPDLOG_CONSTEXPR auto BUF_SIZE = std::numeric_limits::digits10 + 2; + char buf[BUF_SIZE]; + + auto [ptr, ec] = std::to_chars(buf, buf + BUF_SIZE, n, 10); + if (ec == std::errc()) + { + dest.append(buf, ptr); + } + else + { + throw_spdlog_ex("Failed to format int", static_cast(ec)); + } +} +#else template inline void append_int(T n, memory_buf_t &dest) { fmt::format_int i(n); dest.append(i.data(), i.data() + i.size()); } +#endif + +template +SPDLOG_CONSTEXPR unsigned int count_digits_fallback(T n) +{ + // taken from fmt. + unsigned int count = 1; + for (;;) + { + // Integer division is slow so do it for a group of four digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + if (n < 10) + return count; + if (n < 100) + return count + 1; + if (n < 1000) + return count + 2; + if (n < 10000) + return count + 3; + n /= 10000u; + count += 4; + } +} template inline unsigned int count_digits(T n) { using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type; +#ifdef SPDLOG_USE_STD_FORMAT + return count_digits_fallback(static_cast(n)); +#else return static_cast(fmt:: // fmt 7.0.0 renamed the internal namespace to detail. // See: https://github.com/fmtlib/fmt/issues/1538 @@ -44,6 +95,7 @@ inline unsigned int count_digits(T n) detail #endif ::count_digits(static_cast(n))); +#endif } inline void pad2(int n, memory_buf_t &dest) @@ -55,7 +107,7 @@ inline void pad2(int n, memory_buf_t &dest) } else // unlikely, but just in case, let fmt deal with it { - fmt::format_to(std::back_inserter(dest), SPDLOG_FMT_RUNTIME("{:02}"), n); + fmt_lib::format_to(std::back_inserter(dest), "{:02}", n); } } diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index 4602782c..9b2ef0d8 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -381,7 +381,11 @@ SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) { memory_buf_t buf; wstr_to_utf8buf(filename, buf); +#ifdef SPDLOG_USE_STD_FORMAT + return buf; +#else return fmt::to_string(buf); +#endif } #else SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) @@ -458,8 +462,12 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) return; } +#ifdef SPDLOG_USE_STD_FORMAT + int result_size = 0; +#else int result_size = static_cast(target.capacity()); if ((wstr_size + 1) * 2 > result_size) +#endif { result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL); } @@ -476,7 +484,7 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) } } - throw_spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())); + throw_spdlog_ex(fmt_lib::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())); } SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) @@ -493,8 +501,12 @@ SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) return; } +#ifdef SPDLOG_USE_STD_FORMAT + int result_size = 0; +#else int result_size = static_cast(target.capacity()); if (str_size + 1 > result_size) +#endif { result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0); } @@ -511,7 +523,7 @@ SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) } } - throw_spdlog_ex(fmt::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError())); + throw_spdlog_ex(fmt_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError())); } #endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32) diff --git a/include/spdlog/details/tcp_client-windows.h b/include/spdlog/details/tcp_client-windows.h index 45b69630..b331a500 100644 --- a/include/spdlog/details/tcp_client-windows.h +++ b/include/spdlog/details/tcp_client-windows.h @@ -38,10 +38,10 @@ class tcp_client static void throw_winsock_error_(const std::string &msg, int last_error) { char buf[512]; - ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, + ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL); - throw_spdlog_ex(fmt::format("tcp_sink - {}: {}", msg, buf)); + throw_spdlog_ex(fmt_lib::format("tcp_sink - {}: {}", msg, buf)); } public: diff --git a/include/spdlog/details/tcp_client.h b/include/spdlog/details/tcp_client.h index 706d7599..0daff0eb 100644 --- a/include/spdlog/details/tcp_client.h +++ b/include/spdlog/details/tcp_client.h @@ -67,8 +67,7 @@ public: auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result); if (rv != 0) { - auto msg = fmt::format("::getaddrinfo failed: {}", gai_strerror(rv)); - throw_spdlog_ex(msg); + throw_spdlog_ex(fmt_lib::format("::getaddrinfo failed: {}", gai_strerror(rv))); } // Try each address until we successfully connect(2). diff --git a/include/spdlog/details/udp_client-windows.h b/include/spdlog/details/udp_client-windows.h index 5cc3dd88..372efa6d 100644 --- a/include/spdlog/details/udp_client-windows.h +++ b/include/spdlog/details/udp_client-windows.h @@ -40,10 +40,10 @@ class udp_client static void throw_winsock_error_(const std::string &msg, int last_error) { char buf[512]; - ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, + ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL); - throw_spdlog_ex(fmt::format("udp_sink - {}: {}", msg, buf)); + throw_spdlog_ex(fmt_lib::format("udp_sink - {}: {}", msg, buf)); } void cleanup_() diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h index 0b6c3e96..62807d63 100644 --- a/include/spdlog/fmt/bin_to_hex.h +++ b/include/spdlog/fmt/bin_to_hex.h @@ -74,12 +74,8 @@ inline details::dump_info to_hex(const It range_begin, const It range_end, s return details::dump_info(range_begin, range_end, size_per_line); } -} // namespace spdlog - -namespace fmt { - template -struct formatter> +struct fmt_lib::formatter, char> { const char delimiter = ' '; bool put_newlines = true; @@ -90,7 +86,7 @@ struct formatter> // parse the format string flags template - FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) + SPDLOG_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { auto it = ctx.begin(); while (it != ctx.end() && *it != '}') @@ -210,8 +206,8 @@ struct formatter> if (put_positions) { - fmt::format_to(inserter, "{:04X}: ", pos); + fmt_lib::format_to(inserter, "{:04X}: ", pos); } } }; -} // namespace fmt +} // namespace spdlog diff --git a/include/spdlog/fmt/chrono.h b/include/spdlog/fmt/chrono.h index a0544bca..83fad2ff 100644 --- a/include/spdlog/fmt/chrono.h +++ b/include/spdlog/fmt/chrono.h @@ -8,13 +8,15 @@ // include bundled or external copy of fmtlib's chrono support // -#if !defined(SPDLOG_FMT_EXTERNAL) -# ifdef SPDLOG_HEADER_ONLY -# ifndef FMT_HEADER_ONLY -# define FMT_HEADER_ONLY +#if !defined(SPDLOG_USE_STD_FORMAT) +# if !defined(SPDLOG_FMT_EXTERNAL) +# ifdef SPDLOG_HEADER_ONLY +# ifndef FMT_HEADER_ONLY +# define FMT_HEADER_ONLY +# endif # endif +# include +# else +# include # endif -# include -#else -# include #endif diff --git a/include/spdlog/fmt/compile.h b/include/spdlog/fmt/compile.h index 6f345935..906e9f57 100644 --- a/include/spdlog/fmt/compile.h +++ b/include/spdlog/fmt/compile.h @@ -5,16 +5,18 @@ #pragma once // -// include bundled or external copy of fmtlib's ostream support +// include bundled or external copy of fmtlib's compile-time support // -#if !defined(SPDLOG_FMT_EXTERNAL) -# ifdef SPDLOG_HEADER_ONLY -# ifndef FMT_HEADER_ONLY -# define FMT_HEADER_ONLY +#if !defined(SPDLOG_USE_STD_FORMAT) +# if !defined(SPDLOG_FMT_EXTERNAL) +# ifdef SPDLOG_HEADER_ONLY +# ifndef FMT_HEADER_ONLY +# define FMT_HEADER_ONLY +# endif # endif +# include +# else +# include # endif -# include -#else -# include #endif diff --git a/include/spdlog/fmt/fmt.h b/include/spdlog/fmt/fmt.h index 7aaeed81..fa4a2a84 100644 --- a/include/spdlog/fmt/fmt.h +++ b/include/spdlog/fmt/fmt.h @@ -10,7 +10,9 @@ // By default spdlog include its own copy. // -#if !defined(SPDLOG_FMT_EXTERNAL) +#if defined(SPDLOG_USE_STD_FORMAT) // SPDLOG_USE_STD_FORMAT is defined - use std::format +# include +#elif !defined(SPDLOG_FMT_EXTERNAL) # if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY) # define FMT_HEADER_ONLY # endif diff --git a/include/spdlog/fmt/ostr.h b/include/spdlog/fmt/ostr.h index 9d8efe38..75880341 100644 --- a/include/spdlog/fmt/ostr.h +++ b/include/spdlog/fmt/ostr.h @@ -8,13 +8,15 @@ // include bundled or external copy of fmtlib's ostream support // -#if !defined(SPDLOG_FMT_EXTERNAL) -# ifdef SPDLOG_HEADER_ONLY -# ifndef FMT_HEADER_ONLY -# define FMT_HEADER_ONLY +#if !defined(SPDLOG_USE_STD_FORMAT) +# if !defined(SPDLOG_FMT_EXTERNAL) +# ifdef SPDLOG_HEADER_ONLY +# ifndef FMT_HEADER_ONLY +# define FMT_HEADER_ONLY +# endif # endif +# include +# else +# include # endif -# include -#else -# include #endif diff --git a/include/spdlog/fmt/xchar.h b/include/spdlog/fmt/xchar.h index 616d8f5b..9a766e5a 100644 --- a/include/spdlog/fmt/xchar.h +++ b/include/spdlog/fmt/xchar.h @@ -5,16 +5,18 @@ #pragma once // -// include bundled or external copy of fmtlib's ostream support +// include bundled or external copy of fmtlib's xchar support // -#if !defined(SPDLOG_FMT_EXTERNAL) -# ifdef SPDLOG_HEADER_ONLY -# ifndef FMT_HEADER_ONLY -# define FMT_HEADER_ONLY +#if !defined(SPDLOG_USE_STD_FORMAT) +# if !defined(SPDLOG_FMT_EXTERNAL) +# ifdef SPDLOG_HEADER_ONLY +# ifndef FMT_HEADER_ONLY +# define FMT_HEADER_ONLY +# endif # endif +# include +# else +# include # endif -# include -#else -# include #endif diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index eea0afc2..9adfec97 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -33,7 +33,7 @@ { \ if(location.filename) \ { \ - err_handler_(fmt::format("{} [{}({})]", ex.what(), location.filename, location.line)); \ + err_handler_(fmt_lib::format("{} [{}({})]", ex.what(), location.filename, location.line)); \ } \ else \ { \ @@ -85,13 +85,13 @@ public: void swap(spdlog::logger &other) SPDLOG_NOEXCEPT; template - void log(source_loc loc, level::level_enum lvl, fmt::format_string fmt, Args &&...args) + void log(source_loc loc, level::level_enum lvl, format_string_t fmt, Args &&...args) { log_(loc, lvl, fmt, std::forward(args)...); } template - void log(level::level_enum lvl, fmt::format_string fmt, Args &&...args) + void log(level::level_enum lvl, format_string_t fmt, Args &&...args) { log(source_loc{}, lvl, fmt, std::forward(args)...); } @@ -102,14 +102,7 @@ public: log(source_loc{}, lvl, msg); } - // T can be statically converted to string_view - template::value, int>::type = 0> - void log(source_loc loc, level::level_enum lvl, const T &msg) - { - log(loc, lvl, string_view_t{msg}); - } - - // T cannot be statically converted to format string (including string_view) + // T cannot be statically converted to format string (including string_view/wstring_view) template::value, int>::type = 0> void log(source_loc loc, level::level_enum lvl, const T &msg) { @@ -148,86 +141,121 @@ public: } template - void trace(fmt::format_string fmt, Args &&...args) + void trace(format_string_t fmt, Args &&...args) { log(level::trace, fmt, std::forward(args)...); } template - void debug(fmt::format_string fmt, Args &&...args) + void debug(format_string_t fmt, Args &&...args) { log(level::debug, fmt, std::forward(args)...); } template - void info(fmt::format_string fmt, Args &&...args) + void info(format_string_t fmt, Args &&...args) { log(level::info, fmt, std::forward(args)...); } template - void warn(fmt::format_string fmt, Args &&...args) + void warn(format_string_t fmt, Args &&...args) { log(level::warn, fmt, std::forward(args)...); } template - void error(fmt::format_string fmt, Args &&...args) + void error(format_string_t fmt, Args &&...args) { log(level::err, fmt, std::forward(args)...); } template - void critical(fmt::format_string fmt, Args &&...args) + void critical(format_string_t fmt, Args &&...args) { log(level::critical, fmt, std::forward(args)...); } #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT template - void log(level::level_enum lvl, fmt::wformat_string fmt, Args &&...args) + void log(source_loc loc, level::level_enum lvl, wformat_string_t fmt, Args &&...args) { - log(source_loc{}, lvl, fmt, std::forward(args)...); + log_(loc, lvl, fmt, std::forward(args)...); } template - void log(source_loc loc, level::level_enum lvl, fmt::wformat_string fmt, Args &&...args) + void log(level::level_enum lvl, wformat_string_t fmt, Args &&...args) { - log_(loc, lvl, fmt, std::forward(args)...); + log(source_loc{}, lvl, fmt, std::forward(args)...); + } + + void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, wstring_view_t msg) + { + bool log_enabled = should_log(lvl); + bool traceback_enabled = tracer_.enabled(); + if (!log_enabled && !traceback_enabled) + { + return; + } + + memory_buf_t buf; + details::os::wstr_to_utf8buf(wstring_view_t(msg.data(), msg.size()), buf); + details::log_msg log_msg(log_time, loc, name_, lvl, string_view_t(buf.data(), buf.size())); + log_it_(log_msg, log_enabled, traceback_enabled); + } + + void log(source_loc loc, level::level_enum lvl, wstring_view_t msg) + { + bool log_enabled = should_log(lvl); + bool traceback_enabled = tracer_.enabled(); + if (!log_enabled && !traceback_enabled) + { + return; + } + + memory_buf_t buf; + details::os::wstr_to_utf8buf(wstring_view_t(msg.data(), msg.size()), buf); + details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size())); + log_it_(log_msg, log_enabled, traceback_enabled); + } + + void log(level::level_enum lvl, wstring_view_t msg) + { + log(source_loc{}, lvl, msg); } template - void trace(fmt::wformat_string fmt, Args &&...args) + void trace(wformat_string_t fmt, Args &&...args) { log(level::trace, fmt, std::forward(args)...); } template - void debug(fmt::wformat_string fmt, Args &&...args) + void debug(wformat_string_t fmt, Args &&...args) { log(level::debug, fmt, std::forward(args)...); } template - void info(fmt::wformat_string fmt, Args &&...args) + void info(wformat_string_t fmt, Args &&...args) { log(level::info, fmt, std::forward(args)...); } template - void warn(fmt::wformat_string fmt, Args &&...args) + void warn(wformat_string_t fmt, Args &&...args) { log(level::warn, fmt, std::forward(args)...); } template - void error(fmt::wformat_string fmt, Args &&...args) + void error(wformat_string_t fmt, Args &&...args) { log(level::err, fmt, std::forward(args)...); } template - void critical(fmt::wformat_string fmt, Args &&...args) + void critical(wformat_string_t fmt, Args &&...args) { log(level::critical, fmt, std::forward(args)...); } @@ -335,8 +363,12 @@ protected: } SPDLOG_TRY { +#ifdef SPDLOG_USE_STD_FORMAT + memory_buf_t buf = std::vformat(fmt, std::make_format_args(std::forward(args)...)); +#else memory_buf_t buf; - fmt::detail::vformat_to(buf, fmt, fmt::make_format_args(args...)); + fmt::detail::vformat_to(buf, fmt, fmt::make_format_args(std::forward(args)...)); +#endif details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size())); log_it_(log_msg, log_enabled, traceback_enabled); } @@ -356,8 +388,13 @@ protected: SPDLOG_TRY { // format to wmemory_buffer and convert to utf8 - fmt::wmemory_buffer wbuf; - fmt::detail::vformat_to(wbuf, fmt, fmt::make_format_args(args...)); + ; +#ifdef SPDLOG_USE_STD_FORMAT + wmemory_buf_t wbuf = std::vformat(fmt, std::make_wformat_args(std::forward(args)...)); +#else + wmemory_buf_t wbuf; + fmt::detail::vformat_to(wbuf, fmt, fmt::make_format_args(std::forward(args)...)); +#endif memory_buf_t buf; details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf); details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size())); diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index fc552398..cf83800a 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -32,7 +32,7 @@ struct daily_filename_calculator { filename_t basename, ext; std::tie(basename, ext) = details::file_helper::split_by_extension(filename); - return fmt::format( + return fmt_lib::format( SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext); } }; @@ -49,8 +49,10 @@ struct daily_filename_format_calculator static filename_t calc_filename(const filename_t &filename, const tm &now_tm) { // generate fmt datetime format string, e.g. {:%Y-%m-%d}. - filename_t fmt_filename = fmt::format(SPDLOG_FILENAME_T("{{:{}}}"), filename); -#if defined(_MSC_VER) && defined(SPDLOG_WCHAR_FILENAMES) // for some reason msvc doesnt allow fmt::runtime(..) with wchar here + filename_t fmt_filename = fmt_lib::format(SPDLOG_FILENAME_T("{{:{}}}"), filename); +#if defined(SPDLOG_USE_STD_FORMAT) + return std::vformat(fmt_filename, std::make_format_args(now_tm)); +#elif defined(_MSC_VER) && defined(SPDLOG_WCHAR_FILENAMES) // for some reason msvc doesnt allow fmt::runtime(..) with wchar here return fmt::format(fmt_filename, now_tm); #else return fmt::format(SPDLOG_FMT_RUNTIME(fmt_filename), now_tm); diff --git a/include/spdlog/sinks/hourly_file_sink.h b/include/spdlog/sinks/hourly_file_sink.h index 653faceb..71b5a1b4 100644 --- a/include/spdlog/sinks/hourly_file_sink.h +++ b/include/spdlog/sinks/hourly_file_sink.h @@ -31,7 +31,7 @@ struct hourly_filename_calculator { filename_t basename, ext; std::tie(basename, ext) = details::file_helper::split_by_extension(filename); - return fmt::format(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, + return fmt_lib::format(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, now_tm.tm_hour, ext); } }; diff --git a/include/spdlog/sinks/msvc_sink.h b/include/spdlog/sinks/msvc_sink.h index 2ac4a965..76ec40a0 100644 --- a/include/spdlog/sinks/msvc_sink.h +++ b/include/spdlog/sinks/msvc_sink.h @@ -30,7 +30,11 @@ protected: { memory_buf_t formatted; base_sink::formatter_->format(msg, formatted); +#ifdef SPDLOG_USE_STD_FORMAT + OutputDebugStringA(formatted.c_str()); +#else OutputDebugStringA(fmt::to_string(formatted).c_str()); +#endif } void flush_() override {} diff --git a/include/spdlog/sinks/ringbuffer_sink.h b/include/spdlog/sinks/ringbuffer_sink.h index 1ee3f691..5836d98b 100644 --- a/include/spdlog/sinks/ringbuffer_sink.h +++ b/include/spdlog/sinks/ringbuffer_sink.h @@ -50,7 +50,11 @@ public: { memory_buf_t formatted; base_sink::formatter_->format(q_.at(i), formatted); +#ifdef SPDLOG_USE_STD_FORMAT + ret.push_back(std::move(formatted)); +#else ret.push_back(fmt::to_string(formatted)); +#endif } return ret; } diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h index 2e3087af..87e80593 100644 --- a/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -51,7 +51,7 @@ SPDLOG_INLINE filename_t rotating_file_sink::calc_filename(const filename filename_t basename, ext; std::tie(basename, ext) = details::file_helper::split_by_extension(filename); - return fmt::format(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext); + return fmt_lib::format(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext); } template diff --git a/include/spdlog/sinks/win_eventlog_sink.h b/include/spdlog/sinks/win_eventlog_sink.h index 5e70599a..202c79f8 100644 --- a/include/spdlog/sinks/win_eventlog_sink.h +++ b/include/spdlog/sinks/win_eventlog_sink.h @@ -47,6 +47,24 @@ namespace win_eventlog { namespace internal { +struct local_alloc_t +{ + HLOCAL hlocal_; + + SPDLOG_CONSTEXPR local_alloc_t() SPDLOG_NOEXCEPT : hlocal_(nullptr) {} + + local_alloc_t(local_alloc_t const&) = delete; + local_alloc_t& operator=(local_alloc_t const&) = delete; + + ~local_alloc_t() SPDLOG_NOEXCEPT + { + if (hlocal_) + { + LocalFree(hlocal_); + } + } +}; + /** Windows error */ struct win32_error : public spdlog_ex { @@ -55,22 +73,17 @@ struct win32_error : public spdlog_ex { std::string system_message; - LPSTR format_message_result{}; + local_alloc_t format_message_result{}; auto format_message_succeeded = ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, - error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&format_message_result, 0, nullptr); - - if (format_message_succeeded && format_message_result) - { - system_message = fmt::format(" ({})", format_message_result); - } + error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&format_message_result.hlocal_, 0, nullptr); - if (format_message_result) + if (format_message_succeeded && format_message_result.hlocal_) { - LocalFree((HLOCAL)format_message_result); + system_message = fmt_lib::format(" ({})", (LPSTR)format_message_result.hlocal_); } - return fmt::format("{}: {}{}", user_message, error_code, system_message); + return fmt_lib::format("{}: {}{}", user_message, error_code, system_message); } explicit win32_error(std::string const &func_name, DWORD error = GetLastError()) diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index 812896da..327a0ebe 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -128,49 +128,49 @@ SPDLOG_API spdlog::logger *default_logger_raw(); SPDLOG_API void set_default_logger(std::shared_ptr default_logger); template -inline void log(source_loc source, level::level_enum lvl, fmt::format_string fmt, Args &&...args) +inline void log(source_loc source, level::level_enum lvl, format_string_t fmt, Args &&...args) { default_logger_raw()->log(source, lvl, fmt, std::forward(args)...); } template -inline void log(level::level_enum lvl, fmt::format_string fmt, Args &&...args) +inline void log(level::level_enum lvl, format_string_t fmt, Args &&...args) { default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward(args)...); } template -inline void trace(fmt::format_string fmt, Args &&...args) +inline void trace(format_string_t fmt, Args &&...args) { default_logger_raw()->trace(fmt, std::forward(args)...); } template -inline void debug(fmt::format_string fmt, Args &&...args) +inline void debug(format_string_t fmt, Args &&...args) { default_logger_raw()->debug(fmt, std::forward(args)...); } template -inline void info(fmt::format_string fmt, Args &&...args) +inline void info(format_string_t fmt, Args &&...args) { default_logger_raw()->info(fmt, std::forward(args)...); } template -inline void warn(fmt::format_string fmt, Args &&...args) +inline void warn(format_string_t fmt, Args &&...args) { default_logger_raw()->warn(fmt, std::forward(args)...); } template -inline void error(fmt::format_string fmt, Args &&...args) +inline void error(format_string_t fmt, Args &&...args) { default_logger_raw()->error(fmt, std::forward(args)...); } template -inline void critical(fmt::format_string fmt, Args &&...args) +inline void critical(format_string_t fmt, Args &&...args) { default_logger_raw()->critical(fmt, std::forward(args)...); } @@ -189,49 +189,49 @@ inline void log(level::level_enum lvl, const T &msg) #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT template -inline void log(source_loc source, level::level_enum lvl, fmt::wformat_string fmt, Args &&...args) +inline void log(source_loc source, level::level_enum lvl, wformat_string_t fmt, Args &&...args) { default_logger_raw()->log(source, lvl, fmt, std::forward(args)...); } template -inline void log(level::level_enum lvl, fmt::wformat_string fmt, Args &&...args) +inline void log(level::level_enum lvl, wformat_string_t fmt, Args &&...args) { default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward(args)...); } template -inline void trace(fmt::wformat_string fmt, Args &&...args) +inline void trace(wformat_string_t fmt, Args &&...args) { default_logger_raw()->trace(fmt, std::forward(args)...); } template -inline void debug(fmt::wformat_string fmt, Args &&...args) +inline void debug(wformat_string_t fmt, Args &&...args) { default_logger_raw()->debug(fmt, std::forward(args)...); } template -inline void info(fmt::wformat_string fmt, Args &&...args) +inline void info(wformat_string_t fmt, Args &&...args) { default_logger_raw()->info(fmt, std::forward(args)...); } template -inline void warn(fmt::wformat_string fmt, Args &&...args) +inline void warn(wformat_string_t fmt, Args &&...args) { default_logger_raw()->warn(fmt, std::forward(args)...); } template -inline void error(fmt::wformat_string fmt, Args &&...args) +inline void error(wformat_string_t fmt, Args &&...args) { default_logger_raw()->error(fmt, std::forward(args)...); } template -inline void critical(fmt::wformat_string fmt, Args &&...args) +inline void critical(wformat_string_t fmt, Args &&...args) { default_logger_raw()->critical(fmt, std::forward(args)...); } diff --git a/include/spdlog/tweakme.h b/include/spdlog/tweakme.h index 24361f30..08f0f4e5 100644 --- a/include/spdlog/tweakme.h +++ b/include/spdlog/tweakme.h @@ -74,6 +74,13 @@ // #define SPDLOG_FMT_EXTERNAL /////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// Uncomment to use C++20 std::format instead of fmt. This removes compile +// time checking of format strings, but doesn't depend on the fmt library. +// +// #define SPDLOG_USE_STD_FORMAT +/////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////// // Uncomment to enable wchar_t support (convert to utf8) // diff --git a/src/fmt.cpp b/src/fmt.cpp index b3cd3cf1..536aa675 100644 --- a/src/fmt.cpp +++ b/src/fmt.cpp @@ -6,7 +6,7 @@ # error Please define SPDLOG_COMPILED_LIB to compile this file. #endif -#if !defined(SPDLOG_FMT_EXTERNAL) +#if !defined(SPDLOG_FMT_EXTERNAL) && !defined(SPDLOG_USE_STD_FORMAT) # include FMT_BEGIN_NAMESPACE diff --git a/tests/test_async.cpp b/tests/test_async.cpp index 83fc0ec6..124f3132 100644 --- a/tests/test_async.cpp +++ b/tests/test_async.cpp @@ -166,7 +166,7 @@ TEST_CASE("to_file", "[async]") require_message_count(TEST_FILENAME, messages); auto contents = file_contents(TEST_FILENAME); using spdlog::details::os::default_eol; - REQUIRE(ends_with(contents, fmt::format("Hello message #1023{}", default_eol))); + REQUIRE(ends_with(contents, spdlog::fmt_lib::format("Hello message #1023{}", default_eol))); } TEST_CASE("to_file multi-workers", "[async]") diff --git a/tests/test_daily_logger.cpp b/tests/test_daily_logger.cpp index 6aeff5b1..93bd4662 100644 --- a/tests/test_daily_logger.cpp +++ b/tests/test_daily_logger.cpp @@ -3,7 +3,11 @@ */ #include "includes.h" +#ifdef SPDLOG_USE_STD_FORMAT +using filename_memory_buf_t = std::basic_string; +#else using filename_memory_buf_t = fmt::basic_memory_buffer; +#endif TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]") { @@ -15,7 +19,7 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]") spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_dateonly"); std::tm tm = spdlog::details::os::localtime(); filename_memory_buf_t w; - fmt::format_to( + spdlog::fmt_lib::format_to( std::back_inserter(w), SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); auto logger = spdlog::create("logger", basename, 0, 0); @@ -29,9 +33,17 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]") #ifdef SPDLOG_WCHAR_FILENAMES spdlog::memory_buf_t buf; spdlog::details::os::wstr_to_utf8buf(fmt::to_string(w), buf); +#ifdef SPDLOG_USE_STD_FORMAT + auto &filename = buf; +#else auto filename = fmt::to_string(buf); +#endif +#else +#ifdef SPDLOG_USE_STD_FORMAT + auto &filename = w; #else auto filename = fmt::to_string(w); +#endif #endif require_message_count(filename, 10); } @@ -41,9 +53,13 @@ struct custom_daily_file_name_calculator static spdlog::filename_t calc_filename(const spdlog::filename_t &basename, const tm &now_tm) { filename_memory_buf_t w; - fmt::format_to(std::back_inserter(w), SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, + spdlog::fmt_lib::format_to(std::back_inserter(w), SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday); +#ifdef SPDLOG_USE_STD_FORMAT + return w; +#else return fmt::to_string(w); +#endif } }; @@ -57,7 +73,7 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]") spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_dateonly"); std::tm tm = spdlog::details::os::localtime(); filename_memory_buf_t w; - fmt::format_to( + spdlog::fmt_lib::format_to( std::back_inserter(w), SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); auto logger = spdlog::create("logger", basename, 0, 0); @@ -71,9 +87,17 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]") #ifdef SPDLOG_WCHAR_FILENAMES spdlog::memory_buf_t buf; spdlog::details::os::wstr_to_utf8buf(fmt::to_string(w), buf); +#ifdef SPDLOG_USE_STD_FORMAT + auto &filename = buf; +#else auto filename = fmt::to_string(buf); +#endif +#else +#ifdef SPDLOG_USE_STD_FORMAT + auto &filename = w; #else auto filename = fmt::to_string(w); +#endif #endif require_message_count(filename, 10); } diff --git a/tests/test_errors.cpp b/tests/test_errors.cpp index bcc21d73..41441d0d 100644 --- a/tests/test_errors.cpp +++ b/tests/test_errors.cpp @@ -34,7 +34,7 @@ TEST_CASE("default_error_handler", "[errors]]") logger->flush(); using spdlog::details::os::default_eol; - REQUIRE(file_contents(SIMPLE_LOG) == fmt::format("Test message 2{}", default_eol)); + REQUIRE(file_contents(SIMPLE_LOG) == spdlog::fmt_lib::format("Test message 2{}", default_eol)); REQUIRE(count_lines(SIMPLE_LOG) == 1); } diff --git a/tests/test_file_helper.cpp b/tests/test_file_helper.cpp index 37c929f5..168c6601 100644 --- a/tests/test_file_helper.cpp +++ b/tests/test_file_helper.cpp @@ -10,7 +10,7 @@ using spdlog::details::file_helper; static void write_with_helper(file_helper &helper, size_t howmany) { spdlog::memory_buf_t formatted; - fmt::format_to(std::back_inserter(formatted), "{}", std::string(howmany, '1')); + spdlog::fmt_lib::format_to(std::back_inserter(formatted), "{}", std::string(howmany, '1')); helper.write(formatted); helper.flush(); } diff --git a/tests/test_fmt_helper.cpp b/tests/test_fmt_helper.cpp index 3ef18abe..6972d2d2 100644 --- a/tests/test_fmt_helper.cpp +++ b/tests/test_fmt_helper.cpp @@ -8,28 +8,48 @@ void test_pad2(int n, const char *expected) { memory_buf_t buf; spdlog::details::fmt_helper::pad2(n, buf); + +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted == expected); +#else REQUIRE(fmt::to_string(buf) == expected); +#endif } void test_pad3(uint32_t n, const char *expected) { memory_buf_t buf; spdlog::details::fmt_helper::pad3(n, buf); + +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted == expected); +#else REQUIRE(fmt::to_string(buf) == expected); +#endif } void test_pad6(std::size_t n, const char *expected) { memory_buf_t buf; spdlog::details::fmt_helper::pad6(n, buf); + +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted == expected); +#else REQUIRE(fmt::to_string(buf) == expected); +#endif } void test_pad9(std::size_t n, const char *expected) { memory_buf_t buf; spdlog::details::fmt_helper::pad9(n, buf); + +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted == expected); +#else REQUIRE(fmt::to_string(buf) == expected); +#endif } TEST_CASE("pad2", "[fmt_helper]") diff --git a/tests/test_macros.cpp b/tests/test_macros.cpp index dd21ed19..b4ad5654 100644 --- a/tests/test_macros.cpp +++ b/tests/test_macros.cpp @@ -25,7 +25,7 @@ TEST_CASE("debug and trace w/o format string", "[macros]]") logger->flush(); using spdlog::details::os::default_eol; - REQUIRE(ends_with(file_contents(TEST_FILENAME), fmt::format("Test message 2{}", default_eol))); + REQUIRE(ends_with(file_contents(TEST_FILENAME), spdlog::fmt_lib::format("Test message 2{}", default_eol))); REQUIRE(count_lines(TEST_FILENAME) == 1); spdlog::set_default_logger(logger); @@ -35,7 +35,7 @@ TEST_CASE("debug and trace w/o format string", "[macros]]") logger->flush(); require_message_count(TEST_FILENAME, 2); - REQUIRE(ends_with(file_contents(TEST_FILENAME), fmt::format("Test message 4{}", default_eol))); + REQUIRE(ends_with(file_contents(TEST_FILENAME), spdlog::fmt_lib::format("Test message 4{}", default_eol))); } TEST_CASE("disable param evaluation", "[macros]") diff --git a/tests/test_pattern_formatter.cpp b/tests/test_pattern_formatter.cpp index 656f93a0..78c4cd30 100644 --- a/tests/test_pattern_formatter.cpp +++ b/tests/test_pattern_formatter.cpp @@ -64,7 +64,7 @@ TEST_CASE("color range test1", "[pattern_formatter]") auto formatter = std::make_shared("%^%v%$", spdlog::pattern_time_type::local, "\n"); memory_buf_t buf; - fmt::format_to(std::back_inserter(buf), "Hello"); + spdlog::fmt_lib::format_to(std::back_inserter(buf), "Hello"); memory_buf_t formatted; std::string logger_name = "test"; spdlog::details::log_msg msg(logger_name, spdlog::level::info, spdlog::string_view_t(buf.data(), buf.size())); @@ -273,7 +273,11 @@ TEST_CASE("clone-default-formatter", "[pattern_formatter]") formatter_1->format(msg, formatted_1); formatter_2->format(msg, formatted_2); +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted_1 == formatted_2); +#else REQUIRE(fmt::to_string(formatted_1) == fmt::to_string(formatted_2)); +#endif } TEST_CASE("clone-default-formatter2", "[pattern_formatter]") @@ -288,7 +292,11 @@ TEST_CASE("clone-default-formatter2", "[pattern_formatter]") formatter_1->format(msg, formatted_1); formatter_2->format(msg, formatted_2); +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted_1 == formatted_2); +#else REQUIRE(fmt::to_string(formatted_1) == fmt::to_string(formatted_2)); +#endif } TEST_CASE("clone-formatter", "[pattern_formatter]") @@ -302,7 +310,12 @@ TEST_CASE("clone-formatter", "[pattern_formatter]") memory_buf_t formatted_2; formatter_1->format(msg, formatted_1); formatter_2->format(msg, formatted_2); + +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted_1 == formatted_2); +#else REQUIRE(fmt::to_string(formatted_1) == fmt::to_string(formatted_2)); +#endif } TEST_CASE("clone-formatter-2", "[pattern_formatter]") @@ -317,7 +330,12 @@ TEST_CASE("clone-formatter-2", "[pattern_formatter]") memory_buf_t formatted_2; formatter_1->format(msg, formatted_1); formatter_2->format(msg, formatted_2); + +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted_1 == formatted_2); +#else REQUIRE(fmt::to_string(formatted_1) == fmt::to_string(formatted_2)); +#endif } class custom_test_flag : public spdlog::custom_flag_formatter @@ -362,9 +380,15 @@ TEST_CASE("clone-custom_formatter", "[pattern_formatter]") formatter_1->format(msg, formatted_1); formatter_2->format(msg, formatted_2); - auto expected = fmt::format("[logger-name] [custom_output] some message{}", spdlog::details::os::default_eol); + auto expected = spdlog::fmt_lib::format("[logger-name] [custom_output] some message{}", spdlog::details::os::default_eol); + +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted_1 == expected); + REQUIRE(formatted_2 == expected); +#else REQUIRE(fmt::to_string(formatted_1) == expected); REQUIRE(fmt::to_string(formatted_2) == expected); +#endif } // @@ -385,7 +409,12 @@ TEST_CASE("short filename formatter-1", "[pattern_formatter]") spdlog::source_loc source_loc{test_path, 123, "some_func()"}; spdlog::details::log_msg msg(source_loc, "logger-name", spdlog::level::info, "Hello"); formatter.format(msg, formatted); + +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted == "myfile.cpp"); +#else REQUIRE(fmt::to_string(formatted) == "myfile.cpp"); +#endif } TEST_CASE("short filename formatter-2", "[pattern_formatter]") @@ -396,7 +425,12 @@ TEST_CASE("short filename formatter-2", "[pattern_formatter]") spdlog::source_loc source_loc{"myfile.cpp", 123, "some_func()"}; spdlog::details::log_msg msg(source_loc, "logger-name", spdlog::level::info, "Hello"); formatter.format(msg, formatted); + +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted == "myfile.cpp:123"); +#else REQUIRE(fmt::to_string(formatted) == "myfile.cpp:123"); +#endif } TEST_CASE("short filename formatter-3", "[pattern_formatter]") @@ -407,7 +441,12 @@ TEST_CASE("short filename formatter-3", "[pattern_formatter]") spdlog::source_loc source_loc{"", 123, "some_func()"}; spdlog::details::log_msg msg(source_loc, "logger-name", spdlog::level::info, "Hello"); formatter.format(msg, formatted); + +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted == " Hello"); +#else REQUIRE(fmt::to_string(formatted) == " Hello"); +#endif } TEST_CASE("full filename formatter", "[pattern_formatter]") @@ -418,7 +457,12 @@ TEST_CASE("full filename formatter", "[pattern_formatter]") spdlog::source_loc source_loc{test_path, 123, "some_func()"}; spdlog::details::log_msg msg(source_loc, "logger-name", spdlog::level::info, "Hello"); formatter.format(msg, formatted); + +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted == test_path); +#else REQUIRE(fmt::to_string(formatted) == test_path); +#endif } TEST_CASE("custom flags", "[pattern_formatter]") @@ -430,8 +474,13 @@ TEST_CASE("custom flags", "[pattern_formatter]") spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info, "some message"); formatter->format(msg, formatted); - auto expected = fmt::format("[logger-name] [custom1] [custom2] some message{}", spdlog::details::os::default_eol); + auto expected = spdlog::fmt_lib::format("[logger-name] [custom1] [custom2] some message{}", spdlog::details::os::default_eol); + +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted == expected); +#else REQUIRE(fmt::to_string(formatted) == expected); +#endif } TEST_CASE("custom flags-padding", "[pattern_formatter]") @@ -443,8 +492,13 @@ TEST_CASE("custom flags-padding", "[pattern_formatter]") spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info, "some message"); formatter->format(msg, formatted); - auto expected = fmt::format("[logger-name] [custom1] [ custom2] some message{}", spdlog::details::os::default_eol); + auto expected = spdlog::fmt_lib::format("[logger-name] [custom1] [ custom2] some message{}", spdlog::details::os::default_eol); + +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE(formatted == expected); +#else REQUIRE(fmt::to_string(formatted) == expected); +#endif } TEST_CASE("custom flags-exception", "[pattern_formatter]") From a31ae23db11ce96acec36a493d1f1ee805913f11 Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Sat, 13 Nov 2021 11:43:19 -0500 Subject: [PATCH 004/121] Fix build issue when using built-in fmt --- include/spdlog/common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/common.h b/include/spdlog/common.h index fb45d2a2..76370d4f 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -148,7 +148,7 @@ template using format_string_t = fmt::format_string; template -using wformat_string_t = fmt::wformat_string; +using wformat_string_t = fmt::basic_format_string; template using remove_cvref_t = typename std::remove_cv::type>::type; From c475418975ebf07ccebed860505f80966c38653e Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Sat, 13 Nov 2021 11:50:26 -0500 Subject: [PATCH 005/121] Put formatter specialization in its original namespace --- include/spdlog/fmt/bin_to_hex.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h index 62807d63..cf0c84ff 100644 --- a/include/spdlog/fmt/bin_to_hex.h +++ b/include/spdlog/fmt/bin_to_hex.h @@ -74,8 +74,18 @@ inline details::dump_info to_hex(const It range_begin, const It range_end, s return details::dump_info(range_begin, range_end, size_per_line); } +} // namespace spdlog + +namespace +#ifdef SPDLOG_USE_STD_FORMAT + std +#else + fmt +#endif +{ + template -struct fmt_lib::formatter, char> +struct formatter, char> { const char delimiter = ' '; bool put_newlines = true; @@ -210,4 +220,4 @@ struct fmt_lib::formatter, char> } } }; -} // namespace spdlog +} // namespace fmt/std From 4008f31add3d7719abe1713b5354f31892d73fcd Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Sat, 13 Nov 2021 11:51:22 -0500 Subject: [PATCH 006/121] Fix missing spdlog:: --- include/spdlog/fmt/bin_to_hex.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h index cf0c84ff..ab06651b 100644 --- a/include/spdlog/fmt/bin_to_hex.h +++ b/include/spdlog/fmt/bin_to_hex.h @@ -216,7 +216,7 @@ struct formatter, char> if (put_positions) { - fmt_lib::format_to(inserter, "{:04X}: ", pos); + spdlog::fmt_lib::format_to(inserter, "{:04X}: ", pos); } } }; From 6ff1b83038ebbcbc6b0810b1f4d5daf51174efa7 Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Sat, 13 Nov 2021 11:54:06 -0500 Subject: [PATCH 007/121] Fix usage of std::forward --- include/spdlog/logger.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index 9adfec97..4ce41b84 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -364,10 +364,10 @@ protected: SPDLOG_TRY { #ifdef SPDLOG_USE_STD_FORMAT - memory_buf_t buf = std::vformat(fmt, std::make_format_args(std::forward(args)...)); + memory_buf_t buf = std::vformat(fmt, std::make_format_args(std::forward(args)...)); #else memory_buf_t buf; - fmt::detail::vformat_to(buf, fmt, fmt::make_format_args(std::forward(args)...)); + fmt::detail::vformat_to(buf, fmt, fmt::make_format_args(std::forward(args)...)); #endif details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size())); log_it_(log_msg, log_enabled, traceback_enabled); @@ -390,10 +390,10 @@ protected: // format to wmemory_buffer and convert to utf8 ; #ifdef SPDLOG_USE_STD_FORMAT - wmemory_buf_t wbuf = std::vformat(fmt, std::make_wformat_args(std::forward(args)...)); + wmemory_buf_t wbuf = std::vformat(fmt, std::make_wformat_args(std::forward(args)...)); #else wmemory_buf_t wbuf; - fmt::detail::vformat_to(wbuf, fmt, fmt::make_format_args(std::forward(args)...)); + fmt::detail::vformat_to(wbuf, fmt, fmt::make_format_args(std::forward(args)...)); #endif memory_buf_t buf; details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf); From 89c4b1aabe7463afc15a874dc9602624b8f221ec Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Sat, 13 Nov 2021 12:02:40 -0500 Subject: [PATCH 008/121] Fix build issues under C++11 --- include/spdlog/common.h | 6 ++++++ include/spdlog/details/fmt_helper.h | 2 +- include/spdlog/fmt/bin_to_hex.h | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 76370d4f..caace136 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -55,9 +55,15 @@ #if defined(_MSC_VER) && (_MSC_VER < 1900) # define SPDLOG_NOEXCEPT _NOEXCEPT # define SPDLOG_CONSTEXPR +# define SPDLOG_CONSTEXPR_FUNC #else # define SPDLOG_NOEXCEPT noexcept # define SPDLOG_CONSTEXPR constexpr +# if __cplusplus >= 201402L +# define SPDLOG_CONSTEXPR_FUNC constexpr +# else +# define SPDLOG_CONSTEXPR_FUNC +# endif #endif #if defined(__GNUC__) || defined(__clang__) diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h index 31ed64fd..d0b5f98c 100644 --- a/include/spdlog/details/fmt_helper.h +++ b/include/spdlog/details/fmt_helper.h @@ -34,7 +34,7 @@ template inline void append_int(T n, memory_buf_t &dest) { // Buffer should be large enough to hold all digits (digits10 + 1) and a sign - SPDLOG_CONSTEXPR auto BUF_SIZE = std::numeric_limits::digits10 + 2; + SPDLOG_CONSTEXPR const auto BUF_SIZE = std::numeric_limits::digits10 + 2; char buf[BUF_SIZE]; auto [ptr, ec] = std::to_chars(buf, buf + BUF_SIZE, n, 10); diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h index ab06651b..c6aa59d1 100644 --- a/include/spdlog/fmt/bin_to_hex.h +++ b/include/spdlog/fmt/bin_to_hex.h @@ -96,7 +96,7 @@ struct formatter, char> // parse the format string flags template - SPDLOG_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) + SPDLOG_CONSTEXPR_FUNC auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { auto it = ctx.begin(); while (it != ctx.end() && *it != '}') From 48e35f9c3e13ca0792a4ff7eec86ae502e3b1478 Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Sat, 13 Nov 2021 12:08:01 -0500 Subject: [PATCH 009/121] Make clang happy, fix VS 2022 generator name --- appveyor.yml | 2 +- include/spdlog/details/fmt_helper.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 42d928e3..6c5f2103 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -66,7 +66,7 @@ environment: BUILD_EXAMPLE: 'OFF' USE_STD_FORMAT: 'OFF' APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - - GENERATOR: '"Visual Studio 16 2022" -A x64' + - GENERATOR: '"Visual Studio 17 2022" -A x64' BUILD_TYPE: Release BUILD_SHARED: 'ON' WCHAR: 'OFF' diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h index d0b5f98c..25976f50 100644 --- a/include/spdlog/details/fmt_helper.h +++ b/include/spdlog/details/fmt_helper.h @@ -57,7 +57,7 @@ inline void append_int(T n, memory_buf_t &dest) #endif template -SPDLOG_CONSTEXPR unsigned int count_digits_fallback(T n) +SPDLOG_CONSTEXPR_FUNC unsigned int count_digits_fallback(T n) { // taken from fmt. unsigned int count = 1; From 591eedcf36ae2807405e84a2c01c0f64e47098ea Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Sat, 13 Nov 2021 21:54:08 +0200 Subject: [PATCH 010/121] Fix typos --- include/spdlog/common.h | 2 +- include/spdlog/details/mpmc_blocking_q.h | 4 ++-- include/spdlog/fmt/bin_to_hex.h | 2 +- include/spdlog/sinks/daily_file_sink.h | 2 +- include/spdlog/sinks/stdout_sinks-inl.h | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/spdlog/common.h b/include/spdlog/common.h index ddaa34ce..413d8be7 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -46,7 +46,7 @@ # define SPDLOG_FMT_RUNTIME(format_string) format_string #endif -// visual studio upto 2013 does not support noexcept nor constexpr +// visual studio up to 2013 does not support noexcept nor constexpr #if defined(_MSC_VER) && (_MSC_VER < 1900) # define SPDLOG_NOEXCEPT _NOEXCEPT # define SPDLOG_CONSTEXPR diff --git a/include/spdlog/details/mpmc_blocking_q.h b/include/spdlog/details/mpmc_blocking_q.h index 5c3cca76..b70483e5 100644 --- a/include/spdlog/details/mpmc_blocking_q.h +++ b/include/spdlog/details/mpmc_blocking_q.h @@ -49,7 +49,7 @@ public: push_cv_.notify_one(); } - // try to dequeue item. if no item found. wait upto timeout and try again + // try to dequeue item. if no item found. wait up to timeout and try again // Return true, if succeeded dequeue item, false otherwise bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) { @@ -87,7 +87,7 @@ public: push_cv_.notify_one(); } - // try to dequeue item. if no item found. wait upto timeout and try again + // try to dequeue item. if no item found. wait up to timeout and try again // Return true, if succeeded dequeue item, false otherwise bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) { diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h index 0b6c3e96..202eb146 100644 --- a/include/spdlog/fmt/bin_to_hex.h +++ b/include/spdlog/fmt/bin_to_hex.h @@ -10,7 +10,7 @@ // // Support for logging binary data as hex -// format flags, any combination of the followng: +// format flags, any combination of the following: // {:X} - print in uppercase. // {:s} - don't separate each byte with space. // {:p} - don't print the position on each line start. diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index fc552398..7221f0f3 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -50,7 +50,7 @@ struct daily_filename_format_calculator { // generate fmt datetime format string, e.g. {:%Y-%m-%d}. filename_t fmt_filename = fmt::format(SPDLOG_FILENAME_T("{{:{}}}"), filename); -#if defined(_MSC_VER) && defined(SPDLOG_WCHAR_FILENAMES) // for some reason msvc doesnt allow fmt::runtime(..) with wchar here +#if defined(_MSC_VER) && defined(SPDLOG_WCHAR_FILENAMES) // for some reason msvc doesn't allow fmt::runtime(..) with wchar here return fmt::format(fmt_filename, now_tm); #else return fmt::format(SPDLOG_FMT_RUNTIME(fmt_filename), now_tm); diff --git a/include/spdlog/sinks/stdout_sinks-inl.h b/include/spdlog/sinks/stdout_sinks-inl.h index c14066b9..fd5594fe 100644 --- a/include/spdlog/sinks/stdout_sinks-inl.h +++ b/include/spdlog/sinks/stdout_sinks-inl.h @@ -16,7 +16,7 @@ // so instead we use ::FileWrite # include -# ifndef _USING_V110_SDK71_ // fileapi.h doesnt exist in winxp +# ifndef _USING_V110_SDK71_ // fileapi.h doesn't exist in winxp # include // WriteFile (..) # endif @@ -60,7 +60,7 @@ SPDLOG_INLINE void stdout_sink_base::log(const details::log_msg &m std::lock_guard lock(mutex_); memory_buf_t formatted; formatter_->format(msg, formatted); - ::fflush(file_); // flush in case there is somthing in this file_ already + ::fflush(file_); // flush in case there is something in this file_ already auto size = static_cast(formatted.size()); DWORD bytes_written = 0; bool ok = ::WriteFile(handle_, formatted.data(), size, &bytes_written, nullptr) != 0; From c8ba643f53812470ad36776f8581d2465841cce8 Mon Sep 17 00:00:00 2001 From: Keith Williams Date: Sun, 14 Nov 2021 06:44:47 +0000 Subject: [PATCH 011/121] example.cpp failes to build on FreeBSD --- include/spdlog/details/udp_client.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/spdlog/details/udp_client.h b/include/spdlog/details/udp_client.h index ccdf2f0b..7a8ee9cc 100644 --- a/include/spdlog/details/udp_client.h +++ b/include/spdlog/details/udp_client.h @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -90,4 +91,4 @@ public: } }; } // namespace details -} // namespace spdlog \ No newline at end of file +} // namespace spdlog From d75de3d3b25ad53b44002fa7760782e3025513a7 Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Sun, 14 Nov 2021 02:33:15 -0500 Subject: [PATCH 012/121] Add SPDLOG_USE_STD_FORMAT to target_compile_definitions --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d9bc279..21577e50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -231,7 +231,8 @@ foreach( SPDLOG_NO_THREAD_ID SPDLOG_NO_TLS SPDLOG_NO_ATOMIC_LEVELS - SPDLOG_DISABLE_DEFAULT_LOGGER) + SPDLOG_DISABLE_DEFAULT_LOGGER + SPDLOG_USE_STD_FORMAT) if(${SPDLOG_OPTION}) target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION}) target_compile_definitions(spdlog_header_only INTERFACE ${SPDLOG_OPTION}) From 24a551c14e96e16d1338b072976610b62d322e84 Mon Sep 17 00:00:00 2001 From: seker <04070628@163.com> Date: Sat, 13 Nov 2021 17:19:23 +0800 Subject: [PATCH 013/121] file_event_handlers add before_open function --- example/example.cpp | 6 +++++- include/spdlog/common.h | 6 +++++- include/spdlog/details/file_helper-inl.h | 4 ++++ include/spdlog/details/file_helper.h | 2 +- include/spdlog/sinks/basic_file_sink.h | 6 +++--- include/spdlog/sinks/daily_file_sink.h | 10 +++++----- include/spdlog/sinks/hourly_file_sink.h | 6 +++--- include/spdlog/sinks/rotating_file_sink.h | 6 +++--- 8 files changed, 29 insertions(+), 17 deletions(-) diff --git a/example/example.cpp b/example/example.cpp index 56e28825..1e145312 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -121,7 +121,11 @@ void rotating_example() { // Create a file rotating logger with 5mb size max and 3 rotated files. spdlog::file_event_handlers_t file_event_handlers; - file_event_handlers.after_open = [](spdlog::filename_t filename, std::FILE* fstream) + file_event_handlers.before_open = [](spdlog::filename_t filename) + { + spdlog::info("basic_example() : file_event_handlers.before_open : {}", filename.c_str()); + }; + file_event_handlers.after_open = [](spdlog::filename_t filename, std::FILE* fstream) { fputs("OPEN!\r\n", fstream); spdlog::info("basic_example() : file_event_handlers.after_open : {}", filename.c_str()); diff --git a/include/spdlog/common.h b/include/spdlog/common.h index ddaa34ce..70d4daed 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -256,11 +256,15 @@ struct source_loc const char *funcname{nullptr}; }; -typedef struct +typedef struct file_event_handlers { + std::function before_open; std::function after_open; std::function before_close; std::function after_close; + + file_event_handlers(): before_open(nullptr), after_open(nullptr), before_close(nullptr), after_close(nullptr) + {} } file_event_handlers_t; namespace details { diff --git a/include/spdlog/details/file_helper-inl.h b/include/spdlog/details/file_helper-inl.h index df6cd4c1..ab36f52f 100644 --- a/include/spdlog/details/file_helper-inl.h +++ b/include/spdlog/details/file_helper-inl.h @@ -37,6 +37,10 @@ SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate) auto *mode = SPDLOG_FILENAME_T("ab"); auto *trunc_mode = SPDLOG_FILENAME_T("wb"); + if (event_handlers_.before_open) + { + event_handlers_.before_open(filename_); + } for (int tries = 0; tries < open_tries_; ++tries) { // create containing folder if not exists already. diff --git a/include/spdlog/details/file_helper.h b/include/spdlog/details/file_helper.h index 60c47788..6b63f3a1 100644 --- a/include/spdlog/details/file_helper.h +++ b/include/spdlog/details/file_helper.h @@ -51,7 +51,7 @@ private: const unsigned int open_interval_ = 10; std::FILE *fd_{nullptr}; filename_t filename_; - file_event_handlers_t event_handlers_{nullptr, nullptr, nullptr}; + file_event_handlers_t event_handlers_; }; } // namespace details } // namespace spdlog diff --git a/include/spdlog/sinks/basic_file_sink.h b/include/spdlog/sinks/basic_file_sink.h index 51e26e38..f2e35f6e 100644 --- a/include/spdlog/sinks/basic_file_sink.h +++ b/include/spdlog/sinks/basic_file_sink.h @@ -20,7 +20,7 @@ template class basic_file_sink final : public base_sink { public: - explicit basic_file_sink(const filename_t &filename, bool truncate = false, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}); + explicit basic_file_sink(const filename_t &filename, bool truncate = false, const file_event_handlers_t& event_handlers = {}); const filename_t &filename() const; protected: @@ -40,13 +40,13 @@ using basic_file_sink_st = basic_file_sink; // factory functions // template -inline std::shared_ptr basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) +inline std::shared_ptr basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers_t& event_handlers = {}) { return Factory::template create(logger_name, filename, truncate, event_handlers); } template -inline std::shared_ptr basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) +inline std::shared_ptr basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers_t& event_handlers = {}) { return Factory::template create(logger_name, filename, truncate, event_handlers); } diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index fc552398..2958a001 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -68,7 +68,7 @@ class daily_file_sink final : public base_sink { public: // create daily file sink which rotates on given time - daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) + daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) : base_filename_(std::move(base_filename)) , rotation_h_(rotation_hour) , rotation_m_(rotation_minute) @@ -215,28 +215,28 @@ using daily_file_format_sink_st = daily_file_sink inline std::shared_ptr daily_logger_mt( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) + const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) { return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); } template inline std::shared_ptr daily_logger_format_mt( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) + const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) { return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); } template inline std::shared_ptr daily_logger_st( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) + const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) { return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); } template inline std::shared_ptr daily_logger_format_st( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) + const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) { return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); } diff --git a/include/spdlog/sinks/hourly_file_sink.h b/include/spdlog/sinks/hourly_file_sink.h index 653faceb..8914d883 100644 --- a/include/spdlog/sinks/hourly_file_sink.h +++ b/include/spdlog/sinks/hourly_file_sink.h @@ -46,7 +46,7 @@ class hourly_file_sink final : public base_sink { public: // create hourly file sink which rotates on given time - hourly_file_sink(filename_t base_filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) + hourly_file_sink(filename_t base_filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) : base_filename_(std::move(base_filename)) , file_helper_{event_handlers} , truncate_(truncate) @@ -181,14 +181,14 @@ using hourly_file_sink_st = hourly_file_sink; // template inline std::shared_ptr hourly_logger_mt( - const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) + const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) { return Factory::template create(logger_name, filename, truncate, max_files, event_handlers); } template inline std::shared_ptr hourly_logger_st( - const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) + const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) { return Factory::template create(logger_name, filename, truncate, max_files, event_handlers); } diff --git a/include/spdlog/sinks/rotating_file_sink.h b/include/spdlog/sinks/rotating_file_sink.h index 8455227f..304e9e20 100644 --- a/include/spdlog/sinks/rotating_file_sink.h +++ b/include/spdlog/sinks/rotating_file_sink.h @@ -22,7 +22,7 @@ template class rotating_file_sink final : public base_sink { public: - rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}); + rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false, const file_event_handlers_t& event_handlers = {}); static filename_t calc_filename(const filename_t &filename, std::size_t index); filename_t filename(); @@ -60,14 +60,14 @@ using rotating_file_sink_st = rotating_file_sink; template inline std::shared_ptr rotating_logger_mt( - const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) + const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false, const file_event_handlers_t& event_handlers = {}) { return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers); } template inline std::shared_ptr rotating_logger_st( - const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false, const file_event_handlers_t& event_handlers = {nullptr, nullptr, nullptr}) + const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false, const file_event_handlers_t& event_handlers = {}) { return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers); } From 30fb78813beb00ca011ef56db8c2095451829f77 Mon Sep 17 00:00:00 2001 From: gabime Date: Mon, 15 Nov 2021 14:32:34 +0200 Subject: [PATCH 014/121] Updated file events example --- example/example.cpp | 54 +++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/example/example.cpp b/example/example.cpp index 1e145312..a3a6ff7a 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -22,6 +22,7 @@ void err_handler_example(); void syslog_example(); void udp_example(); void custom_flags_example(); +void file_events_example(); #include "spdlog/spdlog.h" #include "spdlog/cfg/env.h" // support for loading levels from the environment variable @@ -73,11 +74,13 @@ int main(int, char *[]) binary_example(); multi_sink_example(); user_defined_example(); - err_handler_example(); + err_handler_example(); trace_example(); stopwatch_example(); udp_example(); custom_flags_example(); + file_events_example(); + // Flush all *registered* loggers using a worker thread every 3 seconds. // note: registered loggers *must* be thread safe for this to work correctly! @@ -120,26 +123,7 @@ void basic_example() void rotating_example() { // Create a file rotating logger with 5mb size max and 3 rotated files. - spdlog::file_event_handlers_t file_event_handlers; - file_event_handlers.before_open = [](spdlog::filename_t filename) - { - spdlog::info("basic_example() : file_event_handlers.before_open : {}", filename.c_str()); - }; - file_event_handlers.after_open = [](spdlog::filename_t filename, std::FILE* fstream) - { - fputs("OPEN!\r\n", fstream); - spdlog::info("basic_example() : file_event_handlers.after_open : {}", filename.c_str()); - }; - file_event_handlers.before_close = [](spdlog::filename_t filename, std::FILE* fstream) - { - fputs("CLOSE!\r\n", fstream); - spdlog::info("basic_example() : file_event_handlers.before_close : {}", filename.c_str()); - }; - file_event_handlers.after_close = [](spdlog::filename_t filename) - { - spdlog::info("basic_example() : file_event_handlers.after_close : {}", filename.c_str()); - }; - auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3, false, file_event_handlers); + auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3); } #include "spdlog/sinks/daily_file_sink.h" @@ -321,5 +305,31 @@ void custom_flags_example() using spdlog::details::make_unique; // for pre c++14 auto formatter = make_unique(); formatter->add_flag('*').set_pattern("[%n] [%*] [%^%l%$] %v"); - spdlog::set_formatter(std::move(formatter)); + // set the new formatter using spdlog::set_formatter(formatter) or logger->set_formatter(formatter) + //spdlog::set_formatter(std::move(formatter)); +} + +void file_events_example(){ + + // pass the spdlog::file_event_handlers_t to file sinks for open/close log file notifications + spdlog::file_event_handlers_t handlers; + handlers.before_open = [](spdlog::filename_t filename) { + spdlog::info("Before opening {}", filename); + }; + handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { + spdlog::info("After opening {}", filename); + fputs("After opening\n", fstream); + }; + handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { + spdlog::info("Before closing {}", filename); + fputs("Before closing\n", fstream); + }; + handlers.after_close = [](spdlog::filename_t filename) { + spdlog::info("After closing {}", filename); + }; + auto file_sink = std::make_shared("logs/events-sample.txt", true, handlers); + spdlog::logger my_logger("some_logger", file_sink); + // or + // auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers); + my_logger.info("Some log line"); } From b813bb863d0b2616300547d864331cc10023ddc4 Mon Sep 17 00:00:00 2001 From: gabime Date: Mon, 15 Nov 2021 14:35:00 +0200 Subject: [PATCH 015/121] Updated file_events example --- example/example.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/example/example.cpp b/example/example.cpp index a3a6ff7a..a4324ffa 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -316,12 +316,10 @@ void file_events_example(){ handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); }; - handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { - spdlog::info("After opening {}", filename); + handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("After opening\n", fstream); }; - handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { - spdlog::info("Before closing {}", filename); + handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("Before closing\n", fstream); }; handlers.after_close = [](spdlog::filename_t filename) { From cbaf4880adbf78515a4347225472deb45c42c4c9 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Mon, 15 Nov 2021 14:42:36 +0200 Subject: [PATCH 016/121] Update README.md --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index 00e0ac29..6c3c4c28 100644 --- a/README.md +++ b/README.md @@ -373,6 +373,34 @@ $ export SPDLOG_LEVEL=info,mylogger=trace $ ./example ``` + +--- +#### Log file open/close event handlers +```c++ +// You can get subscribe to get callbacks from spdlog before/after log file has been opened or closed. +// This is useful for cleanup procedures or for adding someting the start/end of the log files. +void file_events_example(){ + spdlog::file_event_handlers_t handlers; + handlers.before_open = [](spdlog::filename_t filename) { + spdlog::info("Before opening {}", filename); + }; + handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { + fputs("After opening\n", fstream); + }; + handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { + fputs("Before closing\n", fstream); + }; + handlers.after_close = [](spdlog::filename_t filename) { + spdlog::info("After closing {}", filename); + }; + auto file_sink = std::make_shared("logs/events-sample.txt", true, handlers); + spdlog::logger my_logger("some_logger", file_sink); + // or + // auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers); + my_logger.info("Some log line"); +} +``` + --- ## Benchmarks From a5fa6eb356a0b408211458b1da0d2f7aadd5ad1c Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Mon, 15 Nov 2021 14:45:07 +0200 Subject: [PATCH 017/121] Update README.md --- README.md | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 6c3c4c28..f964e067 100644 --- a/README.md +++ b/README.md @@ -379,23 +379,17 @@ $ ./example ```c++ // You can get subscribe to get callbacks from spdlog before/after log file has been opened or closed. // This is useful for cleanup procedures or for adding someting the start/end of the log files. -void file_events_example(){ +void file_events_example() +{ + // pass the spdlog::file_event_handlers_t to file sinks for open/close log file notifications spdlog::file_event_handlers_t handlers; - handlers.before_open = [](spdlog::filename_t filename) { - spdlog::info("Before opening {}", filename); - }; - handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { - fputs("After opening\n", fstream); - }; - handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { - fputs("Before closing\n", fstream); - }; - handlers.after_close = [](spdlog::filename_t filename) { - spdlog::info("After closing {}", filename); - }; + handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); }; + handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("After opening\n", fstream); }; + handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("Before closing\n", fstream); }; + handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); }; auto file_sink = std::make_shared("logs/events-sample.txt", true, handlers); - spdlog::logger my_logger("some_logger", file_sink); - // or + spdlog::logger my_logger("some_logger", file_sink); + // or // auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers); my_logger.info("Some log line"); } From ea92864a4d01655befa2f08463085dbd812f865d Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Mon, 15 Nov 2021 14:46:23 +0200 Subject: [PATCH 018/121] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f964e067..b7d6d034 100644 --- a/README.md +++ b/README.md @@ -388,6 +388,7 @@ void file_events_example() handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("Before closing\n", fstream); }; handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); }; auto file_sink = std::make_shared("logs/events-sample.txt", true, handlers); + spdlog::logger my_logger("some_logger", file_sink); // or // auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers); From da621e4402f6af5a16068bf3b5ce487e1fec5fcd Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Mon, 15 Nov 2021 14:48:20 +0200 Subject: [PATCH 019/121] Update README.md --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index b7d6d034..ed673345 100644 --- a/README.md +++ b/README.md @@ -387,12 +387,7 @@ void file_events_example() handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("After opening\n", fstream); }; handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("Before closing\n", fstream); }; handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); }; - auto file_sink = std::make_shared("logs/events-sample.txt", true, handlers); - - spdlog::logger my_logger("some_logger", file_sink); - // or - // auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers); - my_logger.info("Some log line"); + auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers); } ``` From 698516f3f5e88c7fa31434ef0abb64b5d63e39be Mon Sep 17 00:00:00 2001 From: gabime Date: Mon, 15 Nov 2021 14:54:51 +0200 Subject: [PATCH 020/121] Updated example --- example/example.cpp | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/example/example.cpp b/example/example.cpp index a4324ffa..119fff55 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -74,13 +74,12 @@ int main(int, char *[]) binary_example(); multi_sink_example(); user_defined_example(); - err_handler_example(); + err_handler_example(); trace_example(); stopwatch_example(); udp_example(); custom_flags_example(); file_events_example(); - // Flush all *registered* loggers using a worker thread every 3 seconds. // note: registered loggers *must* be thread safe for this to work correctly! @@ -306,28 +305,24 @@ void custom_flags_example() auto formatter = make_unique(); formatter->add_flag('*').set_pattern("[%n] [%*] [%^%l%$] %v"); // set the new formatter using spdlog::set_formatter(formatter) or logger->set_formatter(formatter) - //spdlog::set_formatter(std::move(formatter)); + // spdlog::set_formatter(std::move(formatter)); } -void file_events_example(){ - - // pass the spdlog::file_event_handlers_t to file sinks for open/close log file notifications +void file_events_example() +{ + // pass the spdlog::file_event_handlers_t to file sinks for open/close log file notifications spdlog::file_event_handlers_t handlers; - handlers.before_open = [](spdlog::filename_t filename) { - spdlog::info("Before opening {}", filename); - }; - handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { - fputs("After opening\n", fstream); + handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); }; + handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { + spdlog::info("After opening {}", filename); + fputs("After opening\n", fstream); }; - handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { - fputs("Before closing\n", fstream); + handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { + spdlog::info("Before closing {}", filename); + fputs("Before closing\n", fstream); }; - handlers.after_close = [](spdlog::filename_t filename) { - spdlog::info("After closing {}", filename); - }; + handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); }; auto file_sink = std::make_shared("logs/events-sample.txt", true, handlers); - spdlog::logger my_logger("some_logger", file_sink); - // or - // auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers); + spdlog::logger my_logger("some_logger", file_sink); my_logger.info("Some log line"); } From 849e90bd017e4b29d2dc691f6a0f33864cabb628 Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Mon, 15 Nov 2021 13:36:29 -0500 Subject: [PATCH 021/121] Use /std:c++latest --- appveyor.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 6c5f2103..2c951071 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,6 +9,7 @@ environment: WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'ON' USE_STD_FORMAT: 'OFF' + CXX_STANDARD: 11 - GENERATOR: '"Visual Studio 14 2015"' BUILD_TYPE: Release BUILD_SHARED: 'OFF' @@ -16,6 +17,7 @@ environment: WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'ON' USE_STD_FORMAT: 'OFF' + CXX_STANDARD: 11 - GENERATOR: '"Visual Studio 14 2015 Win64"' BUILD_TYPE: Debug BUILD_SHARED: 'OFF' @@ -23,6 +25,7 @@ environment: WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'ON' USE_STD_FORMAT: 'OFF' + CXX_STANDARD: 11 - GENERATOR: '"Visual Studio 14 2015 Win64"' BUILD_TYPE: Release BUILD_SHARED: 'OFF' @@ -30,6 +33,7 @@ environment: WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'ON' USE_STD_FORMAT: 'OFF' + CXX_STANDARD: 11 - GENERATOR: '"Visual Studio 15 2017 Win64"' BUILD_TYPE: Debug BUILD_SHARED: 'OFF' @@ -37,6 +41,7 @@ environment: WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'ON' USE_STD_FORMAT: 'OFF' + CXX_STANDARD: 11 - GENERATOR: '"Visual Studio 15 2017 Win64"' BUILD_TYPE: Release BUILD_SHARED: 'OFF' @@ -44,6 +49,7 @@ environment: WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'ON' USE_STD_FORMAT: 'OFF' + CXX_STANDARD: 11 - GENERATOR: '"Visual Studio 15 2017 Win64"' BUILD_TYPE: Release BUILD_SHARED: 'ON' @@ -51,6 +57,7 @@ environment: WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'ON' USE_STD_FORMAT: 'OFF' + CXX_STANDARD: 11 - GENERATOR: '"Visual Studio 15 2017 Win64"' BUILD_TYPE: Release BUILD_SHARED: 'ON' @@ -58,6 +65,7 @@ environment: WCHAR_FILES: 'ON' BUILD_EXAMPLE: 'OFF' USE_STD_FORMAT: 'OFF' + CXX_STANDARD: 11 - GENERATOR: '"Visual Studio 16 2019" -A x64' BUILD_TYPE: Release BUILD_SHARED: 'ON' @@ -65,6 +73,7 @@ environment: WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'OFF' USE_STD_FORMAT: 'OFF' + CXX_STANDARD: 17 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - GENERATOR: '"Visual Studio 17 2022" -A x64' BUILD_TYPE: Release @@ -73,6 +82,7 @@ environment: WCHAR_FILES: 'OFF' BUILD_EXAMPLE: 'OFF' USE_STD_FORMAT: 'ON' + CXX_STANDARD: 23 # std::format is only available with /std:c++latest at the moment. APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 build_script: - cmd: >- @@ -84,7 +94,7 @@ build_script: set PATH=%PATH%;C:\Program Files\Git\usr\bin - cmake -G %GENERATOR% -D CMAKE_BUILD_TYPE=%BUILD_TYPE% -D BUILD_SHARED_LIBS=%BUILD_SHARED% -D SPDLOG_WCHAR_SUPPORT=%WCHAR% -D SPDLOG_WCHAR_FILENAMES=%WCHAR_FILES% -D SPDLOG_BUILD_EXAMPLE=%BUILD_EXAMPLE% -D SPDLOG_BUILD_EXAMPLE_HO=%BUILD_EXAMPLE% -D SPDLOG_BUILD_TESTS=ON -D SPDLOG_BUILD_TESTS_HO=OFF -D SPDLOG_BUILD_WARNINGS=ON -D SPDLOG_USE_STD_FORMAT=%USE_STD_FORMAT% .. + cmake -G %GENERATOR% -D CMAKE_BUILD_TYPE=%BUILD_TYPE% -D BUILD_SHARED_LIBS=%BUILD_SHARED% -D SPDLOG_WCHAR_SUPPORT=%WCHAR% -D SPDLOG_WCHAR_FILENAMES=%WCHAR_FILES% -D SPDLOG_BUILD_EXAMPLE=%BUILD_EXAMPLE% -D SPDLOG_BUILD_EXAMPLE_HO=%BUILD_EXAMPLE% -D SPDLOG_BUILD_TESTS=ON -D SPDLOG_BUILD_TESTS_HO=OFF -D SPDLOG_BUILD_WARNINGS=ON -D SPDLOG_USE_STD_FORMAT=%USE_STD_FORMAT% -D CMAKE_CXX_STANDARD=%CXX_STANDARD% .. cmake --build . --config %BUILD_TYPE% From f6901606f5a36047036072d0c6c58f0c9d90bb9f Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Mon, 15 Nov 2021 14:57:13 -0500 Subject: [PATCH 022/121] Add std::tm formatter, fix spdlog::stopwatch formatter, conditionally use fmt::runtime in test_errors --- include/spdlog/fmt/chrono.h | 86 ++++++++++++++++++++++++++++++++++++- include/spdlog/stopwatch.h | 11 ++++- tests/test_errors.cpp | 12 ++++++ 3 files changed, 105 insertions(+), 4 deletions(-) diff --git a/include/spdlog/fmt/chrono.h b/include/spdlog/fmt/chrono.h index 83fad2ff..17013bc1 100644 --- a/include/spdlog/fmt/chrono.h +++ b/include/spdlog/fmt/chrono.h @@ -4,11 +4,93 @@ // #pragma once + +#if defined(SPDLOG_USE_STD_FORMAT) +// Add a formatter for std::tm, since std::format only supports std::chrono +// taken from fmtlib +#include +#include +#include +#include +#include +#include +#include + +namespace spdlog::details +{ + inline size_t strftime(char *str, size_t count, const char *format, const std::tm *time) + { + // Assign to a pointer to suppress GCCs -Wformat-nonliteral + // First assign the nullptr to suppress -Wsuggest-attribute=format + std::size_t (*strftime)(char*, std::size_t, const char*, const std::tm*) = nullptr; + strftime = std::strftime; + return strftime(str, count, format, time); + } + + inline size_t strftime(wchar_t *str, size_t count, const wchar_t *format, const std::tm *time) + { + // See above + std::size_t (*wcsftime)(wchar_t*, std::size_t, const wchar_t*, const std::tm*) = nullptr; + wcsftime = std::wcsftime; + return wcsftime(str, count, format, time); + } + + // Casts a nonnegative integer to unsigned. + template + SPDLOG_CONSTEXPR auto to_unsigned(Int value) -> typename std::make_unsigned::type + { + assert(value >= 0, "negative value"); + return static_cast::type>(value); + } +} + +template +struct std::formatter +{ + template + SPDLOG_CONSTEXPR_FUNC auto parse(ParseContext &ctx) -> decltype(ctx.begin()) + { + auto it = ctx.begin(); + if (it != ctx.end() && *it == ':') ++it; + auto end = it; + while (end != ctx.end() && *end != '}') ++end; + specs = {it, spdlog::details::to_unsigned(end - it)}; + return end; + } + + template + auto format(const std::tm &tm, FormatContext &ctx) const -> decltype(ctx.out()) { + basic_string tm_format; + tm_format.append(specs); + // By appending an extra space we can distinguish an empty result that + // indicates insufficient buffer size from a guaranteed non-empty result + // https://github.com/fmtlib/fmt/issues/2238 + tm_format.push_back(' '); + + const size_t MIN_SIZE = 10; + basic_string buf; + buf.resize(MIN_SIZE); + for (;;) + { + size_t count = spdlog::details::strftime(buf.data(), buf.size(), tm_format.c_str(), &tm); + if (count != 0) + { + buf.resize(count); + break; + } + buf.resize(buf.size() * 2); + } + // Remove the extra space. + return std::copy(buf.begin(), buf.end() - 1, ctx.out()); + } + + basic_string_view specs; +}; + +#else // // include bundled or external copy of fmtlib's chrono support // - -#if !defined(SPDLOG_USE_STD_FORMAT) # if !defined(SPDLOG_FMT_EXTERNAL) # ifdef SPDLOG_HEADER_ONLY # ifndef FMT_HEADER_ONLY diff --git a/include/spdlog/stopwatch.h b/include/spdlog/stopwatch.h index bb976b19..52b4b6ad 100644 --- a/include/spdlog/stopwatch.h +++ b/include/spdlog/stopwatch.h @@ -48,7 +48,14 @@ public: } // namespace spdlog // Support for fmt formatting (e.g. "{:012.9}" or just "{}") -namespace fmt { +namespace +#ifdef SPDLOG_USE_STD_FORMAT + std +#else + fmt +#endif +{ + template<> struct formatter : formatter { @@ -58,4 +65,4 @@ struct formatter : formatter return formatter::format(sw.elapsed().count(), ctx); } }; -} // namespace fmt +} // namespace fmt/std diff --git a/tests/test_errors.cpp b/tests/test_errors.cpp index 41441d0d..c7aef812 100644 --- a/tests/test_errors.cpp +++ b/tests/test_errors.cpp @@ -29,7 +29,11 @@ TEST_CASE("default_error_handler", "[errors]]") auto logger = spdlog::create("test-error", filename, true); logger->set_pattern("%v"); +#ifdef SPDLOG_USE_STD_FORMAT + logger->info("Test message {} {}", 1); +#else logger->info(fmt::runtime("Test message {} {}"), 1); +#endif logger->info("Test message {}", 2); logger->flush(); @@ -49,7 +53,11 @@ TEST_CASE("custom_error_handler", "[errors]]") logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); logger->info("Good message #1"); +#ifdef SPDLOG_USE_STD_FORMAT + REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex); +#else REQUIRE_THROWS_AS(logger->info(fmt::runtime("Bad format msg {} {}"), "xxx"), custom_ex); +#endif logger->info("Good message #2"); require_message_count(SIMPLE_LOG, 2); } @@ -88,7 +96,11 @@ TEST_CASE("async_error_handler", "[errors]]") ofs << err_msg; }); logger->info("Good message #1"); +#ifdef SPDLOG_USE_STD_FORMAT + logger->info("Bad format msg {} {}", "xxx"); +#else logger->info(fmt::runtime("Bad format msg {} {}"), "xxx"); +#endif logger->info("Good message #2"); spdlog::drop("logger"); // force logger to drain the queue and shutdown } From 2d77ef92b02242cecdf5f1e633706421bf2c2088 Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Mon, 15 Nov 2021 15:27:34 -0500 Subject: [PATCH 023/121] Avoid specializing std::formatter for std::tm (not a great idea after all) --- include/spdlog/details/fmt_helper.h | 17 +++++ include/spdlog/fmt/chrono.h | 86 +------------------------- include/spdlog/sinks/daily_file_sink.h | 59 +++++++++++++++--- include/spdlog/stopwatch.h | 2 +- tests/test_file_logging.cpp | 4 +- 5 files changed, 73 insertions(+), 95 deletions(-) diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h index 25976f50..0a8de309 100644 --- a/include/spdlog/details/fmt_helper.h +++ b/include/spdlog/details/fmt_helper.h @@ -164,6 +164,23 @@ inline ToDuration time_fraction(log_clock::time_point tp) return duration_cast(duration) - duration_cast(secs); } +inline size_t strftime(char *str, size_t count, const char *format, const std::tm *time) +{ + // Assign to a pointer to suppress GCCs -Wformat-nonliteral + // First assign the nullptr to suppress -Wsuggest-attribute=format + std::size_t (*strftime)(char*, std::size_t, const char*, const std::tm*) = nullptr; + strftime = std::strftime; + return strftime(str, count, format, time); +} + +inline size_t strftime(wchar_t *str, size_t count, const wchar_t *format, const std::tm *time) +{ + // See above + std::size_t (*wcsftime)(wchar_t*, std::size_t, const wchar_t*, const std::tm*) = nullptr; + wcsftime = std::wcsftime; + return wcsftime(str, count, format, time); +} + } // namespace fmt_helper } // namespace details } // namespace spdlog diff --git a/include/spdlog/fmt/chrono.h b/include/spdlog/fmt/chrono.h index 17013bc1..83fad2ff 100644 --- a/include/spdlog/fmt/chrono.h +++ b/include/spdlog/fmt/chrono.h @@ -4,93 +4,11 @@ // #pragma once - -#if defined(SPDLOG_USE_STD_FORMAT) -// Add a formatter for std::tm, since std::format only supports std::chrono -// taken from fmtlib -#include -#include -#include -#include -#include -#include -#include - -namespace spdlog::details -{ - inline size_t strftime(char *str, size_t count, const char *format, const std::tm *time) - { - // Assign to a pointer to suppress GCCs -Wformat-nonliteral - // First assign the nullptr to suppress -Wsuggest-attribute=format - std::size_t (*strftime)(char*, std::size_t, const char*, const std::tm*) = nullptr; - strftime = std::strftime; - return strftime(str, count, format, time); - } - - inline size_t strftime(wchar_t *str, size_t count, const wchar_t *format, const std::tm *time) - { - // See above - std::size_t (*wcsftime)(wchar_t*, std::size_t, const wchar_t*, const std::tm*) = nullptr; - wcsftime = std::wcsftime; - return wcsftime(str, count, format, time); - } - - // Casts a nonnegative integer to unsigned. - template - SPDLOG_CONSTEXPR auto to_unsigned(Int value) -> typename std::make_unsigned::type - { - assert(value >= 0, "negative value"); - return static_cast::type>(value); - } -} - -template -struct std::formatter -{ - template - SPDLOG_CONSTEXPR_FUNC auto parse(ParseContext &ctx) -> decltype(ctx.begin()) - { - auto it = ctx.begin(); - if (it != ctx.end() && *it == ':') ++it; - auto end = it; - while (end != ctx.end() && *end != '}') ++end; - specs = {it, spdlog::details::to_unsigned(end - it)}; - return end; - } - - template - auto format(const std::tm &tm, FormatContext &ctx) const -> decltype(ctx.out()) { - basic_string tm_format; - tm_format.append(specs); - // By appending an extra space we can distinguish an empty result that - // indicates insufficient buffer size from a guaranteed non-empty result - // https://github.com/fmtlib/fmt/issues/2238 - tm_format.push_back(' '); - - const size_t MIN_SIZE = 10; - basic_string buf; - buf.resize(MIN_SIZE); - for (;;) - { - size_t count = spdlog::details::strftime(buf.data(), buf.size(), tm_format.c_str(), &tm); - if (count != 0) - { - buf.resize(count); - break; - } - buf.resize(buf.size() * 2); - } - // Remove the extra space. - return std::copy(buf.begin(), buf.end() - 1, ctx.out()); - } - - basic_string_view specs; -}; - -#else // // include bundled or external copy of fmtlib's chrono support // + +#if !defined(SPDLOG_USE_STD_FORMAT) # if !defined(SPDLOG_FMT_EXTERNAL) # ifdef SPDLOG_HEADER_ONLY # ifndef FMT_HEADER_ONLY diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index e56ab763..0e41612a 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -5,9 +5,9 @@ #include #include +#include #include #include -#include #include #include #include @@ -48,14 +48,57 @@ struct daily_filename_format_calculator { static filename_t calc_filename(const filename_t &filename, const tm &now_tm) { - // generate fmt datetime format string, e.g. {:%Y-%m-%d}. - filename_t fmt_filename = fmt_lib::format(SPDLOG_FILENAME_T("{{:{}}}"), filename); -#if defined(SPDLOG_USE_STD_FORMAT) - return std::vformat(fmt_filename, std::make_format_args(now_tm)); -#elif defined(_MSC_VER) && defined(SPDLOG_WCHAR_FILENAMES) // for some reason msvc doesn't allow fmt::runtime(..) with wchar here - return fmt::format(fmt_filename, now_tm); + // adapted from fmtlib +#ifdef SPDLOG_USE_STD_FORMAT + filename_t tm_format; + tm_format.append(filename); + // By appending an extra space we can distinguish an empty result that + // indicates insufficient buffer size from a guaranteed non-empty result + // https://github.com/fmtlib/fmt/issues/2238 + tm_format.push_back(' '); + + const size_t MIN_SIZE = 10; + filename_t buf; + buf.resize(MIN_SIZE); + for (;;) + { + size_t count = details::fmt_helper::strftime(buf.data(), buf.size(), tm_format.c_str(), &now_tm); + if (count != 0) + { + // Remove the extra space. + buf.resize(count - 1); + break; + } + buf.resize(buf.size() * 2); + } + + return buf; #else - return fmt::format(SPDLOG_FMT_RUNTIME(fmt_filename), now_tm); + fmt::basic_memory_buffer tm_format; + tm_format.append(specs.begin(), specs.end()); + // By appending an extra space we can distinguish an empty result that + // indicates insufficient buffer size from a guaranteed non-empty result + // https://github.com/fmtlib/fmt/issues/2238 + tm_format.push_back(' '); + tm_format.push_back('\0'); + + fmt::basic_memory_buffer buf; + size_t start = buf.size(); + for (;;) + { + size_t size = buf.capacity() - start; + size_t count = details::fmt_helper:::strftime(&buf[start], size, &tm_format[0], &tm); + if (count != 0) + { + // Remove the extra space. + buf.resize(start + count - 1); + break; + } + const size_t MIN_GROWTH = 10; + buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); + } + + return fmt::to_string(buf); #endif } }; diff --git a/include/spdlog/stopwatch.h b/include/spdlog/stopwatch.h index 52b4b6ad..a1542b2d 100644 --- a/include/spdlog/stopwatch.h +++ b/include/spdlog/stopwatch.h @@ -42,7 +42,7 @@ public: void reset() { - start_tp_ = clock ::now(); + start_tp_ = clock::now(); } }; } // namespace spdlog diff --git a/tests/test_file_logging.cpp b/tests/test_file_logging.cpp index eab524dc..c808916d 100644 --- a/tests/test_file_logging.cpp +++ b/tests/test_file_logging.cpp @@ -20,7 +20,7 @@ TEST_CASE("simple_file_logger", "[simple_logger]]") logger->flush(); require_message_count(SIMPLE_LOG, 2); using spdlog::details::os::default_eol; - REQUIRE(file_contents(SIMPLE_LOG) == fmt::format("Test message 1{}Test message 2{}", default_eol, default_eol)); + REQUIRE(file_contents(SIMPLE_LOG) == spdlog::fmt_lib::format("Test message 1{}Test message 2{}", default_eol, default_eol)); } TEST_CASE("flush_on", "[flush_on]]") @@ -41,7 +41,7 @@ TEST_CASE("flush_on", "[flush_on]]") require_message_count(SIMPLE_LOG, 3); using spdlog::details::os::default_eol; REQUIRE(file_contents(SIMPLE_LOG) == - fmt::format("Should not be flushed{}Test message 1{}Test message 2{}", default_eol, default_eol, default_eol)); + spdlog::fmt_lib::format("Should not be flushed{}Test message 1{}Test message 2{}", default_eol, default_eol, default_eol)); } TEST_CASE("rotating_file_logger1", "[rotating_logger]]") From 108c656e66411d199db2a4e74e1fe768e1c96989 Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Mon, 15 Nov 2021 15:29:16 -0500 Subject: [PATCH 024/121] Fix copy-paste mistake --- include/spdlog/sinks/daily_file_sink.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index 0e41612a..9e907cd3 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -87,7 +87,7 @@ struct daily_filename_format_calculator for (;;) { size_t size = buf.capacity() - start; - size_t count = details::fmt_helper:::strftime(&buf[start], size, &tm_format[0], &tm); + size_t count = details::fmt_helper::strftime(&buf[start], size, &tm_format[0], &tm); if (count != 0) { // Remove the extra space. From a6945d046f3f4f53822a5b7cc296a3ad385f11af Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Mon, 15 Nov 2021 15:30:30 -0500 Subject: [PATCH 025/121] Fix use of Char --- include/spdlog/sinks/daily_file_sink.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index 9e907cd3..c1c87607 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -74,7 +74,7 @@ struct daily_filename_format_calculator return buf; #else - fmt::basic_memory_buffer tm_format; + fmt::basic_memory_buffer tm_format; tm_format.append(specs.begin(), specs.end()); // By appending an extra space we can distinguish an empty result that // indicates insufficient buffer size from a guaranteed non-empty result @@ -82,7 +82,7 @@ struct daily_filename_format_calculator tm_format.push_back(' '); tm_format.push_back('\0'); - fmt::basic_memory_buffer buf; + fmt::basic_memory_buffer buf; size_t start = buf.size(); for (;;) { From ba120e524b5a0e6e772c67c0aba45d493a22f49f Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Mon, 15 Nov 2021 15:46:22 -0500 Subject: [PATCH 026/121] Add unit test for daily_filename_format_calculator --- include/spdlog/sinks/daily_file_sink.h | 2 +- tests/test_daily_logger.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index c1c87607..b3323780 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -75,7 +75,7 @@ struct daily_filename_format_calculator return buf; #else fmt::basic_memory_buffer tm_format; - tm_format.append(specs.begin(), specs.end()); + tm_format.append(filename.c_str(), filename.c_str() + filename.size()); // By appending an extra space we can distinguish an empty result that // indicates insufficient buffer size from a guaranteed non-empty result // https://github.com/fmtlib/fmt/issues/2238 diff --git a/tests/test_daily_logger.cpp b/tests/test_daily_logger.cpp index 93bd4662..66f01a46 100644 --- a/tests/test_daily_logger.cpp +++ b/tests/test_daily_logger.cpp @@ -142,6 +142,16 @@ TEST_CASE("daily_file_sink::daily_filename_calculator", "[daily_file_sink]]") } #endif +TEST_CASE("daily_file_sink::daily_filename_format_calculator", "[daily_file_sink]]") +{ + std::tm tm = spdlog::details::os::localtime(); + // example-YYYY-MM-DD.log + auto filename = + spdlog::sinks::daily_filename_format_calculator::calc_filename(SPDLOG_FILENAME_T("example-%Y-%m-%d.log"), tm); + + REQUIRE(filename == spdlog::fmt_lib::format(SPDLOG_FILENAME_T("example-{:04d}-{:02d}-{:02d}.log"), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday)); +} + /* Test removal of old files */ static spdlog::details::log_msg create_msg(std::chrono::seconds offset) { From 95aa159bddb8a84c6ad21eefc6ae26232defa719 Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Mon, 15 Nov 2021 15:50:16 -0500 Subject: [PATCH 027/121] Fix daily_filename_format_calculator (hopefully) --- include/spdlog/sinks/daily_file_sink.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index b3323780..8cdf35c1 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -87,7 +87,7 @@ struct daily_filename_format_calculator for (;;) { size_t size = buf.capacity() - start; - size_t count = details::fmt_helper::strftime(&buf[start], size, &tm_format[0], &tm); + size_t count = details::fmt_helper::strftime(&buf[start], size, &tm_format[0], &now_tm); if (count != 0) { // Remove the extra space. From 0ded003703fb848bcffbc51e9f9aad287d347a08 Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Mon, 15 Nov 2021 16:52:31 -0500 Subject: [PATCH 028/121] Fix wchar_t overloads and dump_info formatter --- include/spdlog/common.h | 2 +- include/spdlog/fmt/bin_to_hex.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 191ed224..b702c485 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -154,7 +154,7 @@ template using format_string_t = fmt::format_string; template -using wformat_string_t = fmt::basic_format_string; +using wformat_string_t = fmt::basic_format_string...>; template using remove_cvref_t = typename std::remove_cv::type>::type; diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h index 0dd16e7a..835e4f07 100644 --- a/include/spdlog/fmt/bin_to_hex.h +++ b/include/spdlog/fmt/bin_to_hex.h @@ -137,7 +137,7 @@ struct formatter, char> SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef"; const char *hex_chars = use_uppercase ? hex_upper : hex_lower; -#if FMT_VERSION < 60000 +#if !defined(SPDLOG_USE_STD_FORMAT) && FMT_VERSION < 60000 auto inserter = ctx.begin(); #else auto inserter = ctx.out(); From 484bf07379f9b65f7b867254bdcc993642b7027f Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Mon, 15 Nov 2021 18:34:40 -0500 Subject: [PATCH 029/121] Fix test_fmt_helper --- tests/test_fmt_helper.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_fmt_helper.cpp b/tests/test_fmt_helper.cpp index 6972d2d2..dde5d482 100644 --- a/tests/test_fmt_helper.cpp +++ b/tests/test_fmt_helper.cpp @@ -10,7 +10,7 @@ void test_pad2(int n, const char *expected) spdlog::details::fmt_helper::pad2(n, buf); #ifdef SPDLOG_USE_STD_FORMAT - REQUIRE(formatted == expected); + REQUIRE(buf == expected); #else REQUIRE(fmt::to_string(buf) == expected); #endif @@ -22,7 +22,7 @@ void test_pad3(uint32_t n, const char *expected) spdlog::details::fmt_helper::pad3(n, buf); #ifdef SPDLOG_USE_STD_FORMAT - REQUIRE(formatted == expected); + REQUIRE(buf == expected); #else REQUIRE(fmt::to_string(buf) == expected); #endif @@ -34,7 +34,7 @@ void test_pad6(std::size_t n, const char *expected) spdlog::details::fmt_helper::pad6(n, buf); #ifdef SPDLOG_USE_STD_FORMAT - REQUIRE(formatted == expected); + REQUIRE(buf == expected); #else REQUIRE(fmt::to_string(buf) == expected); #endif @@ -46,7 +46,7 @@ void test_pad9(std::size_t n, const char *expected) spdlog::details::fmt_helper::pad9(n, buf); #ifdef SPDLOG_USE_STD_FORMAT - REQUIRE(formatted == expected); + REQUIRE(buf == expected); #else REQUIRE(fmt::to_string(buf) == expected); #endif From 5b7dfefc7e94b74c7f84db874abffe716df49257 Mon Sep 17 00:00:00 2001 From: gabime Date: Tue, 16 Nov 2021 16:41:04 +0200 Subject: [PATCH 030/121] rename file_event_handlers_t to file_event_handlers --- README.md | 4 ++-- example/example.cpp | 4 ++-- include/spdlog/common.h | 4 ++-- include/spdlog/details/file_helper-inl.h | 2 +- include/spdlog/details/file_helper.h | 4 ++-- include/spdlog/sinks/basic_file_sink-inl.h | 2 +- include/spdlog/sinks/basic_file_sink.h | 6 +++--- include/spdlog/sinks/daily_file_sink.h | 10 +++++----- include/spdlog/sinks/hourly_file_sink.h | 6 +++--- include/spdlog/sinks/rotating_file_sink-inl.h | 2 +- include/spdlog/sinks/rotating_file_sink.h | 6 +++--- 11 files changed, 25 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index ed673345..d1f228c2 100644 --- a/README.md +++ b/README.md @@ -381,8 +381,8 @@ $ ./example // This is useful for cleanup procedures or for adding someting the start/end of the log files. void file_events_example() { - // pass the spdlog::file_event_handlers_t to file sinks for open/close log file notifications - spdlog::file_event_handlers_t handlers; + // pass the spdlog::file_event_handlers to file sinks for open/close log file notifications + spdlog::file_event_handlers handlers; handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); }; handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("After opening\n", fstream); }; handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("Before closing\n", fstream); }; diff --git a/example/example.cpp b/example/example.cpp index 119fff55..e379ca34 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -310,8 +310,8 @@ void custom_flags_example() void file_events_example() { - // pass the spdlog::file_event_handlers_t to file sinks for open/close log file notifications - spdlog::file_event_handlers_t handlers; + // pass the spdlog::file_event_handlers to file sinks for open/close log file notifications + spdlog::file_event_handlers handlers; handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); }; handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { spdlog::info("After opening {}", filename); diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 0dac1726..95877beb 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -256,7 +256,7 @@ struct source_loc const char *funcname{nullptr}; }; -typedef struct file_event_handlers +struct file_event_handlers { std::function before_open; std::function after_open; @@ -265,7 +265,7 @@ typedef struct file_event_handlers file_event_handlers(): before_open(nullptr), after_open(nullptr), before_close(nullptr), after_close(nullptr) {} -} file_event_handlers_t; +}; namespace details { // make_unique support for pre c++14 diff --git a/include/spdlog/details/file_helper-inl.h b/include/spdlog/details/file_helper-inl.h index ab36f52f..63d511c6 100644 --- a/include/spdlog/details/file_helper-inl.h +++ b/include/spdlog/details/file_helper-inl.h @@ -20,7 +20,7 @@ namespace spdlog { namespace details { -SPDLOG_INLINE file_helper::file_helper(const file_event_handlers_t& event_handlers) +SPDLOG_INLINE file_helper::file_helper(const file_event_handlers& event_handlers) : event_handlers_(event_handlers) {} diff --git a/include/spdlog/details/file_helper.h b/include/spdlog/details/file_helper.h index 6b63f3a1..69c5afdc 100644 --- a/include/spdlog/details/file_helper.h +++ b/include/spdlog/details/file_helper.h @@ -17,7 +17,7 @@ class SPDLOG_API file_helper { public: file_helper() = default; - explicit file_helper(const file_event_handlers_t& event_handlers); + explicit file_helper(const file_event_handlers& event_handlers); file_helper(const file_helper &) = delete; file_helper &operator=(const file_helper &) = delete; @@ -51,7 +51,7 @@ private: const unsigned int open_interval_ = 10; std::FILE *fd_{nullptr}; filename_t filename_; - file_event_handlers_t event_handlers_; + file_event_handlers event_handlers_; }; } // namespace details } // namespace spdlog diff --git a/include/spdlog/sinks/basic_file_sink-inl.h b/include/spdlog/sinks/basic_file_sink-inl.h index 463e075f..af383214 100644 --- a/include/spdlog/sinks/basic_file_sink-inl.h +++ b/include/spdlog/sinks/basic_file_sink-inl.h @@ -14,7 +14,7 @@ namespace spdlog { namespace sinks { template -SPDLOG_INLINE basic_file_sink::basic_file_sink(const filename_t &filename, bool truncate, const file_event_handlers_t& event_handlers) +SPDLOG_INLINE basic_file_sink::basic_file_sink(const filename_t &filename, bool truncate, const file_event_handlers& event_handlers) : file_helper_{event_handlers} { file_helper_.open(filename, truncate); diff --git a/include/spdlog/sinks/basic_file_sink.h b/include/spdlog/sinks/basic_file_sink.h index f2e35f6e..12cd95ef 100644 --- a/include/spdlog/sinks/basic_file_sink.h +++ b/include/spdlog/sinks/basic_file_sink.h @@ -20,7 +20,7 @@ template class basic_file_sink final : public base_sink { public: - explicit basic_file_sink(const filename_t &filename, bool truncate = false, const file_event_handlers_t& event_handlers = {}); + explicit basic_file_sink(const filename_t &filename, bool truncate = false, const file_event_handlers& event_handlers = {}); const filename_t &filename() const; protected: @@ -40,13 +40,13 @@ using basic_file_sink_st = basic_file_sink; // factory functions // template -inline std::shared_ptr basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers_t& event_handlers = {}) +inline std::shared_ptr basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers& event_handlers = {}) { return Factory::template create(logger_name, filename, truncate, event_handlers); } template -inline std::shared_ptr basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers_t& event_handlers = {}) +inline std::shared_ptr basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers& event_handlers = {}) { return Factory::template create(logger_name, filename, truncate, event_handlers); } diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index 000becb4..a4469d64 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -68,7 +68,7 @@ class daily_file_sink final : public base_sink { public: // create daily file sink which rotates on given time - daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) + daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) : base_filename_(std::move(base_filename)) , rotation_h_(rotation_hour) , rotation_m_(rotation_minute) @@ -215,28 +215,28 @@ using daily_file_format_sink_st = daily_file_sink inline std::shared_ptr daily_logger_mt( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) + const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) { return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); } template inline std::shared_ptr daily_logger_format_mt( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) + const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) { return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); } template inline std::shared_ptr daily_logger_st( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) + const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) { return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); } template inline std::shared_ptr daily_logger_format_st( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) + const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) { return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); } diff --git a/include/spdlog/sinks/hourly_file_sink.h b/include/spdlog/sinks/hourly_file_sink.h index 8914d883..3656cb0f 100644 --- a/include/spdlog/sinks/hourly_file_sink.h +++ b/include/spdlog/sinks/hourly_file_sink.h @@ -46,7 +46,7 @@ class hourly_file_sink final : public base_sink { public: // create hourly file sink which rotates on given time - hourly_file_sink(filename_t base_filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) + hourly_file_sink(filename_t base_filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) : base_filename_(std::move(base_filename)) , file_helper_{event_handlers} , truncate_(truncate) @@ -181,14 +181,14 @@ using hourly_file_sink_st = hourly_file_sink; // template inline std::shared_ptr hourly_logger_mt( - const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) + const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) { return Factory::template create(logger_name, filename, truncate, max_files, event_handlers); } template inline std::shared_ptr hourly_logger_st( - const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers_t& event_handlers = {}) + const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) { return Factory::template create(logger_name, filename, truncate, max_files, event_handlers); } diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h index 2e3087af..ae715d28 100644 --- a/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -25,7 +25,7 @@ namespace sinks { template SPDLOG_INLINE rotating_file_sink::rotating_file_sink( - filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open, const file_event_handlers_t& event_handlers) + filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open, const file_event_handlers& event_handlers) : base_filename_(std::move(base_filename)) , max_size_(max_size) , max_files_(max_files) diff --git a/include/spdlog/sinks/rotating_file_sink.h b/include/spdlog/sinks/rotating_file_sink.h index 304e9e20..d34ec02e 100644 --- a/include/spdlog/sinks/rotating_file_sink.h +++ b/include/spdlog/sinks/rotating_file_sink.h @@ -22,7 +22,7 @@ template class rotating_file_sink final : public base_sink { public: - rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false, const file_event_handlers_t& event_handlers = {}); + rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false, const file_event_handlers& event_handlers = {}); static filename_t calc_filename(const filename_t &filename, std::size_t index); filename_t filename(); @@ -60,14 +60,14 @@ using rotating_file_sink_st = rotating_file_sink; template inline std::shared_ptr rotating_logger_mt( - const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false, const file_event_handlers_t& event_handlers = {}) + const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false, const file_event_handlers& event_handlers = {}) { return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers); } template inline std::shared_ptr rotating_logger_st( - const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false, const file_event_handlers_t& event_handlers = {}) + const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false, const file_event_handlers& event_handlers = {}) { return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers); } From 518bf36aa97f77d9f83c5fde1a4bc48f8a667654 Mon Sep 17 00:00:00 2001 From: gabime Date: Tue, 16 Nov 2021 16:44:47 +0200 Subject: [PATCH 031/121] removed redundant intialization --- include/spdlog/common.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 95877beb..ce62b9b9 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -262,9 +262,6 @@ struct file_event_handlers std::function after_open; std::function before_close; std::function after_close; - - file_event_handlers(): before_open(nullptr), after_open(nullptr), before_close(nullptr), after_close(nullptr) - {} }; namespace details { From 5d6af189f13678ae1becad9b1438455a25256102 Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Tue, 16 Nov 2021 09:59:48 -0500 Subject: [PATCH 032/121] Use target.capacity() even with std::string --- include/spdlog/details/os-inl.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index 9b2ef0d8..e4cddd41 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -462,12 +462,8 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target) return; } -#ifdef SPDLOG_USE_STD_FORMAT - int result_size = 0; -#else int result_size = static_cast(target.capacity()); if ((wstr_size + 1) * 2 > result_size) -#endif { result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL); } @@ -501,12 +497,8 @@ SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target) return; } -#ifdef SPDLOG_USE_STD_FORMAT - int result_size = 0; -#else int result_size = static_cast(target.capacity()); if (str_size + 1 > result_size) -#endif { result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0); } From 701ef172272eca44e95a71724ac18cd61b5d2316 Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Tue, 16 Nov 2021 10:05:35 -0500 Subject: [PATCH 033/121] Move strftime to daily_filename_format_calculator --- include/spdlog/details/fmt_helper.h | 17 ----------------- include/spdlog/sinks/daily_file_sink.h | 24 ++++++++++++++++++++++-- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h index 0a8de309..25976f50 100644 --- a/include/spdlog/details/fmt_helper.h +++ b/include/spdlog/details/fmt_helper.h @@ -164,23 +164,6 @@ inline ToDuration time_fraction(log_clock::time_point tp) return duration_cast(duration) - duration_cast(secs); } -inline size_t strftime(char *str, size_t count, const char *format, const std::tm *time) -{ - // Assign to a pointer to suppress GCCs -Wformat-nonliteral - // First assign the nullptr to suppress -Wsuggest-attribute=format - std::size_t (*strftime)(char*, std::size_t, const char*, const std::tm*) = nullptr; - strftime = std::strftime; - return strftime(str, count, format, time); -} - -inline size_t strftime(wchar_t *str, size_t count, const wchar_t *format, const std::tm *time) -{ - // See above - std::size_t (*wcsftime)(wchar_t*, std::size_t, const wchar_t*, const std::tm*) = nullptr; - wcsftime = std::wcsftime; - return wcsftime(str, count, format, time); -} - } // namespace fmt_helper } // namespace details } // namespace spdlog diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index 8cdf35c1..447f2529 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -62,7 +62,7 @@ struct daily_filename_format_calculator buf.resize(MIN_SIZE); for (;;) { - size_t count = details::fmt_helper::strftime(buf.data(), buf.size(), tm_format.c_str(), &now_tm); + size_t count = strftime(buf.data(), buf.size(), tm_format.c_str(), &now_tm); if (count != 0) { // Remove the extra space. @@ -87,7 +87,7 @@ struct daily_filename_format_calculator for (;;) { size_t size = buf.capacity() - start; - size_t count = details::fmt_helper::strftime(&buf[start], size, &tm_format[0], &now_tm); + size_t count = strftime(&buf[start], size, &tm_format[0], &now_tm); if (count != 0) { // Remove the extra space. @@ -101,6 +101,26 @@ struct daily_filename_format_calculator return fmt::to_string(buf); #endif } + +private: +#if defined __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + + static size_t strftime(char *str, size_t count, const char *format, const std::tm *time) + { + return std::strftime(str, count, format, time); + } + + static size_t strftime(wchar_t *str, size_t count, const wchar_t *format, const std::tm *time) + { + return std::wcsftime(str, count, format, time); + } + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif }; /* From ad779e486557008158e900f1e0eef170bbf6b3ea Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Tue, 16 Nov 2021 10:10:02 -0500 Subject: [PATCH 034/121] Attempt to solve ambiguous symbol on older MSVC --- include/spdlog/common.h | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/include/spdlog/common.h b/include/spdlog/common.h index b702c485..0d170412 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -127,35 +127,33 @@ using err_handler = std::function; namespace fmt_lib = std; using string_view_t = std::string_view; -using wstring_view_t = std::wstring_view; using memory_buf_t = std::string; -using wmemory_buf_t = std::wstring; template using format_string_t = std::string_view; -template -using wformat_string_t = std::wstring_view; - template struct is_convertible_to_basic_format_string : std::integral_constant>::value> {}; + +# if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) +using wstring_view_t = std::wstring_view; +using memory_buf_t = std::wstring; + +template +using wformat_string_t = std::wstring_view; +# endif #else namespace fmt_lib = fmt; using string_view_t = fmt::basic_string_view; -using wstring_view_t = fmt::basic_string_view; using memory_buf_t = fmt::basic_memory_buffer; -using wmemory_buf_t = fmt::basic_memory_buffer; template using format_string_t = fmt::format_string; -template -using wformat_string_t = fmt::basic_format_string...>; - template using remove_cvref_t = typename std::remove_cv::type>::type; @@ -166,6 +164,14 @@ struct is_convertible_to_basic_format_string : std::integral_constant>::value || std::is_same, fmt::basic_runtime>::value> {}; + +# if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) +using wstring_view_t = fmt::basic_string_view; +using wmemory_buf_t = fmt::basic_memory_buffer;; + +template +using wformat_string_t = fmt::wformat_string; +# endif #endif #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT From 4001032858cb0c4a5d59a58f597b6c1da469c5f9 Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Tue, 16 Nov 2021 11:22:30 -0500 Subject: [PATCH 035/121] Add attribution, return to previous code for daily_filename_format_calculator with fmtlib --- include/spdlog/details/fmt_helper.h | 2 +- include/spdlog/sinks/daily_file_sink.h | 37 +++++++------------------- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h index 25976f50..cc59dd62 100644 --- a/include/spdlog/details/fmt_helper.h +++ b/include/spdlog/details/fmt_helper.h @@ -59,7 +59,7 @@ inline void append_int(T n, memory_buf_t &dest) template SPDLOG_CONSTEXPR_FUNC unsigned int count_digits_fallback(T n) { - // taken from fmt. + // taken from fmt: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/format.h#L899-L912 unsigned int count = 1; for (;;) { diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index 447f2529..36701e98 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -5,9 +5,9 @@ #include #include -#include #include #include +#include #include #include #include @@ -48,8 +48,9 @@ struct daily_filename_format_calculator { static filename_t calc_filename(const filename_t &filename, const tm &now_tm) { - // adapted from fmtlib #ifdef SPDLOG_USE_STD_FORMAT + // adapted from fmtlib: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/chrono.h#L522-L546 + filename_t tm_format; tm_format.append(filename); // By appending an extra space we can distinguish an empty result that @@ -74,31 +75,13 @@ struct daily_filename_format_calculator return buf; #else - fmt::basic_memory_buffer tm_format; - tm_format.append(filename.c_str(), filename.c_str() + filename.size()); - // By appending an extra space we can distinguish an empty result that - // indicates insufficient buffer size from a guaranteed non-empty result - // https://github.com/fmtlib/fmt/issues/2238 - tm_format.push_back(' '); - tm_format.push_back('\0'); - - fmt::basic_memory_buffer buf; - size_t start = buf.size(); - for (;;) - { - size_t size = buf.capacity() - start; - size_t count = strftime(&buf[start], size, &tm_format[0], &now_tm); - if (count != 0) - { - // Remove the extra space. - buf.resize(start + count - 1); - break; - } - const size_t MIN_GROWTH = 10; - buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); - } - - return fmt::to_string(buf); + // generate fmt datetime format string, e.g. {:%Y-%m-%d}. + filename_t fmt_filename = fmt::format(SPDLOG_FILENAME_T("{{:{}}}"), filename); +# if defined(_MSC_VER) && defined(SPDLOG_WCHAR_FILENAMES) // for some reason msvc doesn't allow fmt::runtime(..) with wchar here + return fmt::format(fmt_filename, now_tm); +# else + return fmt::format(SPDLOG_FMT_RUNTIME(fmt_filename), now_tm); +# endif #endif } From 10b640d773a22eaf525258c56fec5e48863c736c Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Tue, 16 Nov 2021 22:37:43 +0200 Subject: [PATCH 036/121] Update example.cpp --- example/example.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/example.cpp b/example/example.cpp index e379ca34..93a7e0b4 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -115,7 +115,7 @@ void stdout_logger_example() void basic_example() { // Create basic file logger (not rotated). - auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt"); + auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt", true); } #include "spdlog/sinks/rotating_file_sink.h" From 24e47efae0dbe8c61f65c5d4140f305bc47ac340 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Tue, 16 Nov 2021 22:48:02 +0200 Subject: [PATCH 037/121] fix gcc 4.8 compile warning --- include/spdlog/common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 40bac3e6..7987b085 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -308,6 +308,7 @@ struct file_event_handlers std::function after_open; std::function before_close; std::function after_close; + file_event_handlers() : before_open{nullptr}, after_open{nullptr}, before_close{nullptr}, after_close{nullptr} {} }; namespace details { From 0df25826743dbbecde9013c5a2b67fe091b0d856 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Tue, 16 Nov 2021 23:21:11 +0200 Subject: [PATCH 038/121] Update appveyor.yml --- appveyor.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 2c951071..c3b23523 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -84,6 +84,15 @@ environment: USE_STD_FORMAT: 'ON' CXX_STANDARD: 23 # std::format is only available with /std:c++latest at the moment. APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 + - GENERATOR: '"Visual Studio 17 2022" -A x64' + BUILD_TYPE: Release + BUILD_SHARED: 'ON' + WCHAR: 'ON' + WCHAR_FILES: 'ON' + BUILD_EXAMPLE: 'OFF' + USE_STD_FORMAT: 'ON' + CXX_STANDARD: 23 # std::format is only available with /std:c++latest at the moment. + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 build_script: - cmd: >- set From 2b4e07dd91fad1e80efb056943762a3b603daf9a Mon Sep 17 00:00:00 2001 From: gabime Date: Tue, 16 Nov 2021 23:42:06 +0200 Subject: [PATCH 039/121] Fixed wchar support for std::format --- include/spdlog/common.h | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 7987b085..fdfff8c1 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -17,7 +17,7 @@ #include #ifdef SPDLOG_USE_STD_FORMAT -#include +# include #endif #ifdef SPDLOG_COMPILED_LIB @@ -133,19 +133,18 @@ template using format_string_t = std::string_view; template -struct is_convertible_to_basic_format_string - : std::integral_constant>::value> +struct is_convertible_to_basic_format_string : std::integral_constant>::value> {}; # if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) using wstring_view_t = std::wstring_view; -using memory_buf_t = std::wstring; +using wmemory_buf_t = std::wstring; template using wformat_string_t = std::wstring_view; # endif -#else + +#else // use fmt lib instead of std::format namespace fmt_lib = fmt; using string_view_t = fmt::basic_string_view; @@ -167,7 +166,8 @@ struct is_convertible_to_basic_format_string # if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) using wstring_view_t = fmt::basic_string_view; -using wmemory_buf_t = fmt::basic_memory_buffer;; +using wmemory_buf_t = fmt::basic_memory_buffer; +; template using wformat_string_t = fmt::wformat_string; @@ -308,7 +308,12 @@ struct file_event_handlers std::function after_open; std::function before_close; std::function after_close; - file_event_handlers() : before_open{nullptr}, after_open{nullptr}, before_close{nullptr}, after_close{nullptr} {} + file_event_handlers() + : before_open{nullptr} + , after_open{nullptr} + , before_close{nullptr} + , after_close{nullptr} + {} }; namespace details { From dc030ec53ce9e4005156049ffb065edcca2fb2f7 Mon Sep 17 00:00:00 2001 From: gabime Date: Tue, 16 Nov 2021 23:44:35 +0200 Subject: [PATCH 040/121] clang-format --- bench/bench.cpp | 8 +- example/example.cpp | 2 +- include/spdlog/async.h | 6 +- include/spdlog/common.h | 2 +- include/spdlog/details/file_helper-inl.h | 2 +- include/spdlog/details/file_helper.h | 2 +- include/spdlog/details/fmt_helper.h | 10 +-- include/spdlog/details/os-inl.h | 6 +- include/spdlog/details/synchronous_factory.h | 2 +- include/spdlog/details/tcp_client-windows.h | 8 +- include/spdlog/details/udp_client-windows.h | 5 +- include/spdlog/details/udp_client.h | 8 +- include/spdlog/fmt/bin_to_hex.h | 2 +- include/spdlog/logger.h | 46 +++++------ include/spdlog/pattern_formatter.h | 2 +- include/spdlog/sinks/basic_file_sink-inl.h | 2 +- include/spdlog/sinks/basic_file_sink.h | 8 +- include/spdlog/sinks/daily_file_sink.h | 27 ++++--- include/spdlog/sinks/hourly_file_sink.h | 11 +-- include/spdlog/sinks/msvc_sink.h | 6 +- include/spdlog/sinks/qt_sinks.h | 81 ++++++++++--------- include/spdlog/sinks/rotating_file_sink-inl.h | 2 +- include/spdlog/sinks/rotating_file_sink.h | 17 ++-- include/spdlog/sinks/udp_sink.h | 2 +- include/spdlog/sinks/win_eventlog_sink.h | 4 +- include/spdlog/spdlog.h | 34 ++++---- include/spdlog/stopwatch.h | 2 +- tests/test_daily_logger.cpp | 34 ++++---- tests/test_pattern_formatter.cpp | 2 +- 29 files changed, 180 insertions(+), 163 deletions(-) diff --git a/bench/bench.cpp b/bench/bench.cpp index af08c7ac..8a46837a 100644 --- a/bench/bench.cpp +++ b/bench/bench.cpp @@ -160,8 +160,8 @@ void bench(int howmany, std::shared_ptr log) auto delta = high_resolution_clock::now() - start; auto delta_d = duration_cast>(delta).count(); - spdlog::info( - spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d))); + spdlog::info(spdlog::fmt_lib::format( + std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d))); spdlog::drop(log->name()); } @@ -191,8 +191,8 @@ void bench_mt(int howmany, std::shared_ptr log, size_t thread_co auto delta = high_resolution_clock::now() - start; auto delta_d = duration_cast>(delta).count(); - spdlog::info( - spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d))); + spdlog::info(spdlog::fmt_lib::format( + std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d))); spdlog::drop(log->name()); } diff --git a/example/example.cpp b/example/example.cpp index 93a7e0b4..6ba5c4c7 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -323,6 +323,6 @@ void file_events_example() }; handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); }; auto file_sink = std::make_shared("logs/events-sample.txt", true, handlers); - spdlog::logger my_logger("some_logger", file_sink); + spdlog::logger my_logger("some_logger", file_sink); my_logger.info("Some log line"); } diff --git a/include/spdlog/async.h b/include/spdlog/async.h index 6406822c..e54fedc6 100644 --- a/include/spdlog/async.h +++ b/include/spdlog/async.h @@ -35,7 +35,7 @@ template struct async_factory_impl { template - static std::shared_ptr create(std::string logger_name, SinkArgs &&...args) + static std::shared_ptr create(std::string logger_name, SinkArgs &&... args) { auto ®istry_inst = details::registry::instance(); @@ -61,13 +61,13 @@ using async_factory = async_factory_impl; using async_factory_nonblock = async_factory_impl; template -inline std::shared_ptr create_async(std::string logger_name, SinkArgs &&...sink_args) +inline std::shared_ptr create_async(std::string logger_name, SinkArgs &&... sink_args) { return async_factory::create(std::move(logger_name), std::forward(sink_args)...); } template -inline std::shared_ptr create_async_nb(std::string logger_name, SinkArgs &&...sink_args) +inline std::shared_ptr create_async_nb(std::string logger_name, SinkArgs &&... sink_args) { return async_factory_nonblock::create(std::move(logger_name), std::forward(sink_args)...); } diff --git a/include/spdlog/common.h b/include/spdlog/common.h index fdfff8c1..c7f7e46d 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -323,7 +323,7 @@ namespace details { using std::make_unique; #else template -std::unique_ptr make_unique(Args &&...args) +std::unique_ptr make_unique(Args &&... args) { static_assert(!std::is_array::value, "arrays not supported"); return std::unique_ptr(new T(std::forward(args)...)); diff --git a/include/spdlog/details/file_helper-inl.h b/include/spdlog/details/file_helper-inl.h index 63d511c6..8276633b 100644 --- a/include/spdlog/details/file_helper-inl.h +++ b/include/spdlog/details/file_helper-inl.h @@ -20,7 +20,7 @@ namespace spdlog { namespace details { -SPDLOG_INLINE file_helper::file_helper(const file_event_handlers& event_handlers) +SPDLOG_INLINE file_helper::file_helper(const file_event_handlers &event_handlers) : event_handlers_(event_handlers) {} diff --git a/include/spdlog/details/file_helper.h b/include/spdlog/details/file_helper.h index 69c5afdc..0f5988b9 100644 --- a/include/spdlog/details/file_helper.h +++ b/include/spdlog/details/file_helper.h @@ -17,7 +17,7 @@ class SPDLOG_API file_helper { public: file_helper() = default; - explicit file_helper(const file_event_handlers& event_handlers); + explicit file_helper(const file_event_handlers &event_handlers); file_helper(const file_helper &) = delete; file_helper &operator=(const file_helper &) = delete; diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h index cc59dd62..1a60bc0d 100644 --- a/include/spdlog/details/fmt_helper.h +++ b/include/spdlog/details/fmt_helper.h @@ -9,8 +9,8 @@ #include #ifdef SPDLOG_USE_STD_FORMAT -#include -#include +# include +# include #endif // Some fmt helpers to efficiently format and pad ints and strings @@ -89,11 +89,11 @@ inline unsigned int count_digits(T n) return static_cast(fmt:: // fmt 7.0.0 renamed the internal namespace to detail. // See: https://github.com/fmtlib/fmt/issues/1538 -#if FMT_VERSION < 70000 +# if FMT_VERSION < 70000 internal -#else +# else detail -#endif +# endif ::count_digits(static_cast(n))); #endif } diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index e4cddd41..0575d811 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -381,11 +381,11 @@ SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) { memory_buf_t buf; wstr_to_utf8buf(filename, buf); -#ifdef SPDLOG_USE_STD_FORMAT +# ifdef SPDLOG_USE_STD_FORMAT return buf; -#else +# else return fmt::to_string(buf); -#endif +# endif } #else SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) diff --git a/include/spdlog/details/synchronous_factory.h b/include/spdlog/details/synchronous_factory.h index e1e42268..1f729ab9 100644 --- a/include/spdlog/details/synchronous_factory.h +++ b/include/spdlog/details/synchronous_factory.h @@ -13,7 +13,7 @@ class logger; struct synchronous_factory { template - static std::shared_ptr create(std::string logger_name, SinkArgs &&...args) + static std::shared_ptr create(std::string logger_name, SinkArgs &&... args) { auto sink = std::make_shared(std::forward(args)...); auto new_logger = std::make_shared(std::move(logger_name), std::move(sink)); diff --git a/include/spdlog/details/tcp_client-windows.h b/include/spdlog/details/tcp_client-windows.h index b331a500..9d3647aa 100644 --- a/include/spdlog/details/tcp_client-windows.h +++ b/include/spdlog/details/tcp_client-windows.h @@ -24,7 +24,7 @@ namespace details { class tcp_client { SOCKET socket_ = INVALID_SOCKET; - + static void init_winsock_() { WSADATA wsaData; @@ -55,7 +55,6 @@ public: close(); ::WSACleanup(); } - bool is_connected() const { @@ -65,7 +64,7 @@ public: void close() { ::closesocket(socket_); - socket_ = INVALID_SOCKET; + socket_ = INVALID_SOCKET; } SOCKET fd() const @@ -73,10 +72,9 @@ public: return socket_; } - // try to connect or throw on failure void connect(const std::string &host, int port) - { + { if (is_connected()) { close(); diff --git a/include/spdlog/details/udp_client-windows.h b/include/spdlog/details/udp_client-windows.h index 372efa6d..d67c7258 100644 --- a/include/spdlog/details/udp_client-windows.h +++ b/include/spdlog/details/udp_client-windows.h @@ -23,7 +23,7 @@ namespace spdlog { namespace details { class udp_client { - static constexpr int TX_BUFFER_SIZE = 1024*10; + static constexpr int TX_BUFFER_SIZE = 1024 * 10; SOCKET socket_ = INVALID_SOCKET; sockaddr_in addr_ = {0}; @@ -64,7 +64,8 @@ public: addr_.sin_family = PF_INET; addr_.sin_port = htons(port); addr_.sin_addr.s_addr = INADDR_ANY; - if (InetPton(PF_INET, TEXT(host.c_str()), &addr_.sin_addr.s_addr) != 1) { + if (InetPton(PF_INET, TEXT(host.c_str()), &addr_.sin_addr.s_addr) != 1) + { int last_error = ::WSAGetLastError(); ::WSACleanup(); throw_winsock_error_("error: Invalid address!", last_error); diff --git a/include/spdlog/details/udp_client.h b/include/spdlog/details/udp_client.h index 7a8ee9cc..2db5b89e 100644 --- a/include/spdlog/details/udp_client.h +++ b/include/spdlog/details/udp_client.h @@ -31,7 +31,6 @@ class udp_client int socket_ = -1; struct sockaddr_in sockAddr_; - void cleanup_() { if (socket_ != -1) @@ -59,8 +58,9 @@ public: sockAddr_.sin_family = AF_INET; sockAddr_.sin_port = htons(port); - - if (::inet_aton(host.c_str(), &sockAddr_.sin_addr) == 0) { + + if (::inet_aton(host.c_str(), &sockAddr_.sin_addr) == 0) + { cleanup_(); throw_spdlog_ex("error: Invalid address!"); } @@ -85,7 +85,7 @@ public: ssize_t toslen = 0; socklen_t tolen = sizeof(struct sockaddr); if ((toslen = ::sendto(socket_, data, n_bytes, 0, (struct sockaddr *)&sockAddr_, tolen)) == -1) - { + { throw_spdlog_ex("sendto(2) failed", errno); } } diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h index 835e4f07..c6ed08d8 100644 --- a/include/spdlog/fmt/bin_to_hex.h +++ b/include/spdlog/fmt/bin_to_hex.h @@ -220,4 +220,4 @@ struct formatter, char> } } }; -} // namespace fmt/std +} // namespace std diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index 4ce41b84..a8abae41 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -31,9 +31,9 @@ # define SPDLOG_LOGGER_CATCH(location) \ catch (const std::exception &ex) \ { \ - if(location.filename) \ + if (location.filename) \ { \ - err_handler_(fmt_lib::format("{} [{}({})]", ex.what(), location.filename, location.line)); \ + err_handler_(fmt_lib::format("{} [{}({})]", ex.what(), location.filename, location.line)); \ } \ else \ { \ @@ -85,13 +85,13 @@ public: void swap(spdlog::logger &other) SPDLOG_NOEXCEPT; template - void log(source_loc loc, level::level_enum lvl, format_string_t fmt, Args &&...args) + void log(source_loc loc, level::level_enum lvl, format_string_t fmt, Args &&... args) { log_(loc, lvl, fmt, std::forward(args)...); } template - void log(level::level_enum lvl, format_string_t fmt, Args &&...args) + void log(level::level_enum lvl, format_string_t fmt, Args &&... args) { log(source_loc{}, lvl, fmt, std::forward(args)...); } @@ -141,50 +141,50 @@ public: } template - void trace(format_string_t fmt, Args &&...args) + void trace(format_string_t fmt, Args &&... args) { log(level::trace, fmt, std::forward(args)...); } template - void debug(format_string_t fmt, Args &&...args) + void debug(format_string_t fmt, Args &&... args) { log(level::debug, fmt, std::forward(args)...); } template - void info(format_string_t fmt, Args &&...args) + void info(format_string_t fmt, Args &&... args) { log(level::info, fmt, std::forward(args)...); } template - void warn(format_string_t fmt, Args &&...args) + void warn(format_string_t fmt, Args &&... args) { log(level::warn, fmt, std::forward(args)...); } template - void error(format_string_t fmt, Args &&...args) + void error(format_string_t fmt, Args &&... args) { log(level::err, fmt, std::forward(args)...); } template - void critical(format_string_t fmt, Args &&...args) + void critical(format_string_t fmt, Args &&... args) { log(level::critical, fmt, std::forward(args)...); } #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT template - void log(source_loc loc, level::level_enum lvl, wformat_string_t fmt, Args &&...args) + void log(source_loc loc, level::level_enum lvl, wformat_string_t fmt, Args &&... args) { log_(loc, lvl, fmt, std::forward(args)...); } template - void log(level::level_enum lvl, wformat_string_t fmt, Args &&...args) + void log(level::level_enum lvl, wformat_string_t fmt, Args &&... args) { log(source_loc{}, lvl, fmt, std::forward(args)...); } @@ -225,37 +225,37 @@ public: } template - void trace(wformat_string_t fmt, Args &&...args) + void trace(wformat_string_t fmt, Args &&... args) { log(level::trace, fmt, std::forward(args)...); } template - void debug(wformat_string_t fmt, Args &&...args) + void debug(wformat_string_t fmt, Args &&... args) { log(level::debug, fmt, std::forward(args)...); } template - void info(wformat_string_t fmt, Args &&...args) + void info(wformat_string_t fmt, Args &&... args) { log(level::info, fmt, std::forward(args)...); } template - void warn(wformat_string_t fmt, Args &&...args) + void warn(wformat_string_t fmt, Args &&... args) { log(level::warn, fmt, std::forward(args)...); } template - void error(wformat_string_t fmt, Args &&...args) + void error(wformat_string_t fmt, Args &&... args) { log(level::err, fmt, std::forward(args)...); } template - void critical(wformat_string_t fmt, Args &&...args) + void critical(wformat_string_t fmt, Args &&... args) { log(level::critical, fmt, std::forward(args)...); } @@ -353,7 +353,7 @@ protected: // common implementation for after templated public api has been resolved template - void log_(source_loc loc, level::level_enum lvl, string_view_t fmt, Args &&...args) + void log_(source_loc loc, level::level_enum lvl, string_view_t fmt, Args &&... args) { bool log_enabled = should_log(lvl); bool traceback_enabled = tracer_.enabled(); @@ -377,7 +377,7 @@ protected: #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT template - void log_(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args &&...args) + void log_(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args &&... args) { bool log_enabled = should_log(lvl); bool traceback_enabled = tracer_.enabled(); @@ -389,12 +389,12 @@ protected: { // format to wmemory_buffer and convert to utf8 ; -#ifdef SPDLOG_USE_STD_FORMAT +# ifdef SPDLOG_USE_STD_FORMAT wmemory_buf_t wbuf = std::vformat(fmt, std::make_wformat_args(std::forward(args)...)); -#else +# else wmemory_buf_t wbuf; fmt::detail::vformat_to(wbuf, fmt, fmt::make_format_args(std::forward(args)...)); -#endif +# endif memory_buf_t buf; details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf); details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size())); diff --git a/include/spdlog/pattern_formatter.h b/include/spdlog/pattern_formatter.h index e9ccfaa7..147d8546 100644 --- a/include/spdlog/pattern_formatter.h +++ b/include/spdlog/pattern_formatter.h @@ -92,7 +92,7 @@ public: void format(const details::log_msg &msg, memory_buf_t &dest) override; template - pattern_formatter &add_flag(char flag, Args &&...args) + pattern_formatter &add_flag(char flag, Args &&... args) { custom_handlers_[flag] = details::make_unique(std::forward(args)...); return *this; diff --git a/include/spdlog/sinks/basic_file_sink-inl.h b/include/spdlog/sinks/basic_file_sink-inl.h index af383214..8d23f96d 100644 --- a/include/spdlog/sinks/basic_file_sink-inl.h +++ b/include/spdlog/sinks/basic_file_sink-inl.h @@ -14,7 +14,7 @@ namespace spdlog { namespace sinks { template -SPDLOG_INLINE basic_file_sink::basic_file_sink(const filename_t &filename, bool truncate, const file_event_handlers& event_handlers) +SPDLOG_INLINE basic_file_sink::basic_file_sink(const filename_t &filename, bool truncate, const file_event_handlers &event_handlers) : file_helper_{event_handlers} { file_helper_.open(filename, truncate); diff --git a/include/spdlog/sinks/basic_file_sink.h b/include/spdlog/sinks/basic_file_sink.h index 12cd95ef..aacc993b 100644 --- a/include/spdlog/sinks/basic_file_sink.h +++ b/include/spdlog/sinks/basic_file_sink.h @@ -20,7 +20,7 @@ template class basic_file_sink final : public base_sink { public: - explicit basic_file_sink(const filename_t &filename, bool truncate = false, const file_event_handlers& event_handlers = {}); + explicit basic_file_sink(const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {}); const filename_t &filename() const; protected: @@ -40,13 +40,15 @@ using basic_file_sink_st = basic_file_sink; // factory functions // template -inline std::shared_ptr basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers& event_handlers = {}) +inline std::shared_ptr basic_logger_mt( + const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {}) { return Factory::template create(logger_name, filename, truncate, event_handlers); } template -inline std::shared_ptr basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers& event_handlers = {}) +inline std::shared_ptr basic_logger_st( + const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {}) { return Factory::template create(logger_name, filename, truncate, event_handlers); } diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index c0f896a1..bb566e18 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -72,7 +72,7 @@ struct daily_filename_format_calculator } buf.resize(buf.size() * 2); } - + return buf; #else // generate fmt datetime format string, e.g. {:%Y-%m-%d}. @@ -116,7 +116,8 @@ class daily_file_sink final : public base_sink { public: // create daily file sink which rotates on given time - daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) + daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0, + const file_event_handlers &event_handlers = {}) : base_filename_(std::move(base_filename)) , rotation_h_(rotation_hour) , rotation_m_(rotation_minute) @@ -262,30 +263,32 @@ using daily_file_format_sink_st = daily_file_sink -inline std::shared_ptr daily_logger_mt( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) +inline std::shared_ptr daily_logger_mt(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, + bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {}) { return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); } template -inline std::shared_ptr daily_logger_format_mt( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) +inline std::shared_ptr daily_logger_format_mt(const std::string &logger_name, const filename_t &filename, int hour = 0, + int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {}) { - return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); + return Factory::template create( + logger_name, filename, hour, minute, truncate, max_files, event_handlers); } template -inline std::shared_ptr daily_logger_st( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) +inline std::shared_ptr daily_logger_st(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, + bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {}) { return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); } template -inline std::shared_ptr daily_logger_format_st( - const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) +inline std::shared_ptr daily_logger_format_st(const std::string &logger_name, const filename_t &filename, int hour = 0, + int minute = 0, bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {}) { - return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, event_handlers); + return Factory::template create( + logger_name, filename, hour, minute, truncate, max_files, event_handlers); } } // namespace spdlog diff --git a/include/spdlog/sinks/hourly_file_sink.h b/include/spdlog/sinks/hourly_file_sink.h index 7a7c715c..029e7b1d 100644 --- a/include/spdlog/sinks/hourly_file_sink.h +++ b/include/spdlog/sinks/hourly_file_sink.h @@ -46,7 +46,8 @@ class hourly_file_sink final : public base_sink { public: // create hourly file sink which rotates on given time - hourly_file_sink(filename_t base_filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) + hourly_file_sink( + filename_t base_filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {}) : base_filename_(std::move(base_filename)) , file_helper_{event_handlers} , truncate_(truncate) @@ -180,15 +181,15 @@ using hourly_file_sink_st = hourly_file_sink; // factory functions // template -inline std::shared_ptr hourly_logger_mt( - const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) +inline std::shared_ptr hourly_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false, + uint16_t max_files = 0, const file_event_handlers &event_handlers = {}) { return Factory::template create(logger_name, filename, truncate, max_files, event_handlers); } template -inline std::shared_ptr hourly_logger_st( - const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0, const file_event_handlers& event_handlers = {}) +inline std::shared_ptr hourly_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false, + uint16_t max_files = 0, const file_event_handlers &event_handlers = {}) { return Factory::template create(logger_name, filename, truncate, max_files, event_handlers); } diff --git a/include/spdlog/sinks/msvc_sink.h b/include/spdlog/sinks/msvc_sink.h index 76ec40a0..cf2516d7 100644 --- a/include/spdlog/sinks/msvc_sink.h +++ b/include/spdlog/sinks/msvc_sink.h @@ -30,11 +30,11 @@ protected: { memory_buf_t formatted; base_sink::formatter_->format(msg, formatted); -#ifdef SPDLOG_USE_STD_FORMAT +# ifdef SPDLOG_USE_STD_FORMAT OutputDebugStringA(formatted.c_str()); -#else +# else OutputDebugStringA(fmt::to_string(formatted).c_str()); -#endif +# endif } void flush_() override {} diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index 99545665..31b49c60 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -21,29 +21,36 @@ // namespace spdlog { namespace sinks { -template class qt_sink : public base_sink { +template +class qt_sink : public base_sink +{ public: - qt_sink(QObject *qt_object, const std::string &meta_method) { - qt_object_ = qt_object; - meta_method_ = meta_method; - } + qt_sink(QObject *qt_object, const std::string &meta_method) + { + qt_object_ = qt_object; + meta_method_ = meta_method; + } - ~qt_sink() { flush_(); } + ~qt_sink() + { + flush_(); + } protected: - void sink_it_(const details::log_msg &msg) override { - memory_buf_t formatted; - base_sink::formatter_->format(msg, formatted); - string_view_t str = string_view_t(formatted.data(), formatted.size()); - QMetaObject::invokeMethod(qt_object_, meta_method_.c_str(), Qt::AutoConnection, - Q_ARG(QString, QString::fromUtf8(str.data(), static_cast(str.size())).trimmed())); - } + void sink_it_(const details::log_msg &msg) override + { + memory_buf_t formatted; + base_sink::formatter_->format(msg, formatted); + string_view_t str = string_view_t(formatted.data(), formatted.size()); + QMetaObject::invokeMethod(qt_object_, meta_method_.c_str(), Qt::AutoConnection, + Q_ARG(QString, QString::fromUtf8(str.data(), static_cast(str.size())).trimmed())); + } - void flush_() override {} + void flush_() override {} private: - QObject *qt_object_ = nullptr; - std::string meta_method_; + QObject *qt_object_ = nullptr; + std::string meta_method_; }; #include "spdlog/details/null_mutex.h" @@ -55,39 +62,41 @@ using qt_sink_st = qt_sink; // // Factory functions // -template -inline std::shared_ptr -qt_logger_mt(const std::string &logger_name, QTextEdit* qt_object, const std::string &meta_method = "append") { - return Factory::template create(logger_name, qt_object, meta_method); +template +inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append") +{ + return Factory::template create(logger_name, qt_object, meta_method); } -template -inline std::shared_ptr -qt_logger_st(const std::string &logger_name, QTextEdit* qt_object, const std::string &meta_method = "append") { - return Factory::template create(logger_name, qt_object, meta_method); +template +inline std::shared_ptr qt_logger_st(const std::string &logger_name, QTextEdit *qt_object, const std::string &meta_method = "append") +{ + return Factory::template create(logger_name, qt_object, meta_method); } -template -inline std::shared_ptr -qt_logger_mt(const std::string &logger_name, QPlainTextEdit* qt_object , const std::string &meta_method = "appendPlainText") { +template +inline std::shared_ptr qt_logger_mt( + const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText") +{ return Factory::template create(logger_name, qt_object, meta_method); } -template -inline std::shared_ptr -qt_logger_st(const std::string &logger_name, QPlainTextEdit* qt_object, const std::string &meta_method = "appendPlainText") { +template +inline std::shared_ptr qt_logger_st( + const std::string &logger_name, QPlainTextEdit *qt_object, const std::string &meta_method = "appendPlainText") +{ return Factory::template create(logger_name, qt_object, meta_method); } -template -inline std::shared_ptr -qt_logger_mt(const std::string &logger_name, QObject* qt_object, const std::string &meta_method) { +template +inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QObject *qt_object, const std::string &meta_method) +{ return Factory::template create(logger_name, qt_object, meta_method); } -template -inline std::shared_ptr -qt_logger_st(const std::string &logger_name, QObject* qt_object, const std::string &meta_method) { +template +inline std::shared_ptr qt_logger_st(const std::string &logger_name, QObject *qt_object, const std::string &meta_method) +{ return Factory::template create(logger_name, qt_object, meta_method); } } // namespace spdlog diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h index 8555d0d2..2c003959 100644 --- a/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -25,7 +25,7 @@ namespace sinks { template SPDLOG_INLINE rotating_file_sink::rotating_file_sink( - filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open, const file_event_handlers& event_handlers) + filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open, const file_event_handlers &event_handlers) : base_filename_(std::move(base_filename)) , max_size_(max_size) , max_files_(max_files) diff --git a/include/spdlog/sinks/rotating_file_sink.h b/include/spdlog/sinks/rotating_file_sink.h index d34ec02e..ce0d7b1e 100644 --- a/include/spdlog/sinks/rotating_file_sink.h +++ b/include/spdlog/sinks/rotating_file_sink.h @@ -22,7 +22,8 @@ template class rotating_file_sink final : public base_sink { public: - rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false, const file_event_handlers& event_handlers = {}); + rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false, + const file_event_handlers &event_handlers = {}); static filename_t calc_filename(const filename_t &filename, std::size_t index); filename_t filename(); @@ -59,17 +60,19 @@ using rotating_file_sink_st = rotating_file_sink; // template -inline std::shared_ptr rotating_logger_mt( - const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false, const file_event_handlers& event_handlers = {}) +inline std::shared_ptr rotating_logger_mt(const std::string &logger_name, const filename_t &filename, size_t max_file_size, + size_t max_files, bool rotate_on_open = false, const file_event_handlers &event_handlers = {}) { - return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers); + return Factory::template create( + logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers); } template -inline std::shared_ptr rotating_logger_st( - const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false, const file_event_handlers& event_handlers = {}) +inline std::shared_ptr rotating_logger_st(const std::string &logger_name, const filename_t &filename, size_t max_file_size, + size_t max_files, bool rotate_on_open = false, const file_event_handlers &event_handlers = {}) { - return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers); + return Factory::template create( + logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers); } } // namespace spdlog diff --git a/include/spdlog/sinks/udp_sink.h b/include/spdlog/sinks/udp_sink.h index 934f6686..ccbce2be 100644 --- a/include/spdlog/sinks/udp_sink.h +++ b/include/spdlog/sinks/udp_sink.h @@ -53,7 +53,7 @@ protected: client_.send(formatted.data(), formatted.size()); } - void flush_() override {} + void flush_() override {} details::udp_client client_; }; diff --git a/include/spdlog/sinks/win_eventlog_sink.h b/include/spdlog/sinks/win_eventlog_sink.h index 202c79f8..8a978a09 100644 --- a/include/spdlog/sinks/win_eventlog_sink.h +++ b/include/spdlog/sinks/win_eventlog_sink.h @@ -53,8 +53,8 @@ struct local_alloc_t SPDLOG_CONSTEXPR local_alloc_t() SPDLOG_NOEXCEPT : hlocal_(nullptr) {} - local_alloc_t(local_alloc_t const&) = delete; - local_alloc_t& operator=(local_alloc_t const&) = delete; + local_alloc_t(local_alloc_t const &) = delete; + local_alloc_t &operator=(local_alloc_t const &) = delete; ~local_alloc_t() SPDLOG_NOEXCEPT { diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index 327a0ebe..65d3e9d5 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -31,7 +31,7 @@ using default_factory = synchronous_factory; // Example: // spdlog::create("logger_name", "dailylog_filename", 11, 59); template -inline std::shared_ptr create(std::string logger_name, SinkArgs &&...sink_args) +inline std::shared_ptr create(std::string logger_name, SinkArgs &&... sink_args) { return default_factory::create(std::move(logger_name), std::forward(sink_args)...); } @@ -128,49 +128,49 @@ SPDLOG_API spdlog::logger *default_logger_raw(); SPDLOG_API void set_default_logger(std::shared_ptr default_logger); template -inline void log(source_loc source, level::level_enum lvl, format_string_t fmt, Args &&...args) +inline void log(source_loc source, level::level_enum lvl, format_string_t fmt, Args &&... args) { default_logger_raw()->log(source, lvl, fmt, std::forward(args)...); } template -inline void log(level::level_enum lvl, format_string_t fmt, Args &&...args) +inline void log(level::level_enum lvl, format_string_t fmt, Args &&... args) { default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward(args)...); } template -inline void trace(format_string_t fmt, Args &&...args) +inline void trace(format_string_t fmt, Args &&... args) { default_logger_raw()->trace(fmt, std::forward(args)...); } template -inline void debug(format_string_t fmt, Args &&...args) +inline void debug(format_string_t fmt, Args &&... args) { default_logger_raw()->debug(fmt, std::forward(args)...); } template -inline void info(format_string_t fmt, Args &&...args) +inline void info(format_string_t fmt, Args &&... args) { default_logger_raw()->info(fmt, std::forward(args)...); } template -inline void warn(format_string_t fmt, Args &&...args) +inline void warn(format_string_t fmt, Args &&... args) { default_logger_raw()->warn(fmt, std::forward(args)...); } template -inline void error(format_string_t fmt, Args &&...args) +inline void error(format_string_t fmt, Args &&... args) { default_logger_raw()->error(fmt, std::forward(args)...); } template -inline void critical(format_string_t fmt, Args &&...args) +inline void critical(format_string_t fmt, Args &&... args) { default_logger_raw()->critical(fmt, std::forward(args)...); } @@ -189,49 +189,49 @@ inline void log(level::level_enum lvl, const T &msg) #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT template -inline void log(source_loc source, level::level_enum lvl, wformat_string_t fmt, Args &&...args) +inline void log(source_loc source, level::level_enum lvl, wformat_string_t fmt, Args &&... args) { default_logger_raw()->log(source, lvl, fmt, std::forward(args)...); } template -inline void log(level::level_enum lvl, wformat_string_t fmt, Args &&...args) +inline void log(level::level_enum lvl, wformat_string_t fmt, Args &&... args) { default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward(args)...); } template -inline void trace(wformat_string_t fmt, Args &&...args) +inline void trace(wformat_string_t fmt, Args &&... args) { default_logger_raw()->trace(fmt, std::forward(args)...); } template -inline void debug(wformat_string_t fmt, Args &&...args) +inline void debug(wformat_string_t fmt, Args &&... args) { default_logger_raw()->debug(fmt, std::forward(args)...); } template -inline void info(wformat_string_t fmt, Args &&...args) +inline void info(wformat_string_t fmt, Args &&... args) { default_logger_raw()->info(fmt, std::forward(args)...); } template -inline void warn(wformat_string_t fmt, Args &&...args) +inline void warn(wformat_string_t fmt, Args &&... args) { default_logger_raw()->warn(fmt, std::forward(args)...); } template -inline void error(wformat_string_t fmt, Args &&...args) +inline void error(wformat_string_t fmt, Args &&... args) { default_logger_raw()->error(fmt, std::forward(args)...); } template -inline void critical(wformat_string_t fmt, Args &&...args) +inline void critical(wformat_string_t fmt, Args &&... args) { default_logger_raw()->critical(fmt, std::forward(args)...); } diff --git a/include/spdlog/stopwatch.h b/include/spdlog/stopwatch.h index a1542b2d..4e62259b 100644 --- a/include/spdlog/stopwatch.h +++ b/include/spdlog/stopwatch.h @@ -65,4 +65,4 @@ struct formatter : formatter return formatter::format(sw.elapsed().count(), ctx); } }; -} // namespace fmt/std +} // namespace std diff --git a/tests/test_daily_logger.cpp b/tests/test_daily_logger.cpp index 66f01a46..09f7c0e5 100644 --- a/tests/test_daily_logger.cpp +++ b/tests/test_daily_logger.cpp @@ -33,17 +33,17 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]") #ifdef SPDLOG_WCHAR_FILENAMES spdlog::memory_buf_t buf; spdlog::details::os::wstr_to_utf8buf(fmt::to_string(w), buf); -#ifdef SPDLOG_USE_STD_FORMAT +# ifdef SPDLOG_USE_STD_FORMAT auto &filename = buf; -#else +# else auto filename = fmt::to_string(buf); -#endif +# endif #else -#ifdef SPDLOG_USE_STD_FORMAT +# ifdef SPDLOG_USE_STD_FORMAT auto &filename = w; -#else +# else auto filename = fmt::to_string(w); -#endif +# endif #endif require_message_count(filename, 10); } @@ -53,8 +53,8 @@ struct custom_daily_file_name_calculator static spdlog::filename_t calc_filename(const spdlog::filename_t &basename, const tm &now_tm) { filename_memory_buf_t w; - spdlog::fmt_lib::format_to(std::back_inserter(w), SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, - now_tm.tm_mday); + spdlog::fmt_lib::format_to(std::back_inserter(w), SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename, now_tm.tm_year + 1900, + now_tm.tm_mon + 1, now_tm.tm_mday); #ifdef SPDLOG_USE_STD_FORMAT return w; #else @@ -87,17 +87,17 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]") #ifdef SPDLOG_WCHAR_FILENAMES spdlog::memory_buf_t buf; spdlog::details::os::wstr_to_utf8buf(fmt::to_string(w), buf); -#ifdef SPDLOG_USE_STD_FORMAT +# ifdef SPDLOG_USE_STD_FORMAT auto &filename = buf; -#else +# else auto filename = fmt::to_string(buf); -#endif +# endif #else -#ifdef SPDLOG_USE_STD_FORMAT +# ifdef SPDLOG_USE_STD_FORMAT auto &filename = w; -#else +# else auto filename = fmt::to_string(w); -#endif +# endif #endif require_message_count(filename, 10); } @@ -146,10 +146,10 @@ TEST_CASE("daily_file_sink::daily_filename_format_calculator", "[daily_file_sink { std::tm tm = spdlog::details::os::localtime(); // example-YYYY-MM-DD.log - auto filename = - spdlog::sinks::daily_filename_format_calculator::calc_filename(SPDLOG_FILENAME_T("example-%Y-%m-%d.log"), tm); + auto filename = spdlog::sinks::daily_filename_format_calculator::calc_filename(SPDLOG_FILENAME_T("example-%Y-%m-%d.log"), tm); - REQUIRE(filename == spdlog::fmt_lib::format(SPDLOG_FILENAME_T("example-{:04d}-{:02d}-{:02d}.log"), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday)); + REQUIRE(filename == + spdlog::fmt_lib::format(SPDLOG_FILENAME_T("example-{:04d}-{:02d}-{:02d}.log"), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday)); } /* Test removal of old files */ diff --git a/tests/test_pattern_formatter.cpp b/tests/test_pattern_formatter.cpp index 78c4cd30..02a1bc2b 100644 --- a/tests/test_pattern_formatter.cpp +++ b/tests/test_pattern_formatter.cpp @@ -5,7 +5,7 @@ using spdlog::memory_buf_t; // log to str and return it template -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; auto oss_sink = std::make_shared(oss); From 8bd5f4f8833ec5a45834db2cddaeb8f5f5ffec6c Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Wed, 17 Nov 2021 01:04:27 +0200 Subject: [PATCH 041/121] Update test_daily_logger.cpp --- tests/test_daily_logger.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_daily_logger.cpp b/tests/test_daily_logger.cpp index 09f7c0e5..01b75d81 100644 --- a/tests/test_daily_logger.cpp +++ b/tests/test_daily_logger.cpp @@ -32,10 +32,11 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]") #ifdef SPDLOG_WCHAR_FILENAMES spdlog::memory_buf_t buf; - spdlog::details::os::wstr_to_utf8buf(fmt::to_string(w), buf); # ifdef SPDLOG_USE_STD_FORMAT + spdlog::details::os::wstr_to_utf8buf(w, buf); auto &filename = buf; # else + spdlog::details::os::wstr_to_utf8buf(fmt::to_string(w), buf); auto filename = fmt::to_string(buf); # endif #else @@ -199,4 +200,4 @@ TEST_CASE("daily_logger rotate", "[daily_file_sink]") test_rotate(days_to_run, 10, 10); test_rotate(days_to_run, 11, 10); test_rotate(days_to_run, 20, 10); -} \ No newline at end of file +} From ca1eaedf7bc14fc0c0b86758d81171314103db7e Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Wed, 17 Nov 2021 04:45:49 +0200 Subject: [PATCH 042/121] Update test_daily_logger.cpp --- tests/test_daily_logger.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_daily_logger.cpp b/tests/test_daily_logger.cpp index 01b75d81..cf93c46e 100644 --- a/tests/test_daily_logger.cpp +++ b/tests/test_daily_logger.cpp @@ -87,10 +87,11 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]") #ifdef SPDLOG_WCHAR_FILENAMES spdlog::memory_buf_t buf; - spdlog::details::os::wstr_to_utf8buf(fmt::to_string(w), buf); # ifdef SPDLOG_USE_STD_FORMAT + spdlog::details::os::wstr_to_utf8buf(w, buf); auto &filename = buf; # else + spdlog::details::os::wstr_to_utf8buf(fmt::to_string(w), buf); auto filename = fmt::to_string(buf); # endif #else From 8d469770600ba16f87d91737b14581e857d9cef3 Mon Sep 17 00:00:00 2001 From: Shen-Ta Hsieh Date: Fri, 19 Nov 2021 09:58:29 +0800 Subject: [PATCH 043/121] Avoid c-style casting --- include/spdlog/sinks/stdout_sinks-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/sinks/stdout_sinks-inl.h b/include/spdlog/sinks/stdout_sinks-inl.h index fd5594fe..756734bf 100644 --- a/include/spdlog/sinks/stdout_sinks-inl.h +++ b/include/spdlog/sinks/stdout_sinks-inl.h @@ -37,7 +37,7 @@ SPDLOG_INLINE stdout_sink_base::stdout_sink_base(FILE *file) #ifdef _WIN32 // get windows handle from the FILE* object - handle_ = (HANDLE)::_get_osfhandle(::_fileno(file_)); + handle_ = reinterpret_cast(::_get_osfhandle(::_fileno(file_))); // don't throw to support cases where no console is attached, // and let the log method to do nothing if (handle_ == INVALID_HANDLE_VALUE). From aac187d3a04daa4a80a0de912783a40a52addefb Mon Sep 17 00:00:00 2001 From: lisr Date: Fri, 19 Nov 2021 10:55:43 +0800 Subject: [PATCH 044/121] fix aix compile error --- include/spdlog/details/os-inl.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index 0575d811..b32eb85b 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -230,8 +230,8 @@ SPDLOG_INLINE size_t filesize(FILE *f) # endif #else // unix -// OpenBSD doesn't compile with :: before the fileno(..) -# if defined(__OpenBSD__) +// OpenBSD and AIX doesn't compile with :: before the fileno(..) +# if defined(__OpenBSD__) || defined(_AIX) int fd = fileno(f); # else int fd = ::fileno(f); @@ -336,7 +336,9 @@ SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT # define SYS_gettid __NR_gettid # endif return static_cast(::syscall(SYS_gettid)); -#elif defined(_AIX) || defined(__DragonFly__) || defined(__FreeBSD__) +#elif defined(_AIX) + return static_cast(::pthread_self()); +#elif defined(__DragonFly__) || defined(__FreeBSD__) return static_cast(::pthread_getthreadid_np()); #elif defined(__NetBSD__) return static_cast(::_lwp_self()); From 17f21df4413885b4a7f4e7ef9a0155958fe28ac8 Mon Sep 17 00:00:00 2001 From: Light <59169246+Light3039@users.noreply.github.com> Date: Fri, 19 Nov 2021 09:30:22 +0330 Subject: [PATCH 045/121] Fix(tweakme): SPDLOG_FUNCTION - Uncommenting SPDLOG_FUNCTION will make MSVC fail to compile: __PRETTY_FUNCTION__ is shown in intellisense but it's not available at compile time https://stackoverflow.com/questions/48857887/pretty-function-in-visual-c --- include/spdlog/tweakme.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/spdlog/tweakme.h b/include/spdlog/tweakme.h index 08f0f4e5..25113f0f 100644 --- a/include/spdlog/tweakme.h +++ b/include/spdlog/tweakme.h @@ -127,5 +127,9 @@ // __PRETTY_FUNCTION__ might be nicer in clang/gcc, and __FUNCTION__ in msvc. // Defaults to __FUNCTION__ (should work on all compilers) if not defined. // -// #define SPDLOG_FUNCTION __PRETTY_FUNCTION__ +// #ifdef __PRETTY_FUNCTION__ +// # define SPDLOG_FUNCTION __PRETTY_FUN CTION__ +// #else +// # define SPDLOG_FUNCTION __FUNCTION__ +// #endif /////////////////////////////////////////////////////////////////////////////// From 29b41741cb86d791b3cdc9a0d303ce6fdc756cd9 Mon Sep 17 00:00:00 2001 From: Light <59169246+Light3039@users.noreply.github.com> Date: Fri, 19 Nov 2021 09:32:59 +0330 Subject: [PATCH 046/121] Fix(tweakme): Typo :( --- include/spdlog/tweakme.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/tweakme.h b/include/spdlog/tweakme.h index 25113f0f..ab557051 100644 --- a/include/spdlog/tweakme.h +++ b/include/spdlog/tweakme.h @@ -128,7 +128,7 @@ // Defaults to __FUNCTION__ (should work on all compilers) if not defined. // // #ifdef __PRETTY_FUNCTION__ -// # define SPDLOG_FUNCTION __PRETTY_FUN CTION__ +// # define SPDLOG_FUNCTION __PRETTY_FUNCTION__ // #else // # define SPDLOG_FUNCTION __FUNCTION__ // #endif From 232df72b82c8645b06a36d71496062d550603ba7 Mon Sep 17 00:00:00 2001 From: lisr Date: Sat, 20 Nov 2021 09:48:14 +0800 Subject: [PATCH 047/121] use pthread_getthrds_np for AIX --- include/spdlog/details/os-inl.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index b32eb85b..a7cd37f7 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -46,7 +46,7 @@ # include //Use gettid() syscall under linux to get thread id # elif defined(_AIX) -# include // for pthread_getthreadid_np +# include // for pthread_getthrds_np # elif defined(__DragonFly__) || defined(__FreeBSD__) # include // for pthread_getthreadid_np @@ -337,7 +337,12 @@ SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT # endif return static_cast(::syscall(SYS_gettid)); #elif defined(_AIX) - return static_cast(::pthread_self()); + struct __pthrdsinfo buf; + int reg_size = 0; + pthread_t pt = pthread_self(); + int retval = pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_TID, &buf, sizeof(buf), NULL, ®_size); + int tid = (!retval) ? buf.__pi_tid : 0; + return static_cast(tid); #elif defined(__DragonFly__) || defined(__FreeBSD__) return static_cast(::pthread_getthreadid_np()); #elif defined(__NetBSD__) From 569b851b8093ca33c7966b732c817b6e948b015d Mon Sep 17 00:00:00 2001 From: lisr Date: Sat, 20 Nov 2021 22:48:18 +0800 Subject: [PATCH 048/121] aix - reference to 'thread' is ambiguous, sys/thread.h vs std::thread --- bench/async_bench.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench/async_bench.cpp b/bench/async_bench.cpp index ce2c9281..cf4d9754 100644 --- a/bench/async_bench.cpp +++ b/bench/async_bench.cpp @@ -162,7 +162,7 @@ void thread_fun(std::shared_ptr logger, int howmany) void bench_mt(int howmany, std::shared_ptr logger, int thread_count) { using std::chrono::high_resolution_clock; - vector threads; + vector threads; auto start = high_resolution_clock::now(); int msgs_per_thread = howmany / thread_count; From 58e2b455fb99e00d263f1ceb0c18a5ac6a0d1578 Mon Sep 17 00:00:00 2001 From: LE GARREC Vincent Date: Thu, 25 Nov 2021 00:16:28 +0100 Subject: [PATCH 049/121] Fix build with "-fvisibility=hidden" --- include/spdlog/common.h | 16 ++++++++++------ include/spdlog/sinks/base_sink.h | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/spdlog/common.h b/include/spdlog/common.h index c7f7e46d..9239ab1a 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -22,13 +22,17 @@ #ifdef SPDLOG_COMPILED_LIB # undef SPDLOG_HEADER_ONLY -# if defined(_WIN32) && defined(SPDLOG_SHARED_LIB) -# ifdef spdlog_EXPORTS -# define SPDLOG_API __declspec(dllexport) -# else -# define SPDLOG_API __declspec(dllimport) +# if defined(SPDLOG_SHARED_LIB) +# if defined(_WIN32) +# ifdef spdlog_EXPORTS +# define SPDLOG_API __declspec(dllexport) +# else // !spdlog_EXPORTS +# define SPDLOG_API __declspec(dllimport) +# endif +# else // !defined(_WIN32) +# define SPDLOG_API __attribute__((visibility ("default"))) # endif -# else // !defined(_WIN32) || !defined(SPDLOG_SHARED_LIB) +# else // !defined(SPDLOG_SHARED_LIB) # define SPDLOG_API # endif # define SPDLOG_INLINE diff --git a/include/spdlog/sinks/base_sink.h b/include/spdlog/sinks/base_sink.h index 21c5545f..2e795f59 100644 --- a/include/spdlog/sinks/base_sink.h +++ b/include/spdlog/sinks/base_sink.h @@ -16,7 +16,7 @@ namespace spdlog { namespace sinks { template -class base_sink : public sink +class SPDLOG_API base_sink : public sink { public: base_sink(); From d5c000394df28e9c0cce4cb76329fb97ec27ec99 Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Wed, 24 Nov 2021 19:25:25 -0500 Subject: [PATCH 050/121] Remove extraneous semicolon --- include/spdlog/common.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/spdlog/common.h b/include/spdlog/common.h index c7f7e46d..ded59d58 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -167,7 +167,6 @@ struct is_convertible_to_basic_format_string # if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) using wstring_view_t = fmt::basic_string_view; using wmemory_buf_t = fmt::basic_memory_buffer; -; template using wformat_string_t = fmt::wformat_string; From e1a4b28039a9097b970e0e9a500c31f9838b034b Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sat, 27 Nov 2021 19:35:35 +0200 Subject: [PATCH 051/121] Added fmt license file to bundled fmt folder --- include/spdlog/fmt/bundled/fmt.license.rst | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 include/spdlog/fmt/bundled/fmt.license.rst diff --git a/include/spdlog/fmt/bundled/fmt.license.rst b/include/spdlog/fmt/bundled/fmt.license.rst new file mode 100644 index 00000000..f0ec3db4 --- /dev/null +++ b/include/spdlog/fmt/bundled/fmt.license.rst @@ -0,0 +1,27 @@ +Copyright (c) 2012 - present, Victor Zverovich + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--- Optional exception to the license --- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into a machine-executable object form of such +source code, you may redistribute such embedded portions in such object form +without including the above copyright and permission notices. From f52d526e1ee2bad54d0234dc6c8c4855b321d796 Mon Sep 17 00:00:00 2001 From: Sean Farrell Date: Sun, 28 Nov 2021 13:55:14 +0100 Subject: [PATCH 052/121] Add example to replace default logger. Close #2193 --- README.md | 14 ++++++++++++++ example/example.cpp | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/README.md b/README.md index d1f228c2..0ad56df5 100644 --- a/README.md +++ b/README.md @@ -391,6 +391,20 @@ void file_events_example() } ``` +--- +#### Replace the Default Logger +```c++ +void replace_default_logger_example() +{ + auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true); + spdlog::set_default_logger(new_logger); + spdlog::set_level(spdlog::level::info); + spdlog::debug("This message should not be displayed!"); + spdlog::set_level(spdlog::level::trace); + spdlog::debug("This message should be displayed.."); +} +``` + --- ## Benchmarks diff --git a/example/example.cpp b/example/example.cpp index 6ba5c4c7..3e33e284 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -23,6 +23,7 @@ void syslog_example(); void udp_example(); void custom_flags_example(); void file_events_example(); +void replace_default_logger_example(); #include "spdlog/spdlog.h" #include "spdlog/cfg/env.h" // support for loading levels from the environment variable @@ -80,6 +81,7 @@ int main(int, char *[]) udp_example(); custom_flags_example(); file_events_example(); + replace_default_logger_example(); // Flush all *registered* loggers using a worker thread every 3 seconds. // note: registered loggers *must* be thread safe for this to work correctly! @@ -326,3 +328,19 @@ void file_events_example() spdlog::logger my_logger("some_logger", file_sink); my_logger.info("Some log line"); } + +void replace_default_logger_example() +{ + // store the old logger so we don't break other examples. + auto old_logger = spdlog::default_logger(); + + auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true); + spdlog::set_default_logger(new_logger); + spdlog::set_level(spdlog::level::info); + spdlog::debug("This message should not be displayed!"); + spdlog::set_level(spdlog::level::trace); + spdlog::debug("This message should be displayed.."); + + spdlog::set_default_logger(old_logger); +} + From c15262c49359fd0f9f9814d429d9bab20d6e37fe Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Wed, 1 Dec 2021 03:29:46 +0200 Subject: [PATCH 053/121] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ad56df5..0b5bbcdc 100644 --- a/README.md +++ b/README.md @@ -377,7 +377,7 @@ $ ./example --- #### Log file open/close event handlers ```c++ -// You can get subscribe to get callbacks from spdlog before/after log file has been opened or closed. +// You can get callbacks from spdlog before/after log file has been opened or closed. // This is useful for cleanup procedures or for adding someting the start/end of the log files. void file_events_example() { From 8a6b5b9e6263e1b74e1a48624b730f3ee8245cf7 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Wed, 1 Dec 2021 03:32:08 +0200 Subject: [PATCH 054/121] Update README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 0b5bbcdc..9b797b79 100644 --- a/README.md +++ b/README.md @@ -398,10 +398,8 @@ void replace_default_logger_example() { auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true); spdlog::set_default_logger(new_logger); - spdlog::set_level(spdlog::level::info); - spdlog::debug("This message should not be displayed!"); spdlog::set_level(spdlog::level::trace); - spdlog::debug("This message should be displayed.."); + spdlog::trace("This message should be displayed.."); } ``` From cabbe65be461b95576ca66cdbcb23de1e6de9944 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Wed, 1 Dec 2021 03:33:26 +0200 Subject: [PATCH 055/121] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b797b79..a77fac23 100644 --- a/README.md +++ b/README.md @@ -399,7 +399,7 @@ void replace_default_logger_example() auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true); spdlog::set_default_logger(new_logger); spdlog::set_level(spdlog::level::trace); - spdlog::trace("This message should be displayed.."); + spdlog::trace("This message should appear.."); } ``` From d93cea97ec9d1b3a26486c7530dbaaba59852aed Mon Sep 17 00:00:00 2001 From: "Roocks Patrick (MTN PTT / External)" Date: Wed, 1 Dec 2021 15:37:48 +0100 Subject: [PATCH 056/121] Fix usage of ranges and to_hex in the same compile unit When trying to use spdlog/fmt/bin_to_hex.h in the same compile unit as spdlog/fmt/bundled/ranges.h you got a compile error because there was a multiple definitions for iterable classes. This fix renames the begin() and end() getters in dump_info into getBegin()/getEnd() in order to avoid this collision. Added an example of ranges in example.cpp to show that it actually works (an to_hex example was already there) --- example/example.cpp | 11 +++++++++++ include/spdlog/fmt/bin_to_hex.h | 21 +++++++++++---------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/example/example.cpp b/example/example.cpp index 3e33e284..87b00518 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -14,6 +14,7 @@ void rotating_example(); void daily_example(); void async_example(); void binary_example(); +void vector_example(); void stopwatch_example(); void trace_example(); void multi_sink_example(); @@ -73,6 +74,7 @@ int main(int, char *[]) daily_example(); async_example(); binary_example(); + vector_example(); multi_sink_example(); user_defined_example(); err_handler_example(); @@ -188,6 +190,15 @@ void binary_example() // logger->info("hexdump style, 20 chars per line {:a}", spdlog::to_hex(buf, 20)); } +// Log a vector of numbers + +#include "spdlog/fmt/bundled/ranges.h" +void vector_example() +{ + std::vector vec = {1, 2, 3}; + spdlog::info("Vector example: {}", vec); +} + // Compile time log levels. // define SPDLOG_ACTIVE_LEVEL to required level (e.g. SPDLOG_LEVEL_TRACE) void trace_example() diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h index c6ed08d8..26785d81 100644 --- a/include/spdlog/fmt/bin_to_hex.h +++ b/include/spdlog/fmt/bin_to_hex.h @@ -39,11 +39,12 @@ public: , size_per_line_(size_per_line) {} - It begin() const + // do not use begin() and end() to avoid collision with fmt/ranges + It getBegin() const { return begin_; } - It end() const + It getEnd() const { return end_; } @@ -144,14 +145,14 @@ struct formatter, char> #endif int size_per_line = static_cast(the_range.size_per_line()); - auto start_of_line = the_range.begin(); - for (auto i = the_range.begin(); i != the_range.end(); i++) + auto start_of_line = the_range.getBegin(); + for (auto i = the_range.getBegin(); i != the_range.getEnd(); i++) { auto ch = static_cast(*i); - if (put_newlines && (i == the_range.begin() || i - start_of_line >= size_per_line)) + if (put_newlines && (i == the_range.getBegin() || i - start_of_line >= size_per_line)) { - if (show_ascii && i != the_range.begin()) + if (show_ascii && i != the_range.getBegin()) { *inserter++ = delimiter; *inserter++ = delimiter; @@ -162,7 +163,7 @@ struct formatter, char> } } - put_newline(inserter, static_cast(i - the_range.begin())); + put_newline(inserter, static_cast(i - the_range.getBegin())); // put first byte without delimiter in front of it *inserter++ = hex_chars[(ch >> 4) & 0x0f]; @@ -181,9 +182,9 @@ struct formatter, char> } if (show_ascii) // add ascii to last line { - if (the_range.end() - the_range.begin() > size_per_line) + if (the_range.getEnd() - the_range.getBegin() > size_per_line) { - auto blank_num = size_per_line - (the_range.end() - start_of_line); + auto blank_num = size_per_line - (the_range.getEnd() - start_of_line); while (blank_num-- > 0) { *inserter++ = delimiter; @@ -196,7 +197,7 @@ struct formatter, char> } *inserter++ = delimiter; *inserter++ = delimiter; - for (auto j = start_of_line; j != the_range.end(); j++) + for (auto j = start_of_line; j != the_range.getEnd(); j++) { auto pc = static_cast(*j); *inserter++ = std::isprint(pc) ? static_cast(*j) : '.'; From f304ca3dafac5e3135c5d1323b49a9693d1a9c3e Mon Sep 17 00:00:00 2001 From: "Roocks Patrick (MTN PTT / External)" Date: Wed, 1 Dec 2021 16:37:29 +0100 Subject: [PATCH 057/121] code style fixes --- include/spdlog/fmt/bin_to_hex.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h index 26785d81..14908310 100644 --- a/include/spdlog/fmt/bin_to_hex.h +++ b/include/spdlog/fmt/bin_to_hex.h @@ -40,11 +40,11 @@ public: {} // do not use begin() and end() to avoid collision with fmt/ranges - It getBegin() const + It get_begin() const { return begin_; } - It getEnd() const + It get_end() const { return end_; } @@ -145,14 +145,14 @@ struct formatter, char> #endif int size_per_line = static_cast(the_range.size_per_line()); - auto start_of_line = the_range.getBegin(); - for (auto i = the_range.getBegin(); i != the_range.getEnd(); i++) + auto start_of_line = the_range.get_begin(); + for (auto i = the_range.get_begin(); i != the_range.get_end(); i++) { auto ch = static_cast(*i); - if (put_newlines && (i == the_range.getBegin() || i - start_of_line >= size_per_line)) + if (put_newlines && (i == the_range.get_begin() || i - start_of_line >= size_per_line)) { - if (show_ascii && i != the_range.getBegin()) + if (show_ascii && i != the_range.get_begin()) { *inserter++ = delimiter; *inserter++ = delimiter; @@ -163,7 +163,7 @@ struct formatter, char> } } - put_newline(inserter, static_cast(i - the_range.getBegin())); + put_newline(inserter, static_cast(i - the_range.get_begin())); // put first byte without delimiter in front of it *inserter++ = hex_chars[(ch >> 4) & 0x0f]; @@ -182,9 +182,9 @@ struct formatter, char> } if (show_ascii) // add ascii to last line { - if (the_range.getEnd() - the_range.getBegin() > size_per_line) + if (the_range.get_end() - the_range.get_begin() > size_per_line) { - auto blank_num = size_per_line - (the_range.getEnd() - start_of_line); + auto blank_num = size_per_line - (the_range.get_end() - start_of_line); while (blank_num-- > 0) { *inserter++ = delimiter; @@ -197,7 +197,7 @@ struct formatter, char> } *inserter++ = delimiter; *inserter++ = delimiter; - for (auto j = start_of_line; j != the_range.getEnd(); j++) + for (auto j = start_of_line; j != the_range.get_end(); j++) { auto pc = static_cast(*j); *inserter++ = std::isprint(pc) ? static_cast(*j) : '.'; From e3e4c4bc950ebeda4479c177509079825450af43 Mon Sep 17 00:00:00 2001 From: semenov_gv Date: Mon, 18 Oct 2021 19:06:05 +0300 Subject: [PATCH 058/121] minor changes added const ref params --- include/spdlog/details/os-inl.h | 4 ++-- include/spdlog/details/os.h | 4 ++-- include/spdlog/pattern_formatter.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index a7cd37f7..5f082e57 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -542,7 +542,7 @@ static SPDLOG_INLINE bool mkdir_(const filename_t &path) // create the given directory - and all directories leading to it // return true on success or if the directory already exists -SPDLOG_INLINE bool create_dir(filename_t path) +SPDLOG_INLINE bool create_dir(const filename_t &path) { if (path_exists(path)) { @@ -581,7 +581,7 @@ SPDLOG_INLINE bool create_dir(filename_t path) // "abc/" => "abc" // "abc" => "" // "abc///" => "abc//" -SPDLOG_INLINE filename_t dir_name(filename_t path) +SPDLOG_INLINE filename_t dir_name(const filename_t &path) { auto pos = path.find_last_of(folder_seps_filename); return pos != filename_t::npos ? path.substr(0, pos) : filename_t{}; diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h index 2b2ffa1d..b154bc47 100644 --- a/include/spdlog/details/os.h +++ b/include/spdlog/details/os.h @@ -99,11 +99,11 @@ SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target); // "abc/" => "abc" // "abc" => "" // "abc///" => "abc//" -SPDLOG_API filename_t dir_name(filename_t path); +SPDLOG_API filename_t dir_name(const filename_t &path); // Create a dir from the given path. // Return true if succeeded or if this dir already exists. -SPDLOG_API bool create_dir(filename_t path); +SPDLOG_API bool create_dir(const filename_t &path); // non thread safe, cross platform getenv/getenv_s // return empty string if field not found diff --git a/include/spdlog/pattern_formatter.h b/include/spdlog/pattern_formatter.h index 147d8546..6810f03b 100644 --- a/include/spdlog/pattern_formatter.h +++ b/include/spdlog/pattern_formatter.h @@ -68,7 +68,7 @@ class SPDLOG_API custom_flag_formatter : public details::flag_formatter public: virtual std::unique_ptr clone() const = 0; - void set_padding_info(details::padding_info padding) + void set_padding_info(const details::padding_info& padding) { flag_formatter::padinfo_ = padding; } From 5bf8728cfaf9b3b7da67821dbea9cbb3438c0de2 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 4 Dec 2021 14:32:01 +0200 Subject: [PATCH 059/121] Fixed example for std_format --- example/example.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/example/example.cpp b/example/example.cpp index 87b00518..71119758 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -191,14 +191,20 @@ void binary_example() } // Log a vector of numbers - -#include "spdlog/fmt/bundled/ranges.h" +#ifndef SPDLOG_USE_STD_FORMAT +# include "spdlog/fmt/bundled/ranges.h" void vector_example() { std::vector vec = {1, 2, 3}; spdlog::info("Vector example: {}", vec); } +#else +void vector_example() {} +#endif + +// ! DSPDLOG_USE_STD_FORMAT + // Compile time log levels. // define SPDLOG_ACTIVE_LEVEL to required level (e.g. SPDLOG_LEVEL_TRACE) void trace_example() @@ -252,10 +258,14 @@ void multi_sink_example() struct my_type { int i; - template - friend OStream &operator<<(OStream &os, const my_type &c) +}; + +template<> +struct std::formatter : std::formatter +{ + auto format(my_type my, format_context &ctx) { - return os << "[my_type i=" << c.i << "]"; + return formatter::format(std::format("[my_type i={}]", my.i), ctx); } }; @@ -354,4 +364,3 @@ void replace_default_logger_example() spdlog::set_default_logger(old_logger); } - From 2a45eff693d70680f74c2ca7bb1da6bdf53d6126 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 4 Dec 2021 14:42:12 +0200 Subject: [PATCH 060/121] Fixed example for custom_type --- example/example.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/example.cpp b/example/example.cpp index 71119758..f38416ad 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -261,11 +261,11 @@ struct my_type }; template<> -struct std::formatter : std::formatter +struct spdlog::fmt_lib::formatter : spdlog::fmt_lib::formatter { auto format(my_type my, format_context &ctx) { - return formatter::format(std::format("[my_type i={}]", my.i), ctx); + return formatter::format(spdlog::fmt_lib::format("[my_type i={}]", my.i), ctx); } }; From 2c21d9ecf83ea0b9360d635235e1446b43858298 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 4 Dec 2021 14:42:34 +0200 Subject: [PATCH 061/121] Fixed example for custom_type --- example/example.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/example.cpp b/example/example.cpp index f38416ad..20f1d6cb 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -257,7 +257,7 @@ void multi_sink_example() // User defined types logging by implementing operator<< struct my_type { - int i; + int i = 0; }; template<> From b8b16e49a5c2ab21015fc1536c1adaa6debc784d Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 4 Dec 2021 14:44:31 +0200 Subject: [PATCH 062/121] Fixed example for custom_type --- example/example.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/example/example.cpp b/example/example.cpp index 20f1d6cb..5e5f85d3 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -260,12 +260,13 @@ struct my_type int i = 0; }; +namespace fmt_lib = spdlog::fmt_lib; template<> -struct spdlog::fmt_lib::formatter : spdlog::fmt_lib::formatter +struct fmt_lib::formatter : fmt_lib::formatter { auto format(my_type my, format_context &ctx) { - return formatter::format(spdlog::fmt_lib::format("[my_type i={}]", my.i), ctx); + return formatter::format(fmt_lib::format("[my_type i={}]", my.i), ctx); } }; From f81970191aad8f7dc9c810ce544a92301a612a88 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 4 Dec 2021 14:53:16 +0200 Subject: [PATCH 063/121] Fixed example for custom_type --- example/example.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/example/example.cpp b/example/example.cpp index 5e5f85d3..0d0db6b8 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -254,7 +254,7 @@ void multi_sink_example() logger.info("this message should not appear in the console, only in the file"); } -// User defined types logging by implementing operator<< +// User defined types logging struct my_type { int i = 0; @@ -262,11 +262,12 @@ struct my_type namespace fmt_lib = spdlog::fmt_lib; template<> -struct fmt_lib::formatter : fmt_lib::formatter +struct fmt_lib::formatter : fmt_lib::formatter { auto format(my_type my, format_context &ctx) - { - return formatter::format(fmt_lib::format("[my_type i={}]", my.i), ctx); + { + auto &&out = ctx.out(); + return fmt_lib::format_to(out, "[my_type i={}]", my.i); } }; From 8dd012096a76026af2c74c5b8eb010bab0f44ba0 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sat, 4 Dec 2021 16:38:08 +0200 Subject: [PATCH 064/121] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a77fac23..920df2bc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # spdlog -Very fast, header-only/compiled, C++ logging library. [![Build Status](https://app.travis-ci.com/gabime/spdlog.svg?branch=v1.x)](https://app.travis-ci.com/gabime/spdlog)  [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog) [![Release](https://img.shields.io/github/release/gabime/spdlog.svg)](https://github.com/gabime/spdlog/releases/latest) +Very fast, header-only/compiled, C++ logging library. [![Build Status](https://app.travis-ci.com/gabime/spdlog.svg?branch=v1.x)](https://app.travis-ci.com/gabime/spdlog)  [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true&branch=v1.x)](https://ci.appveyor.com/project/gabime/spdlog) [![Release](https://img.shields.io/github/release/gabime/spdlog.svg)](https://github.com/gabime/spdlog/releases/latest) ## Install #### Header only version From 1f585359207eddefd7db8a0bf240f2ef61ac548b Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Mon, 6 Dec 2021 13:37:32 +0200 Subject: [PATCH 065/121] Fixed test_macros tests --- tests/test_macros.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/tests/test_macros.cpp b/tests/test_macros.cpp index b4ad5654..c9af60f3 100644 --- a/tests/test_macros.cpp +++ b/tests/test_macros.cpp @@ -27,7 +27,8 @@ TEST_CASE("debug and trace w/o format string", "[macros]]") using spdlog::details::os::default_eol; REQUIRE(ends_with(file_contents(TEST_FILENAME), spdlog::fmt_lib::format("Test message 2{}", default_eol))); REQUIRE(count_lines(TEST_FILENAME) == 1); - + + auto orig_default_logger = spdlog::default_logger(); spdlog::set_default_logger(logger); SPDLOG_TRACE("Test message 3"); @@ -36,6 +37,7 @@ TEST_CASE("debug and trace w/o format string", "[macros]]") require_message_count(TEST_FILENAME, 2); REQUIRE(ends_with(file_contents(TEST_FILENAME), spdlog::fmt_lib::format("Test message 4{}", default_eol))); + spdlog::set_default_logger(std::move(orig_default_logger)); } TEST_CASE("disable param evaluation", "[macros]") @@ -51,12 +53,3 @@ TEST_CASE("pass logger pointer", "[macros]") SPDLOG_LOGGER_DEBUG(&ref, "Test message 2"); } -// ensure that even if right macro level is on- don't evaluate if the logger's level is not high enough -// TEST_CASE("disable param evaluation2", "[macros]") -//{ -// auto logger = std::make_shared("test-macro"); -// logger->set_level(spdlog::level::off); -// int x = 0; -// SPDLOG_LOGGER_DEBUG(logger, "Test message {}", ++x); -// REQUIRE(x == 0); -//} From 9e17fafe1bdbfbe5b870cbab6c23f3a65f56d965 Mon Sep 17 00:00:00 2001 From: Acretock Date: Wed, 8 Dec 2021 17:31:24 +0500 Subject: [PATCH 066/121] c style cast -> static_cast --- include/spdlog/details/os-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index 5f082e57..16f3d2e9 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -305,7 +305,7 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) ((local_year / 100 >> 2) - (gmt_year / 100 >> 2)) // + difference in years * 365 */ - + (long int)(local_year - gmt_year) * 365); + + static_cast(local_year - gmt_year) * 365); long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour); long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min); From 6636ff05e61eece1cb8ef44ecdf70dab6d4db950 Mon Sep 17 00:00:00 2001 From: Philippe Serreault Date: Wed, 8 Dec 2021 15:10:21 +0100 Subject: [PATCH 067/121] Allow custom callback to be executed by thread-pool's threads before joining them. This is similar to a change that was made a while ago ( https://github.com/gabime/spdlog/pull/208 ). --- include/spdlog/details/thread_pool-inl.h | 11 ++++++++--- include/spdlog/details/thread_pool.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/include/spdlog/details/thread_pool-inl.h b/include/spdlog/details/thread_pool-inl.h index deee3766..c99af52d 100644 --- a/include/spdlog/details/thread_pool-inl.h +++ b/include/spdlog/details/thread_pool-inl.h @@ -13,7 +13,7 @@ namespace spdlog { namespace details { -SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start) +SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start, std::function on_thread_stop) : q_(q_max_items) { if (threads_n == 0 || threads_n > 1000) @@ -23,15 +23,20 @@ SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std } for (size_t i = 0; i < threads_n; i++) { - threads_.emplace_back([this, on_thread_start] { + threads_.emplace_back([this, on_thread_start, on_thread_stop] { on_thread_start(); this->thread_pool::worker_loop_(); + on_thread_stop(); }); } } +SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start) + : thread_pool(q_max_items, threads_n, on_thread_start, [] {}) +{} + SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n) - : thread_pool(q_max_items, threads_n, [] {}) + : thread_pool(q_max_items, threads_n, [] {}, [] {}) {} // message all threads to terminate gracefully join them diff --git a/include/spdlog/details/thread_pool.h b/include/spdlog/details/thread_pool.h index 86187e40..0687c650 100644 --- a/include/spdlog/details/thread_pool.h +++ b/include/spdlog/details/thread_pool.h @@ -84,6 +84,7 @@ public: using item_type = async_msg; using q_type = details::mpmc_blocking_queue; + thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start, std::function on_thread_stop); thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start); thread_pool(size_t q_max_items, size_t threads_n); From fda2b361dadcf6842c1c5bc5c63df457c6950892 Mon Sep 17 00:00:00 2001 From: Philippe Serreault Date: Wed, 8 Dec 2021 16:33:56 +0100 Subject: [PATCH 068/121] Added missing global thread-pool initialization helper. --- include/spdlog/async.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/include/spdlog/async.h b/include/spdlog/async.h index e54fedc6..281af697 100644 --- a/include/spdlog/async.h +++ b/include/spdlog/async.h @@ -73,16 +73,20 @@ inline std::shared_ptr create_async_nb(std::string logger_name, } // set global thread pool. -inline void init_thread_pool(size_t q_size, size_t thread_count, std::function on_thread_start) +inline void init_thread_pool(size_t q_size, size_t thread_count, std::function on_thread_start, std::function on_thread_stop) { - auto tp = std::make_shared(q_size, thread_count, on_thread_start); + auto tp = std::make_shared(q_size, thread_count, on_thread_start, on_thread_stop); details::registry::instance().set_tp(std::move(tp)); } -// set global thread pool. +inline void init_thread_pool(size_t q_size, size_t thread_count, std::function on_thread_start) +{ + init_thread_pool(q_size, thread_count, on_thread_start, [] {}); +} + inline void init_thread_pool(size_t q_size, size_t thread_count) { - init_thread_pool(q_size, thread_count, [] {}); + init_thread_pool(q_size, thread_count, [] {}, [] {}); } // get the global thread pool. From b5d6c939fd823018a6930455185bdae37b77c9e4 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Thu, 9 Dec 2021 08:28:21 +0200 Subject: [PATCH 069/121] Update thread_pool.h --- include/spdlog/details/thread_pool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/details/thread_pool.h b/include/spdlog/details/thread_pool.h index 0687c650..ccda0030 100644 --- a/include/spdlog/details/thread_pool.h +++ b/include/spdlog/details/thread_pool.h @@ -88,7 +88,7 @@ public: thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start); thread_pool(size_t q_max_items, size_t threads_n); - // message all threads to terminate gracefully join them + // message all threads to terminate and gracefully join them ~thread_pool(); thread_pool(const thread_pool &) = delete; From da9c16278a9b838b7f1f3ffe6bc4f00985af4ff5 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Thu, 9 Dec 2021 08:29:12 +0200 Subject: [PATCH 070/121] Update thread_pool.h --- include/spdlog/details/thread_pool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/details/thread_pool.h b/include/spdlog/details/thread_pool.h index ccda0030..26c0964e 100644 --- a/include/spdlog/details/thread_pool.h +++ b/include/spdlog/details/thread_pool.h @@ -88,7 +88,7 @@ public: thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start); thread_pool(size_t q_max_items, size_t threads_n); - // message all threads to terminate and gracefully join them + // message all threads to terminate and join them ~thread_pool(); thread_pool(const thread_pool &) = delete; From ab2e72340a03aa514b975f2255004e00b123b4ac Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Thu, 9 Dec 2021 08:30:43 +0200 Subject: [PATCH 071/121] Update thread_pool.h --- include/spdlog/details/thread_pool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/details/thread_pool.h b/include/spdlog/details/thread_pool.h index 26c0964e..63738b5b 100644 --- a/include/spdlog/details/thread_pool.h +++ b/include/spdlog/details/thread_pool.h @@ -88,7 +88,7 @@ public: thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start); thread_pool(size_t q_max_items, size_t threads_n); - // message all threads to terminate and join them + // message all threads to terminate gracefully and join them ~thread_pool(); thread_pool(const thread_pool &) = delete; From 28e415fb3e8641a245ab4485c5b087c46549fc05 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Thu, 9 Dec 2021 21:51:16 +0200 Subject: [PATCH 072/121] Update to google benchmark to v1.6.0 --- bench/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index 5283f83d..489802d9 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -18,8 +18,7 @@ if(NOT benchmark_FOUND) 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_Declare(googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.6.0) FetchContent_MakeAvailable(googlebenchmark) else() message(FATAL_ERROR "GoogleBenchmark is missing. Use CMake >= 3.11 or download it") From ac6908a1399b00759624aba7c92b7cfe6588fbe7 Mon Sep 17 00:00:00 2001 From: gabime Date: Thu, 9 Dec 2021 22:35:16 +0200 Subject: [PATCH 073/121] Update bench CMakelists.txt --- bench/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index 489802d9..144f50b8 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -16,7 +16,9 @@ if(NOT benchmark_FOUND) # User can fetch googlebenchmark message(STATUS "Downloading GoogleBenchmark") include(FetchContent) - set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE INTERNAL "") + + # disable tests + set(BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "") # Do not build and run googlebenchmark tests FetchContent_Declare(googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.6.0) FetchContent_MakeAvailable(googlebenchmark) From 16bc6d04adbf1da95f45062317b00f2515ac495b Mon Sep 17 00:00:00 2001 From: gabime Date: Thu, 9 Dec 2021 23:43:49 +0200 Subject: [PATCH 074/121] Added file event handlers test --- tests/test_file_helper.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/test_file_helper.cpp b/tests/test_file_helper.cpp index 168c6601..883445b7 100644 --- a/tests/test_file_helper.cpp +++ b/tests/test_file_helper.cpp @@ -99,3 +99,34 @@ TEST_CASE("file_helper_split_by_extension", "[file_helper::split_by_extension()] test_split_ext(SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T("")); test_split_ext(SPDLOG_FILENAME_T("..txt"), SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T(".txt")); } + +TEST_CASE("file_event_handlers", "[file_helper]") +{ + prepare_logdir(); + std::vector flags; + spdlog::file_event_handlers handlers; + handlers.before_open = [&](spdlog::filename_t filename) { + REQUIRE(filename == TEST_FILENAME); + flags.push_back(0); + }; + handlers.after_open = [&](spdlog::filename_t filename, std::FILE *fstream) { + REQUIRE(filename == TEST_FILENAME); + REQUIRE(fstream); + flags.push_back(1); + }; + handlers.before_close = [&](spdlog::filename_t filename, std::FILE *fstream) { + REQUIRE(filename == TEST_FILENAME); + REQUIRE(fstream); + flags.push_back(2); + }; + handlers.after_close = [&](spdlog::filename_t filename) { + REQUIRE(filename == TEST_FILENAME); + flags.push_back(3); + }; + spdlog::details::file_helper helper{handlers}; + REQUIRE(flags.empty()); + helper.open(TEST_FILENAME); + REQUIRE(flags == std::vector{0, 1}); + helper.close(); + REQUIRE(flags == std::vector{0, 1, 2, 3}); +} From afdcfc710ebb50ae8351c33a113b14f96191b2e5 Mon Sep 17 00:00:00 2001 From: gabime Date: Fri, 10 Dec 2021 00:12:20 +0200 Subject: [PATCH 075/121] Updated file_event_handlers tests --- tests/test_file_helper.cpp | 52 ++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/tests/test_file_helper.cpp b/tests/test_file_helper.cpp index 883445b7..b4791b03 100644 --- a/tests/test_file_helper.cpp +++ b/tests/test_file_helper.cpp @@ -102,31 +102,55 @@ TEST_CASE("file_helper_split_by_extension", "[file_helper::split_by_extension()] TEST_CASE("file_event_handlers", "[file_helper]") { + enum class flags + { + before_open, + after_open, + before_close, + after_close + }; prepare_logdir(); - std::vector flags; + + // define event handles that update vector of flags when called + std::vector events; spdlog::file_event_handlers handlers; handlers.before_open = [&](spdlog::filename_t filename) { REQUIRE(filename == TEST_FILENAME); - flags.push_back(0); + events.push_back(flags::before_open); }; - handlers.after_open = [&](spdlog::filename_t filename, std::FILE *fstream) { + handlers.after_open = [&](spdlog::filename_t filename, std::FILE *fstream) { REQUIRE(filename == TEST_FILENAME); REQUIRE(fstream); - flags.push_back(1); + events.push_back(flags::after_open); }; - handlers.before_close = [&](spdlog::filename_t filename, std::FILE *fstream) { + handlers.before_close = [&](spdlog::filename_t filename, std::FILE *fstream) { REQUIRE(filename == TEST_FILENAME); REQUIRE(fstream); - flags.push_back(2); + events.push_back(flags::before_close); }; handlers.after_close = [&](spdlog::filename_t filename) { - REQUIRE(filename == TEST_FILENAME); - flags.push_back(3); + REQUIRE(filename == TEST_FILENAME); + events.push_back(flags::after_close); }; - spdlog::details::file_helper helper{handlers}; - REQUIRE(flags.empty()); - helper.open(TEST_FILENAME); - REQUIRE(flags == std::vector{0, 1}); - helper.close(); - REQUIRE(flags == std::vector{0, 1, 2, 3}); + { + spdlog::details::file_helper helper{handlers}; + REQUIRE(events.empty()); + + helper.open(TEST_FILENAME); + REQUIRE(events == std::vector{flags::before_open, flags::after_open}); + + events.clear(); + helper.reopen(true); + REQUIRE(events == std::vector{flags::before_close, flags::after_close, flags::before_open, flags::after_open}); + + events.clear(); + helper.close(); + REQUIRE(events == std::vector{flags::before_close, flags::after_close}); + + helper.reopen(true); + events.clear(); + } + // make sure that the file_helper destrcutor calls the close callbacks if needed + REQUIRE(events == std::vector{flags::before_close, flags::after_close}); + } From 37cbab363eb8fd967f8952894db86d02fb018972 Mon Sep 17 00:00:00 2001 From: gabime Date: Fri, 10 Dec 2021 00:28:25 +0200 Subject: [PATCH 076/121] updated file_event_handlers tests --- tests/test_file_helper.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/test_file_helper.cpp b/tests/test_file_helper.cpp index b4791b03..861f6210 100644 --- a/tests/test_file_helper.cpp +++ b/tests/test_file_helper.cpp @@ -121,11 +121,14 @@ TEST_CASE("file_event_handlers", "[file_helper]") handlers.after_open = [&](spdlog::filename_t filename, std::FILE *fstream) { REQUIRE(filename == TEST_FILENAME); REQUIRE(fstream); + fputs("after_open\n", fstream); events.push_back(flags::after_open); }; handlers.before_close = [&](spdlog::filename_t filename, std::FILE *fstream) { REQUIRE(filename == TEST_FILENAME); REQUIRE(fstream); + fputs("before_close\n", fstream); + fflush(fstream); events.push_back(flags::before_close); }; handlers.after_close = [&](spdlog::filename_t filename) { @@ -138,19 +141,17 @@ TEST_CASE("file_event_handlers", "[file_helper]") helper.open(TEST_FILENAME); REQUIRE(events == std::vector{flags::before_open, flags::after_open}); - - events.clear(); - helper.reopen(true); - REQUIRE(events == std::vector{flags::before_close, flags::after_close, flags::before_open, flags::after_open}); - + events.clear(); helper.close(); REQUIRE(events == std::vector{flags::before_close, flags::after_close}); + REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n"); helper.reopen(true); events.clear(); } // make sure that the file_helper destrcutor calls the close callbacks if needed REQUIRE(events == std::vector{flags::before_close, flags::after_close}); + REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n"); } From 8715f51c61c5f13a26b4b3145f295bc4c15a3e76 Mon Sep 17 00:00:00 2001 From: gabime Date: Fri, 10 Dec 2021 18:31:32 +0200 Subject: [PATCH 077/121] Fixed file_event_handlers test for windows --- tests/test_file_helper.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/test_file_helper.cpp b/tests/test_file_helper.cpp index 861f6210..a27b442f 100644 --- a/tests/test_file_helper.cpp +++ b/tests/test_file_helper.cpp @@ -110,48 +110,48 @@ TEST_CASE("file_event_handlers", "[file_helper]") after_close }; prepare_logdir(); - + + spdlog::filename_t test_filename{TEST_FILENAME}; // define event handles that update vector of flags when called std::vector events; spdlog::file_event_handlers handlers; handlers.before_open = [&](spdlog::filename_t filename) { - REQUIRE(filename == TEST_FILENAME); + REQUIRE(filename == test_filename); events.push_back(flags::before_open); }; - handlers.after_open = [&](spdlog::filename_t filename, std::FILE *fstream) { - REQUIRE(filename == TEST_FILENAME); + handlers.after_open = [&](spdlog::filename_t filename, std::FILE* fstream) { + REQUIRE(filename == test_filename); REQUIRE(fstream); fputs("after_open\n", fstream); events.push_back(flags::after_open); }; - handlers.before_close = [&](spdlog::filename_t filename, std::FILE *fstream) { - REQUIRE(filename == TEST_FILENAME); + handlers.before_close = [&](spdlog::filename_t filename, std::FILE* fstream) { + REQUIRE(filename == test_filename); REQUIRE(fstream); fputs("before_close\n", fstream); - fflush(fstream); events.push_back(flags::before_close); }; handlers.after_close = [&](spdlog::filename_t filename) { - REQUIRE(filename == TEST_FILENAME); + REQUIRE(filename == test_filename); events.push_back(flags::after_close); }; { spdlog::details::file_helper helper{handlers}; REQUIRE(events.empty()); - helper.open(TEST_FILENAME); + helper.open(test_filename); REQUIRE(events == std::vector{flags::before_open, flags::after_open}); events.clear(); helper.close(); REQUIRE(events == std::vector{flags::before_close, flags::after_close}); - REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n"); + REQUIRE(file_contents(test_filename) == "after_open\nbefore_close\n"); helper.reopen(true); events.clear(); } // make sure that the file_helper destrcutor calls the close callbacks if needed REQUIRE(events == std::vector{flags::before_close, flags::after_close}); - REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n"); + REQUIRE(file_contents(test_filename) == "after_open\nbefore_close\n"); } From 9abcf38b902100b45fd3c60c815965671b3c3981 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Fri, 10 Dec 2021 22:31:08 +0200 Subject: [PATCH 078/121] Update test_file_helper.cpp --- tests/test_file_helper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_file_helper.cpp b/tests/test_file_helper.cpp index a27b442f..c4487a1a 100644 --- a/tests/test_file_helper.cpp +++ b/tests/test_file_helper.cpp @@ -145,13 +145,13 @@ TEST_CASE("file_event_handlers", "[file_helper]") events.clear(); helper.close(); REQUIRE(events == std::vector{flags::before_close, flags::after_close}); - REQUIRE(file_contents(test_filename) == "after_open\nbefore_close\n"); + REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n"); helper.reopen(true); events.clear(); } // make sure that the file_helper destrcutor calls the close callbacks if needed REQUIRE(events == std::vector{flags::before_close, flags::after_close}); - REQUIRE(file_contents(test_filename) == "after_open\nbefore_close\n"); + REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n"); } From 378a42c8870653ac34d2c1e6c51f46b457603c38 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Fri, 10 Dec 2021 22:53:50 +0200 Subject: [PATCH 079/121] Update test_file_helper.cpp --- tests/test_file_helper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_file_helper.cpp b/tests/test_file_helper.cpp index c4487a1a..4973e64c 100644 --- a/tests/test_file_helper.cpp +++ b/tests/test_file_helper.cpp @@ -111,7 +111,7 @@ TEST_CASE("file_event_handlers", "[file_helper]") }; prepare_logdir(); - spdlog::filename_t test_filename{TEST_FILENAME}; + spdlog::filename_t test_filename = TEST_FILENAME; // define event handles that update vector of flags when called std::vector events; spdlog::file_event_handlers handlers; From 6638c23cfc06da5c5bbfa4dfd112df7fe7a62a6d Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Fri, 10 Dec 2021 23:25:47 +0200 Subject: [PATCH 080/121] Update test_async.cpp --- tests/test_async.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_async.cpp b/tests/test_async.cpp index 124f3132..5265bca4 100644 --- a/tests/test_async.cpp +++ b/tests/test_async.cpp @@ -51,7 +51,7 @@ TEST_CASE("discard policy using factory ", "[async]") auto logger = spdlog::create_async_nb("as2"); auto test_sink = std::static_pointer_cast(logger->sinks()[0]); - test_sink->set_delay(std::chrono::milliseconds(1)); + test_sink->set_delay(std::chrono::milliseconds(3)); for (size_t i = 0; i < messages; i++) { From ad08f13aacc92b5e38c4a77f9cabd24d54fd53b1 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sat, 11 Dec 2021 00:38:41 +0200 Subject: [PATCH 081/121] Update test_file_helper.cpp --- tests/test_file_helper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_file_helper.cpp b/tests/test_file_helper.cpp index 4973e64c..01fa48ef 100644 --- a/tests/test_file_helper.cpp +++ b/tests/test_file_helper.cpp @@ -111,7 +111,7 @@ TEST_CASE("file_event_handlers", "[file_helper]") }; prepare_logdir(); - spdlog::filename_t test_filename = TEST_FILENAME; + spdlog::filename_t test_filename = SPDLOG_FILENAME_T(TEST_FILENAME); // define event handles that update vector of flags when called std::vector events; spdlog::file_event_handlers handlers; From 4fefd51e08d18042ff5ee9f42efd6e8a5ed5084c Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sat, 11 Dec 2021 17:07:10 +0200 Subject: [PATCH 082/121] Fixed custom type example to work in c++11 --- example/example.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/example/example.cpp b/example/example.cpp index 0d0db6b8..54212374 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -262,12 +262,11 @@ struct my_type namespace fmt_lib = spdlog::fmt_lib; template<> -struct fmt_lib::formatter : fmt_lib::formatter +struct fmt_lib::formatter : fmt_lib::formatter { - auto format(my_type my, format_context &ctx) - { - auto &&out = ctx.out(); - return fmt_lib::format_to(out, "[my_type i={}]", my.i); + auto format(my_type my, format_context &ctx) -> decltype(ctx.out()) + { + return fmt_lib::format_to(ctx.out(), "[my_type i={}]", my.i); } }; From c211288576c7289a393250b3f0a762ff6640f3e9 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sat, 11 Dec 2021 17:12:15 +0200 Subject: [PATCH 083/121] Update example.cpp --- example/example.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/example.cpp b/example/example.cpp index 54212374..ca5356c9 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -272,7 +272,7 @@ struct fmt_lib::formatter : fmt_lib::formatter void user_defined_example() { - spdlog::info("user defined type: {}", my_type{14}); + spdlog::info("user defined type: {}", my_type(14)); } // Custom error handler. Will be triggered on log failure. From e45c11f98a6d124f0044dd9e3f513ee37696cfc6 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sat, 11 Dec 2021 17:18:40 +0200 Subject: [PATCH 084/121] Update example.cpp --- example/example.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/example/example.cpp b/example/example.cpp index ca5356c9..c1251b1b 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -258,6 +258,7 @@ void multi_sink_example() struct my_type { int i = 0; + explicit my_type(int i): i(i){}; }; namespace fmt_lib = spdlog::fmt_lib; From ee74321ac326d01525632bf9e8d6108551787aa5 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sat, 11 Dec 2021 17:39:43 +0200 Subject: [PATCH 085/121] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 152ac47d..424f2926 100644 --- a/.travis.yml +++ b/.travis.yml @@ -73,7 +73,7 @@ addons: &clang12 matrix: include: # Test gcc-4.8: C++11, Build=Release - - env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 + - env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 CXXFLAGS="-fpermissive" os: linux addons: *gcc48 From 0c84e210223d80f86d59d4d6aedaba5b6d48eec8 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sat, 11 Dec 2021 18:08:40 +0200 Subject: [PATCH 086/121] Update .travis.yml --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 424f2926..ae66dd3a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,11 +5,11 @@ sudo: required language: cpp -# gcc t -addons: &gcc48 +# gcc 4.9 +addons: &gcc49 apt: packages: - - g++-4.8 + - g++-4.9 sources: - ubuntu-toolchain-r-test @@ -72,10 +72,10 @@ addons: &clang12 matrix: include: - # Test gcc-4.8: C++11, Build=Release - - env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 CXXFLAGS="-fpermissive" + # Test gcc-4.9: C++11, Build=Release + - env: GCC_VERSION=4.9 BUILD_TYPE=Release CPP=11 os: linux - addons: *gcc48 + addons: *gcc49 # Test gcc-7: C++11, Build=Release - env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11 From 702cf4f54a0cc0edf261f4aa9c2fd38cfccca4c7 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sat, 11 Dec 2021 18:11:55 +0200 Subject: [PATCH 087/121] Update .travis.yml --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index ae66dd3a..3e47fcbc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,10 @@ sudo: required language: cpp # gcc 4.9 -addons: &gcc49 +addons: &gcc5 apt: packages: - - g++-4.9 + - g++-5 sources: - ubuntu-toolchain-r-test @@ -72,10 +72,10 @@ addons: &clang12 matrix: include: - # Test gcc-4.9: C++11, Build=Release - - env: GCC_VERSION=4.9 BUILD_TYPE=Release CPP=11 + # Test gcc-4.5: C++11, Build=Release + - env: GCC_VERSION=5 BUILD_TYPE=Release CPP=11 os: linux - addons: *gcc49 + addons: *gcc5 # Test gcc-7: C++11, Build=Release - env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11 From fe782edc534bed26d0f9844ff4dc525b692ad251 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sat, 11 Dec 2021 18:23:36 +0200 Subject: [PATCH 088/121] Update .travis.yml --- .travis.yml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3e47fcbc..012360a1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,10 @@ sudo: required language: cpp # gcc 4.9 -addons: &gcc5 +addons: &gcc49 apt: packages: - - g++-5 + - g++-4.9 sources: - ubuntu-toolchain-r-test @@ -69,13 +69,16 @@ addons: &clang12 - sourceline: "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-12 main" key_url: "https://apt.llvm.org/llvm-snapshot.gpg.key" - +env: + global: + - BUILD_EXAMPLE='ON' + matrix: include: - # Test gcc-4.5: C++11, Build=Release - - env: GCC_VERSION=5 BUILD_TYPE=Release CPP=11 + # Test gcc-4.9: C++11, Build=Release + - env: GCC_VERSION=4.9 BUILD_TYPE=Release CPP=11 BUILD_EXAMPLE='OFF' os: linux - addons: *gcc5 + addons: *gcc49 # Test gcc-7: C++11, Build=Release - env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11 @@ -139,8 +142,8 @@ script: --warn-uninitialized \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DCMAKE_CXX_STANDARD=$CPP \ - -DSPDLOG_BUILD_EXAMPLE=ON \ - -DSPDLOG_BUILD_EXAMPLE_HO=ON \ + -DSPDLOG_BUILD_EXAMPLE=$BUILD_EXAMPLE \ + -DSPDLOG_BUILD_EXAMPLE_HO=$BUILD_EXAMPLE \ -DSPDLOG_BUILD_WARNINGS=ON \ -DSPDLOG_BUILD_BENCH=OFF \ -DSPDLOG_BUILD_TESTS=ON \ From 4cb11878719522c8026ea85a04536b07b5d2b69b Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sun, 12 Dec 2021 09:59:40 +0200 Subject: [PATCH 089/121] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 920df2bc..04b930aa 100644 --- a/README.md +++ b/README.md @@ -398,8 +398,6 @@ void replace_default_logger_example() { auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true); spdlog::set_default_logger(new_logger); - spdlog::set_level(spdlog::level::trace); - spdlog::trace("This message should appear.."); } ``` From 3f49f0f247067830d744b82381ddc41dac9711a1 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sun, 12 Dec 2021 10:01:34 +0200 Subject: [PATCH 090/121] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 04b930aa..caac8f6c 100644 --- a/README.md +++ b/README.md @@ -398,6 +398,7 @@ void replace_default_logger_example() { auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true); spdlog::set_default_logger(new_logger); + spdlog::info("new logger log message"); } ``` From 7e95963940c6dc5e0cfe46bd59bb9119d1fa19a1 Mon Sep 17 00:00:00 2001 From: Vladislav Nepogodin Date: Sun, 19 Dec 2021 15:04:47 +0400 Subject: [PATCH 091/121] Useless cast --- include/spdlog/details/os-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index 16f3d2e9..8762d847 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -407,7 +407,7 @@ SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT #ifdef _WIN32 return static_cast(::GetCurrentProcessId()); #else - return static_cast(::getpid()); + return ::getpid(); #endif } From f81cb9f3652c940e5b83e9d25d84c9e6d85e1bd1 Mon Sep 17 00:00:00 2001 From: Vladislav Nepogodin Date: Sun, 19 Dec 2021 21:05:21 +0400 Subject: [PATCH 092/121] Revert "Useless cast" This reverts commit 7e95963940c6dc5e0cfe46bd59bb9119d1fa19a1. --- include/spdlog/details/os-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index 8762d847..16f3d2e9 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -407,7 +407,7 @@ SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT #ifdef _WIN32 return static_cast(::GetCurrentProcessId()); #else - return ::getpid(); + return static_cast(::getpid()); #endif } From f096c615c36cd9217fbd1f8642e4a33fb6701558 Mon Sep 17 00:00:00 2001 From: Vladislav Nepogodin Date: Sun, 19 Dec 2021 21:37:21 +0400 Subject: [PATCH 093/121] =?UTF-8?q?=F0=9F=94=A5=20conditional=5Fcast?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/spdlog/common.h | 18 ++++++++++++++++-- include/spdlog/details/os-inl.h | 4 ++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 9903463c..620b4757 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -30,7 +30,7 @@ # define SPDLOG_API __declspec(dllimport) # endif # else // !defined(_WIN32) -# define SPDLOG_API __attribute__((visibility ("default"))) +# define SPDLOG_API __attribute__((visibility("default"))) # endif # else // !defined(SPDLOG_SHARED_LIB) # define SPDLOG_API @@ -320,13 +320,27 @@ struct file_event_handlers }; namespace details { + +// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) +template::value, int> = 0> +constexpr T conditional_static_cast(U value) +{ + return static_cast(value); +} + +template::value, int> = 0> +constexpr T conditional_static_cast(U value) +{ + return value; +} + // make_unique support for pre c++14 #if __cplusplus >= 201402L // C++14 and beyond using std::make_unique; #else template -std::unique_ptr make_unique(Args &&... args) +std::unique_ptr make_unique(Args &&...args) { static_assert(!std::is_array::value, "arrays not supported"); return std::unique_ptr(new T(std::forward(args)...)); diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index 16f3d2e9..e094e0f7 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -405,9 +405,9 @@ SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT { #ifdef _WIN32 - return static_cast(::GetCurrentProcessId()); + return conditional_static_cast(::GetCurrentProcessId()); #else - return static_cast(::getpid()); + return conditional_static_cast(::getpid()); #endif } From a087dee98a59bb70472960d098a69f94ed73b6e3 Mon Sep 17 00:00:00 2001 From: Vladislav Nepogodin Date: Sun, 19 Dec 2021 21:48:39 +0400 Subject: [PATCH 094/121] =?UTF-8?q?=F0=9F=9A=A7=20fix=20building=20with=20?= =?UTF-8?q?c++11?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/spdlog/common.h | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 620b4757..d391b11b 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -321,24 +321,15 @@ struct file_event_handlers namespace details { -// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) -template::value, int> = 0> -constexpr T conditional_static_cast(U value) -{ - return static_cast(value); -} - -template::value, int> = 0> -constexpr T conditional_static_cast(U value) -{ - return value; -} - // make_unique support for pre c++14 #if __cplusplus >= 201402L // C++14 and beyond +using std::enable_if_t; using std::make_unique; #else +template +using enable_if_t = typename std::enable_if::type; + template std::unique_ptr make_unique(Args &&...args) { @@ -346,6 +337,20 @@ std::unique_ptr make_unique(Args &&...args) return std::unique_ptr(new T(std::forward(args)...)); } #endif + +// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) +template::value, int> = 0> +constexpr T conditional_static_cast(U value) +{ + return static_cast(value); +} + +template::value, int> = 0> +constexpr T conditional_static_cast(U value) +{ + return value; +} + } // namespace details } // namespace spdlog From 626efad30761d1ad436e2cc0d5d98d4e11fed7eb Mon Sep 17 00:00:00 2001 From: Tim Blechmann Date: Thu, 30 Dec 2021 09:39:45 +0800 Subject: [PATCH 095/121] spdlog: fmt - support `std::span` in `to_hex` `std::span` does not have `const_iterator`. this prevents `to_hex` from being used with `std::span<>`. to fix this, we provide an explicit overload. compare: https://cplusplus.github.io/LWG/issue3320 --- include/spdlog/fmt/bin_to_hex.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h index 14908310..76dcb836 100644 --- a/include/spdlog/fmt/bin_to_hex.h +++ b/include/spdlog/fmt/bin_to_hex.h @@ -8,6 +8,14 @@ #include #include +#if defined(__has_include) && __has_include() +#include +#endif + +#if __cpp_lib_span >= 202002L +#include +#endif + // // Support for logging binary data as hex // format flags, any combination of the following: @@ -68,6 +76,19 @@ inline details::dump_info to_hex(const Conta return details::dump_info(std::begin(container), std::end(container), size_per_line); } +#if __cpp_lib_span >= 202002L + +template +inline details::dump_info::iterator> to_hex(const std::span &container, size_t size_per_line = 32) +{ + using Container = std::span; + static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1"); + using Iter = typename Container::iterator; + return details::dump_info(std::begin(container), std::end(container), size_per_line); +} + +#endif + // create dump_info from ranges template inline details::dump_info to_hex(const It range_begin, const It range_end, size_t size_per_line = 32) From 3540ba32e9565219ac1e88e242430713c8138a82 Mon Sep 17 00:00:00 2001 From: Sprite Date: Tue, 4 Jan 2022 09:16:20 +0800 Subject: [PATCH 096/121] Reset current size if rotated files on open --- include/spdlog/sinks/rotating_file_sink-inl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h index 2c003959..7401e4f2 100644 --- a/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -36,6 +36,7 @@ SPDLOG_INLINE rotating_file_sink::rotating_file_sink( if (rotate_on_open && current_size_ > 0) { rotate_(); + current_size_ = 0; } } From 2a4c34b8785137eba9da7eb4cbb28b4162218272 Mon Sep 17 00:00:00 2001 From: Dave Rigby Date: Tue, 11 Jan 2022 14:58:58 +0000 Subject: [PATCH 097/121] Allow forward-declaration of level_enum spdlog::level::level_enum cannot be forward-declared at present, as the definition does not specify an underlying type. To allow users to make use of to refer to level::level_enum without pulling in all of (which can be quite costly), specify an underlying type (int) for level::level_enum, then add a forward-declaration for it to spdlog/fwd.h. Note this required explicitly casting level_enum to size_t within ansicolor_sink due to sign-conversion errors: implicit conversion changes signedness: 'const level::level_enum' to 'std::__1::array::size_type' (aka 'unsigned long') [-Wsign-conversion] It would appear that an enum with an unspecified underlying type is in some kind of superposition - it can be treated as both signed _and_ unsigned - using an underlying type of 'unsigned int' triggers even more warnings of this kind... --- include/spdlog/common.h | 2 +- include/spdlog/fwd.h | 4 ++++ include/spdlog/sinks/ansicolor_sink-inl.h | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/spdlog/common.h b/include/spdlog/common.h index d391b11b..0d614dfc 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -208,7 +208,7 @@ using level_t = std::atomic; // Log level enum namespace level { -enum level_enum +enum level_enum : int { trace = SPDLOG_LEVEL_TRACE, debug = SPDLOG_LEVEL_DEBUG, diff --git a/include/spdlog/fwd.h b/include/spdlog/fwd.h index cc05ddd4..d2588257 100644 --- a/include/spdlog/fwd.h +++ b/include/spdlog/fwd.h @@ -11,4 +11,8 @@ namespace sinks { class sink; } +namespace level { +enum level_enum : int; +} + } // namespace spdlog diff --git a/include/spdlog/sinks/ansicolor_sink-inl.h b/include/spdlog/sinks/ansicolor_sink-inl.h index d8db423c..b5848f2d 100644 --- a/include/spdlog/sinks/ansicolor_sink-inl.h +++ b/include/spdlog/sinks/ansicolor_sink-inl.h @@ -34,7 +34,7 @@ template SPDLOG_INLINE void ansicolor_sink::set_color(level::level_enum color_level, string_view_t color) { std::lock_guard lock(mutex_); - colors_[color_level] = to_string_(color); + colors_[static_cast(color_level)] = to_string_(color); } template @@ -52,7 +52,7 @@ SPDLOG_INLINE void ansicolor_sink::log(const details::log_msg &msg // before color range print_range_(formatted, 0, msg.color_range_start); // in color range - print_ccode_(colors_[msg.level]); + print_ccode_(colors_[static_cast(msg.level)]); print_range_(formatted, msg.color_range_start, msg.color_range_end); print_ccode_(reset); // after color range From eab522e743b4f41caed1a7426cf37c77531d1382 Mon Sep 17 00:00:00 2001 From: doug1234 Date: Thu, 13 Jan 2022 20:57:14 -0500 Subject: [PATCH 098/121] Now only getting the date if formater needs to display date related information. --- include/spdlog/pattern_formatter-inl.h | 43 +++++++++++++++++++++++--- include/spdlog/pattern_formatter.h | 1 + 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/include/spdlog/pattern_formatter-inl.h b/include/spdlog/pattern_formatter-inl.h index ec727032..caf7c62c 100644 --- a/include/spdlog/pattern_formatter-inl.h +++ b/include/spdlog/pattern_formatter-inl.h @@ -1021,6 +1021,7 @@ SPDLOG_INLINE pattern_formatter::pattern_formatter( , pattern_time_type_(time_type) , last_log_secs_(0) , custom_handlers_(std::move(custom_user_flags)) + , needs_time_(false) { std::memset(&cached_tm_, 0, sizeof(cached_tm_)); compile_pattern_(pattern_); @@ -1032,6 +1033,7 @@ SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type, , eol_(std::move(eol)) , pattern_time_type_(time_type) , last_log_secs_(0) + , needs_time_(false) { std::memset(&cached_tm_, 0, sizeof(cached_tm_)); formatters_.push_back(details::make_unique(details::padding_info{})); @@ -1049,11 +1051,13 @@ SPDLOG_INLINE std::unique_ptr pattern_formatter::clone() const SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory_buf_t &dest) { - auto secs = std::chrono::duration_cast(msg.time.time_since_epoch()); - if (secs != last_log_secs_) - { - cached_tm_ = get_time_(msg); - last_log_secs_ = secs; + if (needs_time_) { + const auto secs = std::chrono::duration_cast(msg.time.time_since_epoch()); + if (secs != last_log_secs_) + { + cached_tm_ = get_time_(msg); + last_log_secs_ = secs; + } } for (auto &f : formatters_) @@ -1097,6 +1101,7 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i { case ('+'): // default formatter formatters_.push_back(details::make_unique(padding)); + needs_time_ = true; break; case 'n': // logger name @@ -1121,101 +1126,125 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i case ('a'): // weekday formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('A'): // short weekday formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('b'): case ('h'): // month formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('B'): // short month formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('c'): // datetime formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('C'): // year 2 digits formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('Y'): // year 4 digits formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('D'): case ('x'): // datetime MM/DD/YY formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('m'): // month 1-12 formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('d'): // day of month 1-31 formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('H'): // hours 24 formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('I'): // hours 12 formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('M'): // minutes formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('S'): // seconds formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('e'): // milliseconds formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('f'): // microseconds formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('F'): // nanoseconds formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('E'): // seconds since epoch formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('p'): // am/pm formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('r'): // 12 hour clock 02:55:02 pm formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('R'): // 24-hour HH:MM time formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('T'): case ('X'): // ISO 8601 time format (HH:MM:SS) formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('z'): // timezone formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('P'): // pid formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('^'): // color range start @@ -1252,18 +1281,22 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i case ('u'): // elapsed time since last log message in nanos formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('i'): // elapsed time since last log message in micros formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('o'): // elapsed time since last log message in millis formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; case ('O'): // elapsed time since last log message in seconds formatters_.push_back(details::make_unique>(padding)); + needs_time_ = true; break; default: // Unknown flag appears as is diff --git a/include/spdlog/pattern_formatter.h b/include/spdlog/pattern_formatter.h index 6810f03b..57156cb7 100644 --- a/include/spdlog/pattern_formatter.h +++ b/include/spdlog/pattern_formatter.h @@ -103,6 +103,7 @@ private: std::string pattern_; std::string eol_; pattern_time_type pattern_time_type_; + bool needs_time_; std::tm cached_tm_; std::chrono::seconds last_log_secs_; std::vector> formatters_; From 5568b16ed5e3a7dffe3e67d30e0ca721da0a489f Mon Sep 17 00:00:00 2001 From: doug1234 Date: Thu, 13 Jan 2022 21:35:02 -0500 Subject: [PATCH 099/121] Resetting the needs time flag when setting a pattern. --- include/spdlog/pattern_formatter-inl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/spdlog/pattern_formatter-inl.h b/include/spdlog/pattern_formatter-inl.h index caf7c62c..4342685d 100644 --- a/include/spdlog/pattern_formatter-inl.h +++ b/include/spdlog/pattern_formatter-inl.h @@ -1071,6 +1071,7 @@ SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory SPDLOG_INLINE void pattern_formatter::set_pattern(std::string pattern) { pattern_ = std::move(pattern); + needs_time_ = false; compile_pattern_(pattern_); } From d9ec02d40024984e074fd357f352f41f421aedb0 Mon Sep 17 00:00:00 2001 From: doug1234 Date: Fri, 14 Jan 2022 20:06:26 -0500 Subject: [PATCH 100/121] Fix mistake in previous checkin. --- include/spdlog/pattern_formatter-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/pattern_formatter-inl.h b/include/spdlog/pattern_formatter-inl.h index 4342685d..c98c9fb2 100644 --- a/include/spdlog/pattern_formatter-inl.h +++ b/include/spdlog/pattern_formatter-inl.h @@ -1033,7 +1033,7 @@ SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type, , eol_(std::move(eol)) , pattern_time_type_(time_type) , last_log_secs_(0) - , needs_time_(false) + , needs_time_(true) { std::memset(&cached_tm_, 0, sizeof(cached_tm_)); formatters_.push_back(details::make_unique(details::padding_info{})); From 584d77237ea732e924cb5b1473667ce51321a371 Mon Sep 17 00:00:00 2001 From: doug1234 Date: Sat, 15 Jan 2022 13:35:27 -0500 Subject: [PATCH 101/121] Several minor improvements based on code review suggestions. --- include/spdlog/pattern_formatter-inl.h | 62 ++++++++++++-------------- include/spdlog/pattern_formatter.h | 2 +- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/include/spdlog/pattern_formatter-inl.h b/include/spdlog/pattern_formatter-inl.h index c98c9fb2..587e5062 100644 --- a/include/spdlog/pattern_formatter-inl.h +++ b/include/spdlog/pattern_formatter-inl.h @@ -1021,7 +1021,7 @@ SPDLOG_INLINE pattern_formatter::pattern_formatter( , pattern_time_type_(time_type) , last_log_secs_(0) , custom_handlers_(std::move(custom_user_flags)) - , needs_time_(false) + , need_localtime_(false) { std::memset(&cached_tm_, 0, sizeof(cached_tm_)); compile_pattern_(pattern_); @@ -1033,7 +1033,7 @@ SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type, , eol_(std::move(eol)) , pattern_time_type_(time_type) , last_log_secs_(0) - , needs_time_(true) + , need_localtime_(true) { std::memset(&cached_tm_, 0, sizeof(cached_tm_)); formatters_.push_back(details::make_unique(details::padding_info{})); @@ -1051,7 +1051,8 @@ SPDLOG_INLINE std::unique_ptr pattern_formatter::clone() const SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory_buf_t &dest) { - if (needs_time_) { + if (need_localtime_) + { const auto secs = std::chrono::duration_cast(msg.time.time_since_epoch()); if (secs != last_log_secs_) { @@ -1071,7 +1072,7 @@ SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory SPDLOG_INLINE void pattern_formatter::set_pattern(std::string pattern) { pattern_ = std::move(pattern); - needs_time_ = false; + need_localtime_ = false; compile_pattern_(pattern_); } @@ -1102,7 +1103,7 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i { case ('+'): // default formatter formatters_.push_back(details::make_unique(padding)); - needs_time_ = true; + need_localtime_ = true; break; case 'n': // logger name @@ -1127,125 +1128,120 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i case ('a'): // weekday formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('A'): // short weekday formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('b'): case ('h'): // month formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('B'): // short month formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('c'): // datetime formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('C'): // year 2 digits formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('Y'): // year 4 digits formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('D'): case ('x'): // datetime MM/DD/YY formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('m'): // month 1-12 formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('d'): // day of month 1-31 formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('H'): // hours 24 formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('I'): // hours 12 formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('M'): // minutes formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('S'): // seconds formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('e'): // milliseconds formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; break; case ('f'): // microseconds formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; break; case ('F'): // nanoseconds formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; break; case ('E'): // seconds since epoch formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; break; case ('p'): // am/pm formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('r'): // 12 hour clock 02:55:02 pm formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('R'): // 24-hour HH:MM time formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('T'): case ('X'): // ISO 8601 time format (HH:MM:SS) formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('z'): // timezone formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('P'): // pid formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; break; case ('^'): // color range start @@ -1282,22 +1278,22 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i case ('u'): // elapsed time since last log message in nanos formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('i'): // elapsed time since last log message in micros formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('o'): // elapsed time since last log message in millis formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; case ('O'): // elapsed time since last log message in seconds formatters_.push_back(details::make_unique>(padding)); - needs_time_ = true; + need_localtime_ = true; break; default: // Unknown flag appears as is diff --git a/include/spdlog/pattern_formatter.h b/include/spdlog/pattern_formatter.h index 57156cb7..a1a64807 100644 --- a/include/spdlog/pattern_formatter.h +++ b/include/spdlog/pattern_formatter.h @@ -103,7 +103,7 @@ private: std::string pattern_; std::string eol_; pattern_time_type pattern_time_type_; - bool needs_time_; + bool need_localtime_; std::tm cached_tm_; std::chrono::seconds last_log_secs_; std::vector> formatters_; From 28b9adf794ac9affe13402a54f7e9ea58ec4dfc6 Mon Sep 17 00:00:00 2001 From: doug1234 Date: Sat, 15 Jan 2022 16:41:06 -0500 Subject: [PATCH 102/121] Added the last few suggested changes. --- include/spdlog/pattern_formatter-inl.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/spdlog/pattern_formatter-inl.h b/include/spdlog/pattern_formatter-inl.h index 587e5062..19d19e29 100644 --- a/include/spdlog/pattern_formatter-inl.h +++ b/include/spdlog/pattern_formatter-inl.h @@ -1278,22 +1278,18 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i case ('u'): // elapsed time since last log message in nanos formatters_.push_back(details::make_unique>(padding)); - need_localtime_ = true; break; case ('i'): // elapsed time since last log message in micros formatters_.push_back(details::make_unique>(padding)); - need_localtime_ = true; break; case ('o'): // elapsed time since last log message in millis formatters_.push_back(details::make_unique>(padding)); - need_localtime_ = true; break; case ('O'): // elapsed time since last log message in seconds formatters_.push_back(details::make_unique>(padding)); - need_localtime_ = true; break; default: // Unknown flag appears as is From 2382c87aa30277e11b21a50c2fef6f7f891e8fc0 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sun, 16 Jan 2022 23:30:57 +0200 Subject: [PATCH 103/121] Update pattern_formatter-inl.h --- include/spdlog/pattern_formatter-inl.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/spdlog/pattern_formatter-inl.h b/include/spdlog/pattern_formatter-inl.h index 19d19e29..f18b0d16 100644 --- a/include/spdlog/pattern_formatter-inl.h +++ b/include/spdlog/pattern_formatter-inl.h @@ -1368,7 +1368,6 @@ SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::stri { truncate = false; } - return details::padding_info{std::min(width, max_width), side, truncate}; } From 666bec5017f4487670a91306cdbc321c63054050 Mon Sep 17 00:00:00 2001 From: Pixel Date: Mon, 17 Jan 2022 12:13:37 +0530 Subject: [PATCH 104/121] removed unneeded spaces On Line 83 someone probably misclicked tab just removed that tab --- include/spdlog/pattern_formatter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/pattern_formatter.h b/include/spdlog/pattern_formatter.h index a1a64807..df046f67 100644 --- a/include/spdlog/pattern_formatter.h +++ b/include/spdlog/pattern_formatter.h @@ -80,7 +80,7 @@ public: using custom_flags = std::unordered_map>; explicit pattern_formatter(std::string pattern, pattern_time_type time_type = pattern_time_type::local, - std::string eol = spdlog::details::os::default_eol, custom_flags custom_user_flags = custom_flags()); + std::string eol = spdlog::details::os::default_eol, custom_flags custom_user_flags = custom_flags()); // use default pattern is not given explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol); From 792d618c0297dbdf5583026bb03d1195a4a64d9d Mon Sep 17 00:00:00 2001 From: Leon Brands Date: Sun, 23 Jan 2022 18:49:50 +0100 Subject: [PATCH 105/121] added a few missing files/directories to the gitignore --- .gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gitignore b/.gitignore index 1b452285..ca7c4606 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ # Auto generated files +[Dd]ebug/ +[Rr]elease/ build/* *.slo *.lo @@ -55,6 +57,7 @@ example/* # generated files generated +version.rc # Cmake CMakeCache.txt @@ -67,6 +70,8 @@ install_manifest.txt /tests/tests.VC.db /tests/tests /tests/logs/* +spdlogConfig.cmake +spdlogConfigVersion.cmake # idea .idea/ @@ -81,3 +86,7 @@ cmake-build-*/ *.tcl *.user *.sln + +# macos +*.DS_store +*.xcodeproj/ From 5afff7821f879de487f80a8e918fe36afbcb0318 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 5 Feb 2022 14:23:33 +0200 Subject: [PATCH 106/121] throw if flush failed --- include/spdlog/details/file_helper-inl.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/spdlog/details/file_helper-inl.h b/include/spdlog/details/file_helper-inl.h index 8276633b..fa082253 100644 --- a/include/spdlog/details/file_helper-inl.h +++ b/include/spdlog/details/file_helper-inl.h @@ -84,7 +84,10 @@ SPDLOG_INLINE void file_helper::reopen(bool truncate) SPDLOG_INLINE void file_helper::flush() { - std::fflush(fd_); + if(std::fflush(fd_) != 0) + { + throw_spdlog_ex("Failed flush to file " + os::filename_to_str(filename_), errno); + } } SPDLOG_INLINE void file_helper::close() From 75361920583ce1fd804b9d4f97b025bf0fae6cd6 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 5 Feb 2022 17:13:33 +0200 Subject: [PATCH 107/121] Fix #2261 --- include/spdlog/sinks/rotating_file_sink-inl.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h index 7401e4f2..9360edcd 100644 --- a/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -67,13 +67,18 @@ SPDLOG_INLINE void rotating_file_sink::sink_it_(const details::log_msg &m { memory_buf_t formatted; base_sink::formatter_->format(msg, formatted); - current_size_ += formatted.size(); - if (current_size_ > max_size_) + auto new_size = current_size_ + formatted.size(); + + // rotate if the new estimated file size exceeds max size. + // check also that the real size > 0 to better deal with full disk (see issue #2261). + // we only check the real size when new_size > max_size_ because it is relatively expensive. + if (new_size > max_size_ && file_helper_.size() > 0) { rotate_(); - current_size_ = formatted.size(); + new_size = formatted.size(); } file_helper_.write(formatted); + current_size_ = new_size; } template @@ -92,6 +97,7 @@ SPDLOG_INLINE void rotating_file_sink::rotate_() { using details::os::filename_to_str; using details::os::path_exists; + file_helper_.close(); for (auto i = max_files_; i > 0; --i) { From ec8b0beddd01323caeba3b3b53f2c1923fc5e08d Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 5 Feb 2022 17:16:36 +0200 Subject: [PATCH 108/121] comment --- include/spdlog/sinks/rotating_file_sink-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h index 9360edcd..faff7820 100644 --- a/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -70,7 +70,7 @@ SPDLOG_INLINE void rotating_file_sink::sink_it_(const details::log_msg &m auto new_size = current_size_ + formatted.size(); // rotate if the new estimated file size exceeds max size. - // check also that the real size > 0 to better deal with full disk (see issue #2261). + // rotate only if the real size > 0 to better deal with full disk (see issue #2261). // we only check the real size when new_size > max_size_ because it is relatively expensive. if (new_size > max_size_ && file_helper_.size() > 0) { From 5b03dc179658a63c8c7b409512f9d01c5e71ed44 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 5 Feb 2022 17:37:55 +0200 Subject: [PATCH 109/121] Throw if rotating_file_sink constructor receives max_size==0 as arg --- include/spdlog/sinks/rotating_file_sink-inl.h | 4 ++++ tests/test_file_logging.cpp | 9 +++++++++ 2 files changed, 13 insertions(+) diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h index faff7820..e32e20d4 100644 --- a/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -31,6 +31,10 @@ SPDLOG_INLINE rotating_file_sink::rotating_file_sink( , max_files_(max_files) , file_helper_{event_handlers} { + if(max_size == 0) + { + throw_spdlog_ex("rotating sink constructor: max_size arg cannot be zero"); + } file_helper_.open(calc_filename(base_filename_, 0)); current_size_ = file_helper_.size(); // expensive. called only once if (rotate_on_open && current_size_ > 0) diff --git a/tests/test_file_logging.cpp b/tests/test_file_logging.cpp index c808916d..1c7a1853 100644 --- a/tests/test_file_logging.cpp +++ b/tests/test_file_logging.cpp @@ -98,3 +98,12 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]") REQUIRE(get_filesize(ROTATING_LOG) <= max_size); REQUIRE(get_filesize(ROTATING_LOG ".1") <= max_size); } + +// test that passing max_size=0 throws +TEST_CASE("rotating_file_logger3", "[rotating_logger]]") +{ + prepare_logdir(); + size_t max_size = 0; + spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG); + REQUIRE_THROWS_AS(spdlog::rotating_logger_mt("logger", basename, max_size, 0), spdlog::spdlog_ex); +} From 0b48976be49a69d8ee30b9b153b261704bbc02a0 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 5 Feb 2022 19:45:19 +0200 Subject: [PATCH 110/121] flush before rotating --- include/spdlog/sinks/rotating_file_sink-inl.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h index e32e20d4..050a3e6c 100644 --- a/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -76,10 +76,14 @@ SPDLOG_INLINE void rotating_file_sink::sink_it_(const details::log_msg &m // rotate if the new estimated file size exceeds max size. // rotate only if the real size > 0 to better deal with full disk (see issue #2261). // we only check the real size when new_size > max_size_ because it is relatively expensive. - if (new_size > max_size_ && file_helper_.size() > 0) + if (new_size > max_size_) { - rotate_(); - new_size = formatted.size(); + file_helper_.flush(); + if(file_helper_.size() > 0) + { + rotate_(); + new_size = formatted.size(); + } } file_helper_.write(formatted); current_size_ = new_size; From d497f494f09fc4e6b17a83474fbbf94a9b7dd3cb Mon Sep 17 00:00:00 2001 From: Kyuheon Kim Date: Tue, 8 Feb 2022 20:29:58 +0900 Subject: [PATCH 111/121] Apply pattern width to one of the source information fields while missing source information --- include/spdlog/pattern_formatter-inl.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/spdlog/pattern_formatter-inl.h b/include/spdlog/pattern_formatter-inl.h index f18b0d16..4d312081 100644 --- a/include/spdlog/pattern_formatter-inl.h +++ b/include/spdlog/pattern_formatter-inl.h @@ -766,6 +766,7 @@ public: { if (msg.source.empty()) { + ScopedPadder p(0, padinfo_, dest); return; } @@ -800,6 +801,7 @@ public: { if (msg.source.empty()) { + ScopedPadder p(0, padinfo_, dest); return; } size_t text_size = padinfo_.enabled() ? std::char_traits::length(msg.source.filename) : 0; @@ -846,6 +848,7 @@ public: { if (msg.source.empty()) { + ScopedPadder p(0, padinfo_, dest); return; } auto filename = basename(msg.source.filename); @@ -867,6 +870,7 @@ public: { if (msg.source.empty()) { + ScopedPadder p(0, padinfo_, dest); return; } @@ -889,6 +893,7 @@ public: { if (msg.source.empty()) { + ScopedPadder p(0, padinfo_, dest); return; } size_t text_size = padinfo_.enabled() ? std::char_traits::length(msg.source.funcname) : 0; @@ -1051,7 +1056,7 @@ SPDLOG_INLINE std::unique_ptr pattern_formatter::clone() const SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory_buf_t &dest) { - if (need_localtime_) + if (need_localtime_) { const auto secs = std::chrono::duration_cast(msg.time.time_since_epoch()); if (secs != last_log_secs_) From d8199b607d26cc023d33df17e3bce04e3004fdaf Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 12 Feb 2022 13:17:41 +0200 Subject: [PATCH 112/121] Bump fmt to version 8.1.1 and run clang-format --- include/spdlog/fmt/bundled/args.h | 2 + include/spdlog/fmt/bundled/chrono.h | 1181 +++++++++++++++++++---- include/spdlog/fmt/bundled/color.h | 35 +- include/spdlog/fmt/bundled/compile.h | 25 +- include/spdlog/fmt/bundled/core.h | 746 +++++++++----- include/spdlog/fmt/bundled/format-inl.h | 549 ++++++----- include/spdlog/fmt/bundled/format.h | 902 +++++++++++------ include/spdlog/fmt/bundled/os.h | 48 +- include/spdlog/fmt/bundled/ostream.h | 108 +-- include/spdlog/fmt/bundled/printf.h | 19 +- include/spdlog/fmt/bundled/ranges.h | 547 ++++++++--- include/spdlog/fmt/bundled/xchar.h | 10 +- src/fmt.cpp | 115 ++- 13 files changed, 2975 insertions(+), 1312 deletions(-) diff --git a/include/spdlog/fmt/bundled/args.h b/include/spdlog/fmt/bundled/args.h index 562e8ab1..9a8e4ed2 100644 --- a/include/spdlog/fmt/bundled/args.h +++ b/include/spdlog/fmt/bundled/args.h @@ -143,6 +143,8 @@ class dynamic_format_arg_store } public: + constexpr dynamic_format_arg_store() = default; + /** \rst Adds an argument into the dynamic store for later passing to a formatting diff --git a/include/spdlog/fmt/bundled/chrono.h b/include/spdlog/fmt/bundled/chrono.h index c024fd71..682efd8d 100644 --- a/include/spdlog/fmt/bundled/chrono.h +++ b/include/spdlog/fmt/bundled/chrono.h @@ -11,13 +11,29 @@ #include #include #include +#include #include -#include +#include +#include #include "format.h" FMT_BEGIN_NAMESPACE +// Enable tzset. +#ifndef FMT_USE_TZSET +// UWP doesn't provide _tzset. +# if FMT_HAS_INCLUDE("winapifamily.h") +# include +# endif +# if defined(_WIN32) && (!defined(WINAPI_FAMILY) || \ + (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) +# define FMT_USE_TZSET 1 +# else +# define FMT_USE_TZSET 0 +# endif +#endif + // Enable safe chrono durations, unless explicitly disabled. #ifndef FMT_SAFE_DURATION_CAST # define FMT_SAFE_DURATION_CAST 1 @@ -44,7 +60,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { static_assert(T::is_integer, "To must be integral"); // A and B are both signed, or both unsigned. - if (F::digits <= T::digits) { + if (detail::const_check(F::digits <= T::digits)) { // From fits in To without any problem. } else { // From does not always fit in To, resort to a dynamic check. @@ -79,14 +95,15 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) { return {}; } // From is positive. Can it always fit in To? - if (F::digits > T::digits && + if (detail::const_check(F::digits > T::digits) && from > static_cast(detail::max_value())) { ec = 1; return {}; } } - if (!F::is_signed && T::is_signed && F::digits >= T::digits && + if (detail::const_check(!F::is_signed && T::is_signed && + F::digits >= T::digits) && from > static_cast(detail::max_value())) { ec = 1; return {}; @@ -243,7 +260,7 @@ To safe_duration_cast(std::chrono::duration from, } // multiply with Factor::num without overflow or underflow - if (Factor::num != 1) { + if (detail::const_check(Factor::num != 1)) { constexpr auto max1 = detail::max_value() / static_cast(Factor::num); if (count > max1) { @@ -260,7 +277,7 @@ To safe_duration_cast(std::chrono::duration from, } // this can't go wrong, right? den>0 is checked earlier. - if (Factor::den != 1) { + if (detail::const_check(Factor::den != 1)) { using common_t = typename std::common_type::type; count /= static_cast(Factor::den); } @@ -288,74 +305,139 @@ inline null<> localtime_s(...) { return null<>(); } inline null<> gmtime_r(...) { return null<>(); } inline null<> gmtime_s(...) { return null<>(); } -inline auto do_write(const std::tm& time, const std::locale& loc, char format, - char modifier) -> std::string { - auto&& os = std::ostringstream(); - os.imbue(loc); - using iterator = std::ostreambuf_iterator; - const auto& facet = std::use_facet>(loc); - auto end = facet.put(os, os, ' ', &time, format, modifier); - if (end.failed()) FMT_THROW(format_error("failed to format time")); - auto str = os.str(); - if (!detail::is_utf8() || loc == std::locale::classic()) return str; - // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and - // gcc-4. -#if FMT_MSC_VER != 0 || \ - (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) - // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 - // and newer. - using code_unit = wchar_t; +inline const std::locale& get_classic_locale() { + static const auto& locale = std::locale::classic(); + return locale; +} + +template struct codecvt_result { + static constexpr const size_t max_size = 32; + CodeUnit buf[max_size]; + CodeUnit* end; +}; +template +constexpr const size_t codecvt_result::max_size; + +template +void write_codecvt(codecvt_result& out, string_view in_buf, + const std::locale& loc) { + using codecvt = std::codecvt; +#if FMT_CLANG_VERSION +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated" + auto& f = std::use_facet(loc); +# pragma clang diagnostic pop #else - using code_unit = char32_t; + auto& f = std::use_facet(loc); #endif - auto& f = std::use_facet>(loc); auto mb = std::mbstate_t(); const char* from_next = nullptr; - code_unit* to_next = nullptr; - constexpr size_t buf_size = 32; - code_unit buf[buf_size] = {}; - auto result = f.in(mb, str.data(), str.data() + str.size(), from_next, buf, - buf + buf_size, to_next); + auto result = f.in(mb, in_buf.begin(), in_buf.end(), from_next, + std::begin(out.buf), std::end(out.buf), out.end); if (result != std::codecvt_base::ok) FMT_THROW(format_error("failed to format time")); - str.clear(); - for (code_unit* p = buf; p != to_next; ++p) { - uint32_t c = static_cast(*p); - if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) { - // surrogate pair - ++p; - if (p == to_next || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) { +} + +template +auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) + -> OutputIt { + if (detail::is_utf8() && loc != get_classic_locale()) { + // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and + // gcc-4. +#if FMT_MSC_VER != 0 || \ + (defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI)) + // The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5 + // and newer. + using code_unit = wchar_t; +#else + using code_unit = char32_t; +#endif + + using unit_t = codecvt_result; + unit_t unit; + write_codecvt(unit, in, loc); + // In UTF-8 is used one to four one-byte code units. + auto&& buf = basic_memory_buffer(); + for (code_unit* p = unit.buf; p != unit.end; ++p) { + uint32_t c = static_cast(*p); + if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) { + // surrogate pair + ++p; + if (p == unit.end || (c & 0xfc00) != 0xd800 || + (*p & 0xfc00) != 0xdc00) { + FMT_THROW(format_error("failed to format time")); + } + c = (c << 10) + static_cast(*p) - 0x35fdc00; + } + if (c < 0x80) { + buf.push_back(static_cast(c)); + } else if (c < 0x800) { + buf.push_back(static_cast(0xc0 | (c >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { + buf.push_back(static_cast(0xe0 | (c >> 12))); + buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else if (c >= 0x10000 && c <= 0x10ffff) { + buf.push_back(static_cast(0xf0 | (c >> 18))); + buf.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12))); + buf.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); + buf.push_back(static_cast(0x80 | (c & 0x3f))); + } else { FMT_THROW(format_error("failed to format time")); } - c = (c << 10) + static_cast(*p) - 0x35fdc00; - } - if (c < 0x80) { - str.push_back(static_cast(c)); - } else if (c < 0x800) { - str.push_back(static_cast(0xc0 | (c >> 6))); - str.push_back(static_cast(0x80 | (c & 0x3f))); - } else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { - str.push_back(static_cast(0xe0 | (c >> 12))); - str.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); - str.push_back(static_cast(0x80 | (c & 0x3f))); - } else if (c >= 0x10000 && c <= 0x10ffff) { - str.push_back(static_cast(0xf0 | (c >> 18))); - str.push_back(static_cast(0x80 | ((c & 0x3ffff) >> 12))); - str.push_back(static_cast(0x80 | ((c & 0xfff) >> 6))); - str.push_back(static_cast(0x80 | (c & 0x3f))); - } else { - FMT_THROW(format_error("failed to format time")); } + return copy_str(buf.data(), buf.data() + buf.size(), out); } - return str; + return copy_str(in.data(), in.data() + in.size(), out); } -template +template ::value)> +auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) + -> OutputIt { + codecvt_result unit; + write_codecvt(unit, sv, loc); + return copy_str(unit.buf, unit.end, out); +} + +template ::value)> +auto write_tm_str(OutputIt out, string_view sv, const std::locale& loc) + -> OutputIt { + return write_encoded_tm_str(out, sv, loc); +} + +template +inline void do_write(buffer& buf, const std::tm& time, + const std::locale& loc, char format, char modifier) { + auto&& format_buf = formatbuf>(buf); + auto&& os = std::basic_ostream(&format_buf); + os.imbue(loc); + using iterator = std::ostreambuf_iterator; + const auto& facet = std::use_facet>(loc); + auto end = facet.put(os, os, Char(' '), &time, format, modifier); + if (end.failed()) FMT_THROW(format_error("failed to format time")); +} + +template ::value)> +auto write(OutputIt out, const std::tm& time, const std::locale& loc, + char format, char modifier = 0) -> OutputIt { + auto&& buf = get_buffer(out); + do_write(buf, time, loc, format, modifier); + return buf.out(); +} + +template ::value)> auto write(OutputIt out, const std::tm& time, const std::locale& loc, char format, char modifier = 0) -> OutputIt { - auto str = do_write(time, loc, format, modifier); - return std::copy(str.begin(), str.end(), out); + auto&& buf = basic_memory_buffer(); + do_write(buf, time, loc, format, modifier); + return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); } + } // namespace detail FMT_MODULE_EXPORT_BEGIN @@ -453,102 +535,39 @@ inline std::tm gmtime( FMT_BEGIN_DETAIL_NAMESPACE -inline size_t strftime(char* str, size_t count, const char* format, - const std::tm* time) { - // Assign to a pointer to suppress GCCs -Wformat-nonliteral - // First assign the nullptr to suppress -Wsuggest-attribute=format - std::size_t (*strftime)(char*, std::size_t, const char*, const std::tm*) = - nullptr; - strftime = std::strftime; - return strftime(str, count, format, time); -} - -inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format, - const std::tm* time) { - // See above - std::size_t (*wcsftime)(wchar_t*, std::size_t, const wchar_t*, - const std::tm*) = nullptr; - wcsftime = std::wcsftime; - return wcsftime(str, count, format, time); -} - -FMT_END_DETAIL_NAMESPACE - -template -struct formatter, - Char> : formatter { - FMT_CONSTEXPR formatter() { - this->specs = {default_specs, sizeof(default_specs) / sizeof(Char)}; - } - - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - auto it = ctx.begin(); - if (it != ctx.end() && *it == ':') ++it; - auto end = it; - while (end != ctx.end() && *end != '}') ++end; - if (end != it) this->specs = {it, detail::to_unsigned(end - it)}; - return end; - } - - template - auto format(std::chrono::time_point val, - FormatContext& ctx) -> decltype(ctx.out()) { - std::tm time = localtime(val); - return formatter::format(time, ctx); - } - - static constexpr Char default_specs[] = {'%', 'Y', '-', '%', 'm', '-', - '%', 'd', ' ', '%', 'H', ':', - '%', 'M', ':', '%', 'S'}; -}; - -template -constexpr Char - formatter, - Char>::default_specs[]; - -template struct formatter { - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - auto it = ctx.begin(); - if (it != ctx.end() && *it == ':') ++it; - auto end = it; - while (end != ctx.end() && *end != '}') ++end; - specs = {it, detail::to_unsigned(end - it)}; - return end; - } - - template - auto format(const std::tm& tm, FormatContext& ctx) const - -> decltype(ctx.out()) { - basic_memory_buffer tm_format; - tm_format.append(specs.begin(), specs.end()); - // By appending an extra space we can distinguish an empty result that - // indicates insufficient buffer size from a guaranteed non-empty result - // https://github.com/fmtlib/fmt/issues/2238 - tm_format.push_back(' '); - tm_format.push_back('\0'); - basic_memory_buffer buf; - size_t start = buf.size(); - for (;;) { - size_t size = buf.capacity() - start; - size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm); - if (count != 0) { - buf.resize(start + count); - break; - } - const size_t MIN_GROWTH = 10; - buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); - } - // Remove the extra space. - return std::copy(buf.begin(), buf.end() - 1, ctx.out()); +// Writes two-digit numbers a, b and c separated by sep to buf. +// The method by Pavel Novikov based on +// https://johnnylee-sde.github.io/Fast-unsigned-integer-to-time-string/. +inline void write_digit2_separated(char* buf, unsigned a, unsigned b, + unsigned c, char sep) { + unsigned long long digits = + a | (b << 24) | (static_cast(c) << 48); + // Convert each value to BCD. + // We have x = a * 10 + b and we want to convert it to BCD y = a * 16 + b. + // The difference is + // y - x = a * 6 + // a can be found from x: + // a = floor(x / 10) + // then + // y = x + a * 6 = x + floor(x / 10) * 6 + // floor(x / 10) is (x * 205) >> 11 (needs 16 bits). + digits += (((digits * 205) >> 11) & 0x000f00000f00000f) * 6; + // Put low nibbles to high bytes and high nibbles to low bytes. + digits = ((digits & 0x00f00000f00000f0) >> 4) | + ((digits & 0x000f00000f00000f) << 8); + auto usep = static_cast(sep); + // Add ASCII '0' to each digit byte and insert separators. + digits |= 0x3030003030003030 | (usep << 16) | (usep << 40); + + constexpr const size_t len = 8; + if (const_check(is_big_endian())) { + char tmp[len]; + memcpy(tmp, &digits, len); + std::reverse_copy(tmp, tmp + len, buf); + } else { + memcpy(buf, &digits, len); } - - basic_string_view specs; -}; - -FMT_BEGIN_DETAIL_NAMESPACE +} template FMT_CONSTEXPR inline const char* get_units() { if (std::is_same::value) return "as"; @@ -610,6 +629,22 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, handler.on_text(tab, tab + 1); break; } + // Year: + case 'Y': + handler.on_year(numeric_system::standard); + break; + case 'y': + handler.on_short_year(numeric_system::standard); + break; + case 'C': + handler.on_century(numeric_system::standard); + break; + case 'G': + handler.on_iso_week_based_year(); + break; + case 'g': + handler.on_iso_week_based_short_year(); + break; // Day of the week: case 'a': handler.on_abbr_weekday(); @@ -625,11 +660,34 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, break; // Month: case 'b': + case 'h': handler.on_abbr_month(); break; case 'B': handler.on_full_month(); break; + case 'm': + handler.on_dec_month(numeric_system::standard); + break; + // Day of the year/month: + case 'U': + handler.on_dec0_week_of_year(numeric_system::standard); + break; + case 'W': + handler.on_dec1_week_of_year(numeric_system::standard); + break; + case 'V': + handler.on_iso_week_of_year(numeric_system::standard); + break; + case 'j': + handler.on_day_of_year(); + break; + case 'd': + handler.on_day_of_month(numeric_system::standard); + break; + case 'e': + handler.on_day_of_month_space(numeric_system::standard); + break; // Hour, minute, second: case 'H': handler.on_24_hour(numeric_system::standard); @@ -688,6 +746,15 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; switch (c) { + case 'Y': + handler.on_year(numeric_system::alternative); + break; + case 'y': + handler.on_offset_year(); + break; + case 'C': + handler.on_century(numeric_system::alternative); + break; case 'c': handler.on_datetime(numeric_system::alternative); break; @@ -706,6 +773,27 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin, if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; switch (c) { + case 'y': + handler.on_short_year(numeric_system::alternative); + break; + case 'm': + handler.on_dec_month(numeric_system::alternative); + break; + case 'U': + handler.on_dec0_week_of_year(numeric_system::alternative); + break; + case 'W': + handler.on_dec1_week_of_year(numeric_system::alternative); + break; + case 'V': + handler.on_iso_week_of_year(numeric_system::alternative); + break; + case 'd': + handler.on_day_of_month(numeric_system::alternative); + break; + case 'e': + handler.on_day_of_month_space(numeric_system::alternative); + break; case 'w': handler.on_dec0_weekday(numeric_system::alternative); break; @@ -741,12 +829,25 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void unsupported() { static_cast(this)->unsupported(); } + FMT_CONSTEXPR void on_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_short_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_offset_year() { unsupported(); } + FMT_CONSTEXPR void on_century(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_iso_week_based_year() { unsupported(); } + FMT_CONSTEXPR void on_iso_week_based_short_year() { unsupported(); } FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); } FMT_CONSTEXPR void on_full_weekday() { unsupported(); } FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_abbr_month() { unsupported(); } FMT_CONSTEXPR void on_full_month() { unsupported(); } + FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_day_of_year() { unsupported(); } + FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } @@ -766,6 +867,509 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void on_tz_name() { unsupported(); } }; +struct tm_format_checker : null_chrono_spec_handler { + FMT_NORETURN void unsupported() { FMT_THROW(format_error("no format")); } + + template + FMT_CONSTEXPR void on_text(const Char*, const Char*) {} + FMT_CONSTEXPR void on_year(numeric_system) {} + FMT_CONSTEXPR void on_short_year(numeric_system) {} + FMT_CONSTEXPR void on_offset_year() {} + FMT_CONSTEXPR void on_century(numeric_system) {} + FMT_CONSTEXPR void on_iso_week_based_year() {} + FMT_CONSTEXPR void on_iso_week_based_short_year() {} + FMT_CONSTEXPR void on_abbr_weekday() {} + FMT_CONSTEXPR void on_full_weekday() {} + FMT_CONSTEXPR void on_dec0_weekday(numeric_system) {} + FMT_CONSTEXPR void on_dec1_weekday(numeric_system) {} + FMT_CONSTEXPR void on_abbr_month() {} + FMT_CONSTEXPR void on_full_month() {} + FMT_CONSTEXPR void on_dec_month(numeric_system) {} + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_day_of_year() {} + FMT_CONSTEXPR void on_day_of_month(numeric_system) {} + FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {} + FMT_CONSTEXPR void on_24_hour(numeric_system) {} + FMT_CONSTEXPR void on_12_hour(numeric_system) {} + FMT_CONSTEXPR void on_minute(numeric_system) {} + FMT_CONSTEXPR void on_second(numeric_system) {} + FMT_CONSTEXPR void on_datetime(numeric_system) {} + FMT_CONSTEXPR void on_loc_date(numeric_system) {} + FMT_CONSTEXPR void on_loc_time(numeric_system) {} + FMT_CONSTEXPR void on_us_date() {} + FMT_CONSTEXPR void on_iso_date() {} + FMT_CONSTEXPR void on_12_hour_time() {} + FMT_CONSTEXPR void on_24_hour_time() {} + FMT_CONSTEXPR void on_iso_time() {} + FMT_CONSTEXPR void on_am_pm() {} + FMT_CONSTEXPR void on_utc_offset() {} + FMT_CONSTEXPR void on_tz_name() {} +}; + +inline const char* tm_wday_full_name(int wday) { + static constexpr const char* full_name_list[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday"}; + return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?"; +} +inline const char* tm_wday_short_name(int wday) { + static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"}; + return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???"; +} + +inline const char* tm_mon_full_name(int mon) { + static constexpr const char* full_name_list[] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"}; + return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?"; +} +inline const char* tm_mon_short_name(int mon) { + static constexpr const char* short_name_list[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + return mon >= 0 && mon <= 11 ? short_name_list[mon] : "???"; +} + +template +struct has_member_data_tm_gmtoff : std::false_type {}; +template +struct has_member_data_tm_gmtoff> + : std::true_type {}; + +template +struct has_member_data_tm_zone : std::false_type {}; +template +struct has_member_data_tm_zone> + : std::true_type {}; + +#if FMT_USE_TZSET +inline void tzset_once() { + static bool init = []() -> bool { + _tzset(); + return true; + }(); + ignore_unused(init); +} +#endif + +template class tm_writer { + private: + static constexpr int days_per_week = 7; + + const std::locale& loc_; + const bool is_classic_; + OutputIt out_; + const std::tm& tm_; + + auto tm_sec() const noexcept -> int { + FMT_ASSERT(tm_.tm_sec >= 0 && tm_.tm_sec <= 61, ""); + return tm_.tm_sec; + } + auto tm_min() const noexcept -> int { + FMT_ASSERT(tm_.tm_min >= 0 && tm_.tm_min <= 59, ""); + return tm_.tm_min; + } + auto tm_hour() const noexcept -> int { + FMT_ASSERT(tm_.tm_hour >= 0 && tm_.tm_hour <= 23, ""); + return tm_.tm_hour; + } + auto tm_mday() const noexcept -> int { + FMT_ASSERT(tm_.tm_mday >= 1 && tm_.tm_mday <= 31, ""); + return tm_.tm_mday; + } + auto tm_mon() const noexcept -> int { + FMT_ASSERT(tm_.tm_mon >= 0 && tm_.tm_mon <= 11, ""); + return tm_.tm_mon; + } + auto tm_year() const noexcept -> long long { return 1900ll + tm_.tm_year; } + auto tm_wday() const noexcept -> int { + FMT_ASSERT(tm_.tm_wday >= 0 && tm_.tm_wday <= 6, ""); + return tm_.tm_wday; + } + auto tm_yday() const noexcept -> int { + FMT_ASSERT(tm_.tm_yday >= 0 && tm_.tm_yday <= 365, ""); + return tm_.tm_yday; + } + + auto tm_hour12() const noexcept -> int { + const auto h = tm_hour(); + const auto z = h < 12 ? h : h - 12; + return z == 0 ? 12 : z; + } + + // POSIX and the C Standard are unclear or inconsistent about what %C and %y + // do if the year is negative or exceeds 9999. Use the convention that %C + // concatenated with %y yields the same output as %Y, and that %Y contains at + // least 4 characters, with more only if necessary. + auto split_year_lower(long long year) const noexcept -> int { + auto l = year % 100; + if (l < 0) l = -l; // l in [0, 99] + return static_cast(l); + } + + // Algorithm: + // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date + auto iso_year_weeks(long long curr_year) const noexcept -> int { + const auto prev_year = curr_year - 1; + const auto curr_p = + (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % + days_per_week; + const auto prev_p = + (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % + days_per_week; + return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0); + } + auto iso_week_num(int tm_yday, int tm_wday) const noexcept -> int { + return (tm_yday + 11 - (tm_wday == 0 ? days_per_week : tm_wday)) / + days_per_week; + } + auto tm_iso_week_year() const noexcept -> long long { + const auto year = tm_year(); + const auto w = iso_week_num(tm_yday(), tm_wday()); + if (w < 1) return year - 1; + if (w > iso_year_weeks(year)) return year + 1; + return year; + } + auto tm_iso_week_of_year() const noexcept -> int { + const auto year = tm_year(); + const auto w = iso_week_num(tm_yday(), tm_wday()); + if (w < 1) return iso_year_weeks(year - 1); + if (w > iso_year_weeks(year)) return 1; + return w; + } + + void write1(int value) { + *out_++ = static_cast('0' + to_unsigned(value) % 10); + } + void write2(int value) { + const char* d = digits2(to_unsigned(value) % 100); + *out_++ = *d++; + *out_++ = *d; + } + + void write_year_extended(long long year) { + // At least 4 characters. + int width = 4; + if (year < 0) { + *out_++ = '-'; + year = 0 - year; + --width; + } + uint32_or_64_or_128_t n = to_unsigned(year); + const int num_digits = count_digits(n); + if (width > num_digits) out_ = std::fill_n(out_, width - num_digits, '0'); + out_ = format_decimal(out_, n, num_digits).end; + } + void write_year(long long year) { + if (year >= 0 && year < 10000) { + write2(static_cast(year / 100)); + write2(static_cast(year % 100)); + } else { + write_year_extended(year); + } + } + + void write_utc_offset(long offset) { + if (offset < 0) { + *out_++ = '-'; + offset = -offset; + } else { + *out_++ = '+'; + } + offset /= 60; + write2(static_cast(offset / 60)); + write2(static_cast(offset % 60)); + } + template ::value)> + void format_utc_offset_impl(const T& tm) { + write_utc_offset(tm.tm_gmtoff); + } + template ::value)> + void format_utc_offset_impl(const T& tm) { +#if defined(_WIN32) && defined(_UCRT) +# if FMT_USE_TZSET + tzset_once(); +# endif + long offset = 0; + _get_timezone(&offset); + if (tm.tm_isdst) { + long dstbias = 0; + _get_dstbias(&dstbias); + offset += dstbias; + } + write_utc_offset(-offset); +#else + ignore_unused(tm); + format_localized('z'); +#endif + } + + template ::value)> + void format_tz_name_impl(const T& tm) { + if (is_classic_) + out_ = write_tm_str(out_, tm.tm_zone, loc_); + else + format_localized('Z'); + } + template ::value)> + void format_tz_name_impl(const T&) { + format_localized('Z'); + } + + void format_localized(char format, char modifier = 0) { + out_ = write(out_, tm_, loc_, format, modifier); + } + + public: + tm_writer(const std::locale& loc, OutputIt out, const std::tm& tm) + : loc_(loc), + is_classic_(loc_ == get_classic_locale()), + out_(out), + tm_(tm) {} + + OutputIt out() const { return out_; } + + FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { + out_ = copy_str(begin, end, out_); + } + + void on_abbr_weekday() { + if (is_classic_) + out_ = write(out_, tm_wday_short_name(tm_wday())); + else + format_localized('a'); + } + void on_full_weekday() { + if (is_classic_) + out_ = write(out_, tm_wday_full_name(tm_wday())); + else + format_localized('A'); + } + void on_dec0_weekday(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write1(tm_wday()); + format_localized('w', 'O'); + } + void on_dec1_weekday(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto wday = tm_wday(); + write1(wday == 0 ? days_per_week : wday); + } else { + format_localized('u', 'O'); + } + } + + void on_abbr_month() { + if (is_classic_) + out_ = write(out_, tm_mon_short_name(tm_mon())); + else + format_localized('b'); + } + void on_full_month() { + if (is_classic_) + out_ = write(out_, tm_mon_full_name(tm_mon())); + else + format_localized('B'); + } + + void on_datetime(numeric_system ns) { + if (is_classic_) { + on_abbr_weekday(); + *out_++ = ' '; + on_abbr_month(); + *out_++ = ' '; + on_day_of_month_space(numeric_system::standard); + *out_++ = ' '; + on_iso_time(); + *out_++ = ' '; + on_year(numeric_system::standard); + } else { + format_localized('c', ns == numeric_system::standard ? '\0' : 'E'); + } + } + void on_loc_date(numeric_system ns) { + if (is_classic_) + on_us_date(); + else + format_localized('x', ns == numeric_system::standard ? '\0' : 'E'); + } + void on_loc_time(numeric_system ns) { + if (is_classic_) + on_iso_time(); + else + format_localized('X', ns == numeric_system::standard ? '\0' : 'E'); + } + void on_us_date() { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_mon() + 1), + to_unsigned(tm_mday()), + to_unsigned(split_year_lower(tm_year())), '/'); + out_ = copy_str(std::begin(buf), std::end(buf), out_); + } + void on_iso_date() { + auto year = tm_year(); + char buf[10]; + size_t offset = 0; + if (year >= 0 && year < 10000) { + copy2(buf, digits2(to_unsigned(year / 100))); + } else { + offset = 4; + write_year_extended(year); + year = 0; + } + write_digit2_separated(buf + 2, static_cast(year % 100), + to_unsigned(tm_mon() + 1), to_unsigned(tm_mday()), + '-'); + out_ = copy_str(std::begin(buf) + offset, std::end(buf), out_); + } + + void on_utc_offset() { format_utc_offset_impl(tm_); } + void on_tz_name() { format_tz_name_impl(tm_); } + + void on_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write_year(tm_year()); + format_localized('Y', 'E'); + } + void on_short_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(split_year_lower(tm_year())); + format_localized('y', 'O'); + } + void on_offset_year() { + if (is_classic_) return write2(split_year_lower(tm_year())); + format_localized('y', 'E'); + } + + void on_century(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto year = tm_year(); + auto upper = year / 100; + if (year >= -99 && year < 0) { + // Zero upper on negative year. + *out_++ = '-'; + *out_++ = '0'; + } else if (upper >= 0 && upper < 100) { + write2(static_cast(upper)); + } else { + out_ = write(out_, upper); + } + } else { + format_localized('C', 'E'); + } + } + + void on_dec_month(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_mon() + 1); + format_localized('m', 'O'); + } + + void on_dec0_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week); + format_localized('U', 'O'); + } + void on_dec1_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto wday = tm_wday(); + write2((tm_yday() + days_per_week - + (wday == 0 ? (days_per_week - 1) : (wday - 1))) / + days_per_week); + } else { + format_localized('W', 'O'); + } + } + void on_iso_week_of_year(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_iso_week_of_year()); + format_localized('V', 'O'); + } + + void on_iso_week_based_year() { write_year(tm_iso_week_year()); } + void on_iso_week_based_short_year() { + write2(split_year_lower(tm_iso_week_year())); + } + + void on_day_of_year() { + auto yday = tm_yday() + 1; + write1(yday / 100); + write2(yday % 100); + } + void on_day_of_month(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday()); + format_localized('d', 'O'); + } + void on_day_of_month_space(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) { + auto mday = to_unsigned(tm_mday()) % 100; + const char* d2 = digits2(mday); + *out_++ = mday < 10 ? ' ' : d2[0]; + *out_++ = d2[1]; + } else { + format_localized('e', 'O'); + } + } + + void on_24_hour(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_hour()); + format_localized('H', 'O'); + } + void on_12_hour(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_hour12()); + format_localized('I', 'O'); + } + void on_minute(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_min()); + format_localized('M', 'O'); + } + void on_second(numeric_system ns) { + if (is_classic_ || ns == numeric_system::standard) return write2(tm_sec()); + format_localized('S', 'O'); + } + + void on_12_hour_time() { + if (is_classic_) { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_hour12()), + to_unsigned(tm_min()), to_unsigned(tm_sec()), ':'); + out_ = copy_str(std::begin(buf), std::end(buf), out_); + *out_++ = ' '; + on_am_pm(); + } else { + format_localized('r'); + } + } + void on_24_hour_time() { + write2(tm_hour()); + *out_++ = ':'; + write2(tm_min()); + } + void on_iso_time() { + char buf[8]; + write_digit2_separated(buf, to_unsigned(tm_hour()), to_unsigned(tm_min()), + to_unsigned(tm_sec()), ':'); + out_ = copy_str(std::begin(buf), std::end(buf), out_); + } + + void on_am_pm() { + if (is_classic_) { + *out_++ = tm_hour() < 12 ? 'A' : 'P'; + *out_++ = 'M'; + } else { + format_localized('p'); + } + } + + // These apply to chrono durations but not tm. + void on_duration_value() {} + void on_duration_unit() {} +}; + struct chrono_format_checker : null_chrono_spec_handler { FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); } @@ -796,26 +1400,20 @@ template ::value)> inline bool isfinite(T) { return true; } -template ::value)> -inline bool isfinite(T value) { - return std::isfinite(value); -} -// Converts value to int and checks that it's in the range [0, upper). -template ::value)> -inline int to_nonnegative_int(T value, int upper) { +// Converts value to Int and checks that it's in the range [0, upper). +template ::value)> +inline Int to_nonnegative_int(T value, Int upper) { FMT_ASSERT(value >= 0 && to_unsigned(value) <= to_unsigned(upper), "invalid value"); (void)upper; - return static_cast(value); + return static_cast(value); } -template ::value)> -inline int to_nonnegative_int(T value, int upper) { - FMT_ASSERT( - std::isnan(value) || (value >= 0 && value <= static_cast(upper)), - "invalid value"); - (void)upper; - return static_cast(value); +template ::value)> +inline Int to_nonnegative_int(T value, Int upper) { + if (value < 0 || value > static_cast(upper)) + FMT_THROW(format_error("invalid value")); + return static_cast(value); } template ::value)> @@ -872,15 +1470,37 @@ inline std::chrono::duration get_milliseconds( #endif } -template ::value)> -inline std::chrono::duration get_milliseconds( +// Returns the number of fractional digits in the range [0, 18] according to the +// C++20 spec. If more than 18 fractional digits are required then returns 6 for +// microseconds precision. +constexpr int count_fractional_digits(long long num, long long den, int n = 0) { + return num % den == 0 + ? n + : (n > 18 ? 6 : count_fractional_digits(num * 10, den, n + 1)); +} + +constexpr long long pow10(std::uint32_t n) { + return n == 0 ? 1 : 10 * pow10(n - 1); +} + +template ::is_signed)> +constexpr std::chrono::duration abs( + std::chrono::duration d) { + // We need to compare the duration using the count() method directly + // due to a compiler bug in clang-11 regarding the spaceship operator, + // when -Wzero-as-null-pointer-constant is enabled. + // In clang-12 the bug has been fixed. See + // https://bugs.llvm.org/show_bug.cgi?id=46235 and the reproducible example: + // https://www.godbolt.org/z/Knbb5joYx. + return d.count() >= d.zero().count() ? d : -d; +} + +template ::is_signed)> +constexpr std::chrono::duration abs( std::chrono::duration d) { - using common_type = typename std::common_type::type; - auto ms = mod(d.count() * static_cast(Period::num) / - static_cast(Period::den) * 1000, - 1000); - return std::chrono::duration(static_cast(ms)); + return d; } template (); specs.precision = precision; - specs.type = precision > 0 ? 'f' : 'g'; + specs.type = precision >= 0 ? presentation_type::fixed_lower + : presentation_type::general_lower; return write(out, val, specs); } @@ -926,6 +1547,26 @@ OutputIt format_duration_unit(OutputIt out) { return out; } +class get_locale { + private: + union { + std::locale locale_; + }; + bool has_locale_ = false; + + public: + get_locale(bool localized, locale_ref loc) : has_locale_(localized) { + if (localized) + ::new (&locale_) std::locale(loc.template get()); + } + ~get_locale() { + if (has_locale_) locale_.~locale(); + } + operator const std::locale&() const { + return has_locale_ ? locale_ : get_classic_locale(); + } +}; + template struct chrono_formatter { @@ -944,9 +1585,10 @@ struct chrono_formatter { bool negative; using char_type = typename FormatContext::char_type; + using tm_writer_type = tm_writer; - explicit chrono_formatter(FormatContext& ctx, OutputIt o, - std::chrono::duration d) + chrono_formatter(FormatContext& ctx, OutputIt o, + std::chrono::duration d) : context(ctx), out(o), val(static_cast(d.count())), @@ -1021,15 +1663,48 @@ struct chrono_formatter { out = format_decimal(out, n, num_digits).end; } + template void write_fractional_seconds(Duration d) { + constexpr auto num_fractional_digits = + count_fractional_digits(Duration::period::num, Duration::period::den); + + using subsecond_precision = std::chrono::duration< + typename std::common_type::type, + std::ratio<1, detail::pow10(num_fractional_digits)>>; + if (std::ratio_less::value) { + *out++ = '.'; + // Don't convert long double to integer seconds to avoid overflow. + using sec = conditional_t< + std::is_same::value, + std::chrono::duration, std::chrono::seconds>; + auto fractional = detail::abs(d) - std::chrono::duration_cast(d); + const auto subseconds = + std::chrono::treat_as_floating_point< + typename subsecond_precision::rep>::value + ? fractional.count() + : std::chrono::duration_cast(fractional) + .count(); + uint32_or_64_or_128_t n = + to_unsigned(to_nonnegative_int(subseconds, max_value())); + int num_digits = detail::count_digits(n); + if (num_fractional_digits > num_digits) + out = std::fill_n(out, num_fractional_digits - num_digits, '0'); + out = format_decimal(out, n, num_digits).end; + } + } + void write_nan() { std::copy_n("nan", 3, out); } void write_pinf() { std::copy_n("inf", 3, out); } void write_ninf() { std::copy_n("-inf", 4, out); } - void format_localized(const tm& time, char format, char modifier = 0) { + template + void format_tm(const tm& time, Callback cb, Args... args) { if (isnan(val)) return write_nan(); - const auto& loc = localized ? context.locale().template get() - : std::locale::classic(); - out = detail::write(out, time, loc, format, modifier); + get_locale loc(localized, context.locale()); + auto w = tm_writer_type(loc, out, time); + (w.*cb)(args...); + out = w.out(); } void on_text(const char_type* begin, const char_type* end) { @@ -1050,6 +1725,19 @@ struct chrono_formatter { void on_iso_date() {} void on_utc_offset() {} void on_tz_name() {} + void on_year(numeric_system) {} + void on_short_year(numeric_system) {} + void on_offset_year() {} + void on_century(numeric_system) {} + void on_iso_week_based_year() {} + void on_iso_week_based_short_year() {} + void on_dec_month(numeric_system) {} + void on_dec0_week_of_year(numeric_system) {} + void on_dec1_week_of_year(numeric_system) {} + void on_iso_week_of_year(numeric_system) {} + void on_day_of_year() {} + void on_day_of_month(numeric_system) {} + void on_day_of_month_space(numeric_system) {} void on_24_hour(numeric_system ns) { if (handle_nan_inf()) return; @@ -1057,7 +1745,7 @@ struct chrono_formatter { if (ns == numeric_system::standard) return write(hour(), 2); auto time = tm(); time.tm_hour = to_nonnegative_int(hour(), 24); - format_localized(time, 'H', 'O'); + format_tm(time, &tm_writer_type::on_24_hour, ns); } void on_12_hour(numeric_system ns) { @@ -1066,7 +1754,7 @@ struct chrono_formatter { if (ns == numeric_system::standard) return write(hour12(), 2); auto time = tm(); time.tm_hour = to_nonnegative_int(hour12(), 12); - format_localized(time, 'I', 'O'); + format_tm(time, &tm_writer_type::on_12_hour, ns); } void on_minute(numeric_system ns) { @@ -1075,7 +1763,7 @@ struct chrono_formatter { if (ns == numeric_system::standard) return write(minute(), 2); auto time = tm(); time.tm_min = to_nonnegative_int(minute(), 60); - format_localized(time, 'M', 'O'); + format_tm(time, &tm_writer_type::on_minute, ns); } void on_second(numeric_system ns) { @@ -1083,29 +1771,17 @@ struct chrono_formatter { if (ns == numeric_system::standard) { write(second(), 2); -#if FMT_SAFE_DURATION_CAST - // convert rep->Rep - using duration_rep = std::chrono::duration; - using duration_Rep = std::chrono::duration; - auto tmpval = fmt_safe_duration_cast(duration_rep{val}); -#else - auto tmpval = std::chrono::duration(val); -#endif - auto ms = get_milliseconds(tmpval); - if (ms != std::chrono::milliseconds(0)) { - *out++ = '.'; - write(ms.count(), 3); - } + write_fractional_seconds(std::chrono::duration{val}); return; } auto time = tm(); time.tm_sec = to_nonnegative_int(second(), 60); - format_localized(time, 'S', 'O'); + format_tm(time, &tm_writer_type::on_second, ns); } void on_12_hour_time() { if (handle_nan_inf()) return; - format_localized(time(), 'r'); + format_tm(time(), &tm_writer_type::on_12_hour_time); } void on_24_hour_time() { @@ -1124,12 +1800,12 @@ struct chrono_formatter { on_24_hour_time(); *out++ = ':'; if (handle_nan_inf()) return; - write(second(), 2); + on_second(numeric_system::standard); } void on_am_pm() { if (handle_nan_inf()) return; - format_localized(time(), 'p'); + format_tm(time(), &tm_writer_type::on_am_pm); } void on_duration_value() { @@ -1159,15 +1835,18 @@ class weekday { : value(static_cast(wd != 7 ? wd : 0)) {} constexpr unsigned c_encoding() const noexcept { return value; } }; + +class year_month_day {}; #endif // A rudimentary weekday formatter. -template <> struct formatter { +template struct formatter { private: bool localized = false; public: - FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { auto begin = ctx.begin(), end = ctx.end(); if (begin != end && *begin == 'L') { ++begin; @@ -1176,12 +1855,14 @@ template <> struct formatter { return begin; } - auto format(weekday wd, format_context& ctx) -> decltype(ctx.out()) { + template + auto format(weekday wd, FormatContext& ctx) const -> decltype(ctx.out()) { auto time = std::tm(); time.tm_wday = static_cast(wd.c_encoding()); - const auto& loc = localized ? ctx.locale().template get() - : std::locale::classic(); - return detail::write(ctx.out(), time, loc, 'a'); + detail::get_locale loc(localized, ctx.locale()); + auto w = detail::tm_writer(loc, ctx.out(), time); + w.on_abbr_weekday(); + return w.out(); } }; @@ -1260,7 +1941,8 @@ struct formatter, Char> { ++begin; localized = true; } - end = parse_chrono_format(begin, end, detail::chrono_format_checker()); + end = detail::parse_chrono_format(begin, end, + detail::chrono_format_checker()); return {begin, end}; } @@ -1302,6 +1984,83 @@ struct formatter, Char> { } }; +template +struct formatter, + Char> : formatter { + FMT_CONSTEXPR formatter() { + this->do_parse(default_specs, + default_specs + sizeof(default_specs) / sizeof(Char)); + } + + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return this->do_parse(ctx.begin(), ctx.end(), true); + } + + template + auto format(std::chrono::time_point val, + FormatContext& ctx) const -> decltype(ctx.out()) { + return formatter::format(localtime(val), ctx); + } + + static constexpr const Char default_specs[] = {'%', 'F', ' ', '%', 'T'}; +}; + +template +constexpr const Char + formatter, + Char>::default_specs[]; + +template struct formatter { + private: + enum class spec { + unknown, + year_month_day, + hh_mm_ss, + }; + spec spec_ = spec::unknown; + basic_string_view specs; + + protected: + template + FMT_CONSTEXPR auto do_parse(It begin, It end, bool with_default = false) + -> It { + if (begin != end && *begin == ':') ++begin; + end = detail::parse_chrono_format(begin, end, detail::tm_format_checker()); + if (!with_default || end != begin) + specs = {begin, detail::to_unsigned(end - begin)}; + // basic_string_view<>::compare isn't constexpr before C++17. + if (specs.size() == 2 && specs[0] == Char('%')) { + if (specs[1] == Char('F')) + spec_ = spec::year_month_day; + else if (specs[1] == Char('T')) + spec_ = spec::hh_mm_ss; + } + return end; + } + + public: + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return this->do_parse(ctx.begin(), ctx.end()); + } + + template + auto format(const std::tm& tm, FormatContext& ctx) const + -> decltype(ctx.out()) { + const auto loc_ref = ctx.locale(); + detail::get_locale loc(static_cast(loc_ref), loc_ref); + auto w = detail::tm_writer(loc, ctx.out(), tm); + if (spec_ == spec::year_month_day) + w.on_iso_date(); + else if (spec_ == spec::hh_mm_ss) + w.on_iso_time(); + else + detail::parse_chrono_format(specs.begin(), specs.end(), w); + return w.out(); + } +}; + FMT_MODULE_EXPORT_END FMT_END_NAMESPACE diff --git a/include/spdlog/fmt/bundled/color.h b/include/spdlog/fmt/bundled/color.h index 3d5490e8..dfbe4829 100644 --- a/include/spdlog/fmt/bundled/color.h +++ b/include/spdlog/fmt/bundled/color.h @@ -185,9 +185,13 @@ enum class terminal_color : uint8_t { enum class emphasis : uint8_t { bold = 1, - italic = 1 << 1, - underline = 1 << 2, - strikethrough = 1 << 3 + faint = 1 << 1, + italic = 1 << 2, + underline = 1 << 3, + blink = 1 << 4, + reverse = 1 << 5, + conceal = 1 << 6, + strikethrough = 1 << 7, }; // rgb is a struct for red, green and blue colors. @@ -409,16 +413,18 @@ template struct ansi_color_escape { buffer[19] = static_cast(0); } FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { - uint8_t em_codes[4] = {}; - uint8_t em_bits = static_cast(em); - if (em_bits & static_cast(emphasis::bold)) em_codes[0] = 1; - if (em_bits & static_cast(emphasis::italic)) em_codes[1] = 3; - if (em_bits & static_cast(emphasis::underline)) em_codes[2] = 4; - if (em_bits & static_cast(emphasis::strikethrough)) - em_codes[3] = 9; + uint8_t em_codes[num_emphases] = {}; + if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1; + if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2; + if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3; + if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4; + if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5; + if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7; + if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; + if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; size_t index = 0; - for (int i = 0; i < 4; ++i) { + for (size_t i = 0; i < num_emphases; ++i) { if (!em_codes[i]) continue; buffer[index++] = static_cast('\x1b'); buffer[index++] = static_cast('['); @@ -435,7 +441,8 @@ template struct ansi_color_escape { } private: - Char buffer[7u + 3u * 4u + 1u]; + static constexpr size_t num_emphases = 8; + Char buffer[7u + 3u * num_emphases + 1u]; static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, char delimiter) FMT_NOEXCEPT { @@ -444,6 +451,10 @@ template struct ansi_color_escape { out[2] = static_cast('0' + c % 10); out[3] = static_cast(delimiter); } + static FMT_CONSTEXPR bool has_emphasis(emphasis em, + emphasis mask) FMT_NOEXCEPT { + return static_cast(em) & static_cast(mask); + } }; template diff --git a/include/spdlog/fmt/bundled/compile.h b/include/spdlog/fmt/bundled/compile.h index 00000c92..1dba3ddb 100644 --- a/include/spdlog/fmt/bundled/compile.h +++ b/include/spdlog/fmt/bundled/compile.h @@ -156,7 +156,7 @@ struct is_compiled_string : std::is_base_of {}; std::string s = fmt::format(FMT_COMPILE("{}"), 42); \endrst */ -#ifdef __cpp_if_constexpr +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) # define FMT_COMPILE(s) \ FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) #else @@ -179,7 +179,7 @@ const T& first(const T& value, const Tail&...) { return value; } -#ifdef __cpp_if_constexpr +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) template struct type_list {}; // Returns a reference to the argument at index N from [first, rest...]. @@ -190,7 +190,7 @@ constexpr const auto& get([[maybe_unused]] const T& first, if constexpr (N == 0) return first; else - return get(rest...); + return detail::get(rest...); } template @@ -202,7 +202,8 @@ constexpr int get_arg_index_by_name(basic_string_view name, template struct get_type_impl; template struct get_type_impl> { - using type = remove_cvref_t(std::declval()...))>; + using type = + remove_cvref_t(std::declval()...))>; }; template @@ -242,7 +243,7 @@ template struct code_unit { // This ensures that the argument type is convertible to `const T&`. template constexpr const T& get_arg_checked(const Args&... args) { - const auto& arg = get(args...); + const auto& arg = detail::get(args...); if constexpr (detail::is_named_arg>()) { return arg.value; } else { @@ -289,7 +290,7 @@ template struct runtime_named_field { constexpr OutputIt format(OutputIt out, const Args&... args) const { bool found = (try_format_argument(out, name, args) || ...); if (!found) { - throw format_error("argument with specified name is not found"); + FMT_THROW(format_error("argument with specified name is not found")); } return out; } @@ -399,7 +400,9 @@ template struct arg_id_handler { return 0; } - constexpr void on_error(const char* message) { throw format_error(message); } + constexpr void on_error(const char* message) { + FMT_THROW(format_error(message)); + } }; template struct parse_arg_id_result { @@ -451,7 +454,7 @@ constexpr auto compile_format_string(S format_str) { constexpr auto str = basic_string_view(format_str); if constexpr (str[POS] == '{') { if constexpr (POS + 1 == str.size()) - throw format_error("unmatched '{' in format string"); + FMT_THROW(format_error("unmatched '{' in format string")); if constexpr (str[POS + 1] == '{') { return parse_tail(make_text(str, POS, 1), format_str); } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { @@ -500,7 +503,7 @@ constexpr auto compile_format_string(S format_str) { } } else if constexpr (str[POS] == '}') { if constexpr (POS + 1 == str.size()) - throw format_error("unmatched '}' in format string"); + FMT_THROW(format_error("unmatched '}' in format string")); return parse_tail(make_text(str, POS, 1), format_str); } else { constexpr auto end = parse_text(str, POS + 1); @@ -527,12 +530,12 @@ constexpr auto compile(S format_str) { return result; } } -#endif // __cpp_if_constexpr +#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) } // namespace detail FMT_MODULE_EXPORT_BEGIN -#ifdef __cpp_if_constexpr +#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) template // std::FILE +#include // std::byte +#include // std::FILE #include #include #include @@ -16,29 +17,33 @@ #include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 80001 +#define FMT_VERSION 80101 -#ifdef __clang__ +#if defined(__clang__) && !defined(__ibmxl__) # define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__) #else # define FMT_CLANG_VERSION 0 #endif -#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) +#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && \ + !defined(__NVCOMPILER) # define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FMT_GCC_PRAGMA(arg) _Pragma(arg) #else # define FMT_GCC_VERSION 0 -# define FMT_GCC_PRAGMA(arg) #endif -#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__) -# define FMT_HAS_GXX_CXX11 FMT_GCC_VERSION -#else -# define FMT_HAS_GXX_CXX11 0 +#ifndef FMT_GCC_PRAGMA +// Workaround _Pragma bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59884. +# if FMT_GCC_VERSION >= 504 +# define FMT_GCC_PRAGMA(arg) _Pragma(arg) +# else +# define FMT_GCC_PRAGMA(arg) +# endif #endif -#if defined(__INTEL_COMPILER) +#ifdef __ICL +# define FMT_ICC_VERSION __ICL +#elif defined(__INTEL_COMPILER) # define FMT_ICC_VERSION __INTEL_COMPILER #else # define FMT_ICC_VERSION 0 @@ -78,17 +83,23 @@ # define FMT_HAS_CPP_ATTRIBUTE(x) 0 #endif +#ifdef _MSVC_LANG +# define FMT_CPLUSPLUS _MSVC_LANG +#else +# define FMT_CPLUSPLUS __cplusplus +#endif + #define FMT_HAS_CPP14_ATTRIBUTE(attribute) \ - (__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + (FMT_CPLUSPLUS >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute)) #define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ - (__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) + (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) // Check if relaxed C++14 constexpr is supported. // GCC doesn't allow throw in constexpr until version 6 (bug 67371). #ifndef FMT_USE_CONSTEXPR # define FMT_USE_CONSTEXPR \ - (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \ + (FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1912 || \ (FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \ !FMT_NVCC && !FMT_ICC_VERSION #endif @@ -100,6 +111,14 @@ # define FMT_CONSTEXPR_DECL #endif +#if ((__cplusplus >= 202002L) && \ + (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \ + (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) +# define FMT_CONSTEXPR20 constexpr +#else +# define FMT_CONSTEXPR20 +#endif + // Check if constexpr std::char_traits<>::compare,length is supported. #if defined(__GLIBCXX__) # if __cplusplus >= 201703L && defined(_GLIBCXX_RELEASE) && \ @@ -116,15 +135,6 @@ # define FMT_CONSTEXPR_CHAR_TRAITS #endif -#ifndef FMT_OVERRIDE -# if FMT_HAS_FEATURE(cxx_override_control) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 -# define FMT_OVERRIDE override -# else -# define FMT_OVERRIDE -# endif -#endif - // Check if exceptions are disabled. #ifndef FMT_EXCEPTIONS # if (defined(__GNUC__) && !defined(__EXCEPTIONS)) || \ @@ -141,7 +151,7 @@ #endif #if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900 + FMT_GCC_VERSION >= 408 || FMT_MSC_VER >= 1900 # define FMT_DETECTED_NOEXCEPT noexcept # define FMT_HAS_CXX11_NOEXCEPT 1 #else @@ -166,14 +176,6 @@ # define FMT_NORETURN #endif -#ifndef FMT_MAYBE_UNUSED -# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) -# define FMT_MAYBE_UNUSED [[maybe_unused]] -# else -# define FMT_MAYBE_UNUSED -# endif -#endif - #if __cplusplus == 201103L || __cplusplus == 201402L # if defined(__INTEL_COMPILER) || defined(__PGI) # define FMT_FALLTHROUGH @@ -185,13 +187,20 @@ # else # define FMT_FALLTHROUGH # endif -#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) || \ - (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#elif FMT_HAS_CPP17_ATTRIBUTE(fallthrough) # define FMT_FALLTHROUGH [[fallthrough]] #else # define FMT_FALLTHROUGH #endif +#ifndef FMT_NODISCARD +# if FMT_HAS_CPP17_ATTRIBUTE(nodiscard) +# define FMT_NODISCARD [[nodiscard]] +# else +# define FMT_NODISCARD +# endif +#endif + #ifndef FMT_USE_FLOAT # define FMT_USE_FLOAT 1 #endif @@ -210,31 +219,27 @@ # endif #endif -#ifndef FMT_USE_INLINE_NAMESPACES -# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \ - (FMT_MSC_VER >= 1900 && (!defined(_MANAGED) || !_MANAGED)) -# define FMT_USE_INLINE_NAMESPACES 1 +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 +# define FMT_DEPRECATED [[deprecated]] # else -# define FMT_USE_INLINE_NAMESPACES 0 +# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) +# define FMT_DEPRECATED __attribute__((deprecated)) +# elif FMT_MSC_VER +# define FMT_DEPRECATED __declspec(deprecated) +# else +# define FMT_DEPRECATED /* deprecated */ +# endif # endif #endif #ifndef FMT_BEGIN_NAMESPACE -# if FMT_USE_INLINE_NAMESPACES -# define FMT_INLINE_NAMESPACE inline namespace -# define FMT_END_NAMESPACE \ - } \ - } -# else -# define FMT_INLINE_NAMESPACE namespace -# define FMT_END_NAMESPACE \ - } \ - using namespace v8; \ - } -# endif # define FMT_BEGIN_NAMESPACE \ namespace fmt { \ - FMT_INLINE_NAMESPACE v8 { + inline namespace v8 { +# define FMT_END_NAMESPACE \ + } \ + } #endif #ifndef FMT_MODULE_EXPORT @@ -264,12 +269,6 @@ # define FMT_API #endif -#if FMT_GCC_VERSION -# define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden"))) -#else -# define FMT_GCC_VISIBILITY_HIDDEN -#endif - // libc++ supports string_view in pre-c++17. #if (FMT_HAS_INCLUDE() && \ (__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \ @@ -286,10 +285,11 @@ #endif #ifndef FMT_CONSTEVAL -# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ - __cplusplus > 201703L) || \ - (defined(__cpp_consteval) && \ - !FMT_MSC_VER) // consteval is broken in MSVC. +# if ((FMT_GCC_VERSION >= 1000 || FMT_CLANG_VERSION >= 1101) && \ + __cplusplus > 201703L && !defined(__apple_build_version__)) || \ + (defined(__cpp_consteval) && \ + (!FMT_MSC_VER || _MSC_FULL_VER >= 193030704)) +// consteval is broken in MSVC before VS2022 and Apple clang 13. # define FMT_CONSTEVAL consteval # define FMT_HAS_CONSTEVAL # else @@ -325,6 +325,8 @@ template using bool_constant = std::integral_constant; template using remove_reference_t = typename std::remove_reference::type; template +using remove_const_t = typename std::remove_const::type; +template using remove_cvref_t = typename std::remove_cv>::type; template struct type_identity { using type = T; }; template using type_identity_t = typename type_identity::type; @@ -333,11 +335,6 @@ struct monostate { constexpr monostate() {} }; -// Suppress "unused variable" warnings with the method described in -// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. -// (void)var does not work on many Intel compilers. -template FMT_CONSTEXPR void ignore_unused(const T&...) {} - // An enable_if helper to be used in template parameters which results in much // shorter symbols: https://godbolt.org/z/sWw4vP. Extra parentheses are needed // to workaround a bug in MSVC 2019 (see #1140 and #1186). @@ -349,16 +346,25 @@ template FMT_CONSTEXPR void ignore_unused(const T&...) {} FMT_BEGIN_DETAIL_NAMESPACE -constexpr FMT_INLINE auto is_constant_evaluated() FMT_NOEXCEPT -> bool { +// Suppress "unused variable" warnings with the method described in +// https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. +// (void)var does not work on many Intel compilers. +template FMT_CONSTEXPR void ignore_unused(const T&...) {} + +constexpr FMT_INLINE auto is_constant_evaluated(bool default_value = false) + FMT_NOEXCEPT -> bool { #ifdef __cpp_lib_is_constant_evaluated + ignore_unused(default_value); return std::is_constant_evaluated(); #else - return false; + return default_value; #endif } // A function to suppress "conditional expression is constant" warnings. -template constexpr auto const_check(T value) -> T { return value; } +template constexpr FMT_INLINE auto const_check(T value) -> T { + return value; +} FMT_NORETURN FMT_API void assert_fail(const char* file, int line, const char* message); @@ -367,7 +373,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, # ifdef NDEBUG // FMT_ASSERT is not empty to avoid -Werror=empty-body. # define FMT_ASSERT(condition, message) \ - ::fmt::ignore_unused((condition), (message)) + ::fmt::detail::ignore_unused((condition), (message)) # else # define FMT_ASSERT(condition, message) \ ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ @@ -376,6 +382,12 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, # endif #endif +#ifdef __cpp_lib_byte +using byte = std::byte; +#else +enum class byte : unsigned char {}; +#endif + #if defined(FMT_USE_STRING_VIEW) template using std_string_view = std::basic_string_view; #elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW) @@ -457,13 +469,12 @@ template class basic_string_view { */ FMT_CONSTEXPR_CHAR_TRAITS FMT_INLINE - basic_string_view(const Char* s) : data_(s) { - if (detail::const_check(std::is_same::value && - !detail::is_constant_evaluated())) - size_ = std::strlen(reinterpret_cast(s)); - else - size_ = std::char_traits::length(s); - } + basic_string_view(const Char* s) + : data_(s), + size_(detail::const_check(std::is_same::value && + !detail::is_constant_evaluated(true)) + ? std::strlen(reinterpret_cast(s)) + : std::char_traits::length(s)) {} /** Constructs a string reference from a ``std::basic_string`` object. */ template @@ -478,19 +489,19 @@ template class basic_string_view { size_(s.size()) {} /** Returns a pointer to the string data. */ - constexpr auto data() const -> const Char* { return data_; } + constexpr auto data() const FMT_NOEXCEPT -> const Char* { return data_; } /** Returns the string size. */ - constexpr auto size() const -> size_t { return size_; } + constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; } - constexpr auto begin() const -> iterator { return data_; } - constexpr auto end() const -> iterator { return data_ + size_; } + constexpr auto begin() const FMT_NOEXCEPT -> iterator { return data_; } + constexpr auto end() const FMT_NOEXCEPT -> iterator { return data_ + size_; } - constexpr auto operator[](size_t pos) const -> const Char& { + constexpr auto operator[](size_t pos) const FMT_NOEXCEPT -> const Char& { return data_[pos]; } - FMT_CONSTEXPR void remove_prefix(size_t n) { + FMT_CONSTEXPR void remove_prefix(size_t n) FMT_NOEXCEPT { data_ += n; size_ -= n; } @@ -570,7 +581,7 @@ constexpr auto to_string_view(const S& s) FMT_BEGIN_DETAIL_NAMESPACE void to_string_view(...); -using fmt::v8::to_string_view; +using fmt::to_string_view; // Specifies whether S is a string type convertible to fmt::basic_string_view. // It should be a constexpr function but MSVC 2017 fails to compile it in @@ -597,6 +608,8 @@ FMT_INLINE void check_format_string(const S&) { template ::value)> void check_format_string(S); +FMT_NORETURN FMT_API void throw_format_error(const char* message); + struct error_handler { constexpr error_handler() = default; constexpr error_handler(const error_handler&) = default; @@ -711,6 +724,22 @@ class appender; FMT_BEGIN_DETAIL_NAMESPACE +template +constexpr auto has_const_formatter_impl(T*) + -> decltype(typename Context::template formatter_type().format( + std::declval(), std::declval()), + true) { + return true; +} +template +constexpr auto has_const_formatter_impl(...) -> bool { + return false; +} +template +constexpr auto has_const_formatter() -> bool { + return has_const_formatter_impl(static_cast(nullptr)); +} + // Extracts a reference to the container from back_insert_iterator. template inline auto get_container(std::back_insert_iterator it) @@ -730,13 +759,13 @@ FMT_CONSTEXPR auto copy_str(InputIt begin, InputIt end, OutputIt out) return out; } -template ::value)> -FMT_CONSTEXPR auto copy_str(const Char* begin, const Char* end, Char* out) - -> Char* { - if (is_constant_evaluated()) - return copy_str(begin, end, out); +template , U>::value&& is_char::value)> +FMT_CONSTEXPR auto copy_str(T* begin, T* end, U* out) -> U* { + if (is_constant_evaluated()) return copy_str(begin, end, out); auto size = to_unsigned(end - begin); - memcpy(out, begin, size); + memcpy(out, begin, size * sizeof(U)); return out + size; } @@ -757,22 +786,22 @@ template class buffer { FMT_MSC_WARNING(suppress : 26495) buffer(size_t sz) FMT_NOEXCEPT : size_(sz), capacity_(sz) {} - buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) FMT_NOEXCEPT - : ptr_(p), - size_(sz), - capacity_(cap) {} + FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, + size_t cap = 0) FMT_NOEXCEPT : ptr_(p), + size_(sz), + capacity_(cap) {} - ~buffer() = default; + FMT_CONSTEXPR20 ~buffer() = default; buffer(buffer&&) = default; /** Sets the buffer data and capacity. */ - void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT { + FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT { ptr_ = buf_data; capacity_ = buf_capacity; } /** Increases the buffer capacity to hold at least *capacity* elements. */ - virtual void grow(size_t capacity) = 0; + virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0; public: using value_type = T; @@ -788,23 +817,23 @@ template class buffer { auto end() const FMT_NOEXCEPT -> const T* { return ptr_ + size_; } /** Returns the size of this buffer. */ - auto size() const FMT_NOEXCEPT -> size_t { return size_; } + constexpr auto size() const FMT_NOEXCEPT -> size_t { return size_; } /** Returns the capacity of this buffer. */ - auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; } + constexpr auto capacity() const FMT_NOEXCEPT -> size_t { return capacity_; } /** Returns a pointer to the buffer data. */ - auto data() FMT_NOEXCEPT -> T* { return ptr_; } + FMT_CONSTEXPR auto data() FMT_NOEXCEPT -> T* { return ptr_; } /** Returns a pointer to the buffer data. */ - auto data() const FMT_NOEXCEPT -> const T* { return ptr_; } + FMT_CONSTEXPR auto data() const FMT_NOEXCEPT -> const T* { return ptr_; } /** Clears this buffer. */ void clear() { size_ = 0; } // Tries resizing the buffer to contain *count* elements. If T is a POD type // the new elements may not be initialized. - void try_resize(size_t count) { + FMT_CONSTEXPR20 void try_resize(size_t count) { try_reserve(count); size_ = count <= capacity_ ? count : capacity_; } @@ -813,11 +842,11 @@ template class buffer { // 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) { + FMT_CONSTEXPR20 void try_reserve(size_t new_capacity) { if (new_capacity > capacity_) grow(new_capacity); } - void push_back(const T& value) { + FMT_CONSTEXPR20 void push_back(const T& value) { try_reserve(size_ + 1); ptr_[size_++] = value; } @@ -825,8 +854,11 @@ template class buffer { /** Appends data to the end of the buffer. */ template void append(const U* begin, const U* end); - template auto operator[](I index) -> T& { return ptr_[index]; } - template auto operator[](I index) const -> const T& { + template FMT_CONSTEXPR auto operator[](I index) -> T& { + return ptr_[index]; + } + template + FMT_CONSTEXPR auto operator[](I index) const -> const T& { return ptr_[index]; } }; @@ -861,7 +893,7 @@ class iterator_buffer final : public Traits, public buffer { T data_[buffer_size]; protected: - void grow(size_t) final FMT_OVERRIDE { + FMT_CONSTEXPR20 void grow(size_t) override { if (this->size() == buffer_size) flush(); } @@ -885,9 +917,55 @@ class iterator_buffer final : public Traits, public buffer { auto count() const -> size_t { return Traits::count() + this->size(); } }; +template +class iterator_buffer final + : public fixed_buffer_traits, + public buffer { + private: + T* out_; + enum { buffer_size = 256 }; + T data_[buffer_size]; + + protected: + FMT_CONSTEXPR20 void grow(size_t) override { + if (this->size() == this->capacity()) flush(); + } + + void flush() { + size_t n = this->limit(this->size()); + if (this->data() == out_) { + out_ += n; + this->set(data_, buffer_size); + } + this->clear(); + } + + public: + explicit iterator_buffer(T* out, size_t n = buffer_size) + : fixed_buffer_traits(n), buffer(out, 0, n), out_(out) {} + iterator_buffer(iterator_buffer&& other) + : fixed_buffer_traits(other), + buffer(std::move(other)), + out_(other.out_) { + if (this->data() != out_) { + this->set(data_, buffer_size); + this->clear(); + } + } + ~iterator_buffer() { flush(); } + + auto out() -> T* { + flush(); + return out_; + } + auto count() const -> size_t { + return fixed_buffer_traits::count() + this->size(); + } +}; + template class iterator_buffer final : public buffer { protected: - void grow(size_t) final FMT_OVERRIDE {} + FMT_CONSTEXPR20 void grow(size_t) override {} public: explicit iterator_buffer(T* out, size_t = 0) : buffer(out, 0, ~size_t()) {} @@ -905,7 +983,7 @@ class iterator_buffer, Container& container_; protected: - void grow(size_t capacity) final FMT_OVERRIDE { + FMT_CONSTEXPR20 void grow(size_t capacity) override { container_.resize(capacity); this->set(&container_[0], capacity); } @@ -928,7 +1006,7 @@ template class counting_buffer final : public buffer { size_t count_ = 0; protected: - void grow(size_t) final FMT_OVERRIDE { + FMT_CONSTEXPR20 void grow(size_t) override { if (this->size() != buffer_size) return; count_ += this->size(); this->clear(); @@ -1045,6 +1123,11 @@ template constexpr auto count_named_args() -> size_t { return count::value...>(); } +template +constexpr auto count_statically_named_args() -> size_t { + return count::value...>(); +} + enum class type { none_type, // Integer types should go first, @@ -1100,6 +1183,11 @@ constexpr bool is_arithmetic_type(type t) { return t > type::none_type && t <= type::last_numeric_type; } +struct unformattable {}; +struct unformattable_char : unformattable {}; +struct unformattable_const : unformattable {}; +struct unformattable_pointer : unformattable {}; + template struct string_value { const Char* data; size_t size; @@ -1112,8 +1200,8 @@ template struct named_arg_value { template struct custom_value { using parse_context = typename Context::parse_context_type; - const void* value; - void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx); + void* value; + void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); }; // A formatting argument value. @@ -1147,8 +1235,8 @@ template class value { constexpr FMT_INLINE value(unsigned long long val) : ulong_long_value(val) {} FMT_INLINE value(int128_t val) : int128_value(val) {} FMT_INLINE value(uint128_t val) : uint128_value(val) {} - FMT_INLINE value(float val) : float_value(val) {} - FMT_INLINE value(double val) : double_value(val) {} + constexpr FMT_INLINE value(float val) : float_value(val) {} + constexpr FMT_INLINE value(double val) : double_value(val) {} FMT_INLINE value(long double val) : long_double_value(val) {} constexpr FMT_INLINE value(bool val) : bool_value(val) {} constexpr FMT_INLINE value(char_type val) : char_value(val) {} @@ -1164,26 +1252,34 @@ template class value { FMT_INLINE value(const named_arg_info* args, size_t size) : named_args{args, size} {} - template FMT_CONSTEXPR FMT_INLINE value(const T& val) { - custom.value = &val; + template FMT_CONSTEXPR FMT_INLINE value(T& val) { + using value_type = remove_cvref_t; + custom.value = const_cast(&val); // Get the formatter type through the context to allow different contexts // have different extension points, e.g. `formatter` for `format` and // `printf_formatter` for `printf`. custom.format = format_custom_arg< - T, conditional_t::value, - typename Context::template formatter_type, - fallback_formatter>>; + value_type, + conditional_t::value, + typename Context::template formatter_type, + fallback_formatter>>; } + value(unformattable); + value(unformattable_char); + value(unformattable_const); + value(unformattable_pointer); private: // Formats an argument of a custom type, such as a user-defined class. template - static void format_custom_arg(const void* arg, + static void format_custom_arg(void* arg, typename Context::parse_context_type& parse_ctx, Context& ctx) { - Formatter f; + auto f = Formatter(); parse_ctx.advance_to(f.parse(parse_ctx)); - ctx.advance_to(f.format(*static_cast(arg), ctx)); + using qualified_type = + conditional_t(), const T, T>; + ctx.advance_to(f.format(*static_cast(arg), ctx)); } }; @@ -1196,9 +1292,9 @@ enum { long_short = sizeof(long) == sizeof(int) }; using long_type = conditional_t; using ulong_type = conditional_t; -struct unformattable {}; - // Maps formatting arguments to core types. +// arg_mapper reports errors by returning unformattable instead of using +// static_assert because it's used in the is_formattable trait. template struct arg_mapper { using char_type = typename Context::char_type; @@ -1225,13 +1321,22 @@ template struct arg_mapper { FMT_CONSTEXPR FMT_INLINE auto map(uint128_t val) -> uint128_t { return val; } FMT_CONSTEXPR FMT_INLINE auto map(bool val) -> bool { return val; } - template ::value)> + template ::value || + std::is_same::value)> FMT_CONSTEXPR FMT_INLINE auto map(T val) -> char_type { - static_assert( - std::is_same::value || std::is_same::value, - "mixing character types is disallowed"); return val; } + template ::value || +#ifdef __cpp_char8_t + std::is_same::value || +#endif + std::is_same::value || + std::is_same::value) && + !std::is_same::value, + int> = 0> + FMT_CONSTEXPR FMT_INLINE auto map(T) -> unformattable_char { + return {}; + } FMT_CONSTEXPR FMT_INLINE auto map(float val) -> float { return val; } FMT_CONSTEXPR FMT_INLINE auto map(double val) -> double { return val; } @@ -1245,13 +1350,19 @@ template struct arg_mapper { FMT_CONSTEXPR FMT_INLINE auto map(const char_type* val) -> const char_type* { return val; } - template ::value)> + template ::value && !std::is_pointer::value && + std::is_same>::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> basic_string_view { - static_assert(std::is_same>::value, - "mixing character types is disallowed"); return to_string_view(val); } + template ::value && !std::is_pointer::value && + !std::is_same>::value)> + FMT_CONSTEXPR FMT_INLINE auto map(const T&) -> unformattable_char { + return {}; + } template , T>::value && @@ -1272,21 +1383,25 @@ template struct arg_mapper { -> basic_string_view { return std_string_view(val); } - FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val) -> const char* { - static_assert(std::is_same::value, "invalid string type"); - return reinterpret_cast(val); + + using cstring_result = conditional_t::value, + const char*, unformattable_pointer>; + + FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const signed char* val) + -> cstring_result { + return map(reinterpret_cast(val)); } - FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val) -> const char* { - static_assert(std::is_same::value, "invalid string type"); - return reinterpret_cast(val); + FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(const unsigned char* val) + -> cstring_result { + return map(reinterpret_cast(val)); } - FMT_CONSTEXPR FMT_INLINE auto map(signed char* val) -> const char* { - const auto* const_val = val; - return map(const_val); + FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(signed char* val) + -> cstring_result { + return map(reinterpret_cast(val)); } - FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val) -> const char* { - const auto* const_val = val; - return map(const_val); + FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto map(unsigned char* val) + -> cstring_result { + return map(reinterpret_cast(val)); } FMT_CONSTEXPR FMT_INLINE auto map(void* val) -> const void* { return val; } @@ -1299,37 +1414,69 @@ template struct arg_mapper { // We use SFINAE instead of a const T* parameter to avoid conflicting with // the C array overload. - template - FMT_CONSTEXPR auto map(T) -> enable_if_t::value, int> { - // Formatting of arbitrary pointers is disallowed. If you want to output - // a pointer cast it to "void *" or "const void *". In particular, this - // forbids formatting of "[const] volatile char *" which is printed as bool - // by iostreams. - static_assert(!sizeof(T), "formatting of non-void pointers is disallowed"); - return 0; + template < + typename T, + FMT_ENABLE_IF( + std::is_member_pointer::value || + std::is_function::type>::value || + (std::is_convertible::value && + !std::is_convertible::value))> + FMT_CONSTEXPR auto map(const T&) -> unformattable_pointer { + return {}; } - template + template ::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] { return values; } template ::value && - !has_formatter::value && - !has_fallback_formatter::value)> + FMT_ENABLE_IF( + std::is_enum::value&& std::is_convertible::value && + !has_formatter::value && + !has_fallback_formatter::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(std::declval().map( static_cast::type>(val))) { return map(static_cast::type>(val)); } - template ::value && !is_char::value && - (has_formatter::value || - has_fallback_formatter::value))> - FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> const T& { + + FMT_CONSTEXPR FMT_INLINE auto map(detail::byte val) -> unsigned { + return map(static_cast(val)); + } + + template > + struct formattable + : bool_constant() || + !std::is_const>::value || + has_fallback_formatter::value> {}; + +#if FMT_MSC_VER != 0 && FMT_MSC_VER < 1910 + // Workaround a bug in MSVC. + template FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { + return val; + } +#else + template ::value)> + FMT_CONSTEXPR FMT_INLINE auto do_map(T&& val) -> T& { return val; } + template ::value)> + FMT_CONSTEXPR FMT_INLINE auto do_map(T&&) -> unformattable_const { + return {}; + } +#endif + + template , + FMT_ENABLE_IF(!is_string::value && !is_char::value && + !std::is_array::value && + (has_formatter::value || + has_fallback_formatter::value))> + FMT_CONSTEXPR FMT_INLINE auto map(T&& val) + -> decltype(this->do_map(std::forward(val))) { + return do_map(std::forward(val)); + } template ::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg) @@ -1366,19 +1513,12 @@ class appender : public std::back_insert_iterator> { public: using std::back_insert_iterator>::back_insert_iterator; - appender(base it) : base(it) {} + appender(base it) FMT_NOEXCEPT : base(it) {} using _Unchecked_type = appender; // Mark iterator as checked. - auto operator++() -> appender& { - base::operator++(); - return *this; - } + auto operator++() FMT_NOEXCEPT -> appender& { return *this; } - auto operator++(int) -> appender { - auto tmp = *this; - ++*this; - return tmp; - } + auto operator++(int) FMT_NOEXCEPT -> appender { return *this; } }; // A formatting argument. It is a trivially copyable/constructible type to @@ -1562,10 +1702,30 @@ FMT_CONSTEXPR auto make_arg(const T& value) -> basic_format_arg { // another (not recommended). template -FMT_CONSTEXPR FMT_INLINE auto make_arg(const T& val) -> value { - const auto& arg = arg_mapper().map(val); +FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { + const auto& arg = arg_mapper().map(std::forward(val)); + + constexpr bool formattable_char = + !std::is_same::value; + static_assert(formattable_char, "Mixing character types is disallowed."); + + constexpr bool formattable_const = + !std::is_same::value; + static_assert(formattable_const, "Cannot format a const argument."); + + // Formatting of arbitrary pointers is disallowed. If you want to output + // a pointer cast it to "void *" or "const void *". In particular, this + // forbids formatting of "[const] volatile char *" which is printed as bool + // by iostreams. + constexpr bool formattable_pointer = + !std::is_same::value; + static_assert(formattable_pointer, + "Formatting of non-void pointers is disallowed."); + + constexpr bool formattable = + !std::is_same::value; static_assert( - !std::is_same::value, + formattable, "Cannot format an argument. To make type T formattable provide a " "formatter specialization: https://fmt.dev/latest/api.html#udt"); return {arg}; @@ -1643,9 +1803,9 @@ using format_context = buffer_context; template using is_formattable = bool_constant< - !std::is_same>().map( - std::declval())), - detail::unformattable>::value && + !std::is_base_of>().map( + std::declval()))>::value && !detail::has_fallback_formatter::value>; /** @@ -1684,14 +1844,16 @@ class format_arg_store : 0); public: - FMT_CONSTEXPR FMT_INLINE format_arg_store(const Args&... args) + template + FMT_CONSTEXPR FMT_INLINE format_arg_store(T&&... args) : #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 basic_format_args(*this), #endif data_{detail::make_arg< is_packed, Context, - detail::mapped_type_constant::value>(args)...} { + detail::mapped_type_constant, Context>::value>( + std::forward(args))...} { detail::init_named_args(data_.named_args(), 0, 0, args...); } }; @@ -1705,9 +1867,9 @@ class format_arg_store \endrst */ template -constexpr auto make_format_args(const Args&... args) - -> format_arg_store { - return {args...}; +constexpr auto make_format_args(Args&&... args) + -> format_arg_store...> { + return {std::forward(args)...}; } /** @@ -1866,8 +2028,6 @@ using sign_t = sign::type; FMT_BEGIN_DETAIL_NAMESPACE -void throw_format_error(const char* message); - // Workaround an array initialization issue in gcc 4.8. template struct fill_t { private: @@ -1893,11 +2053,33 @@ template struct fill_t { }; FMT_END_DETAIL_NAMESPACE +enum class presentation_type : unsigned char { + none, + // Integer types should go first, + dec, // 'd' + oct, // 'o' + hex_lower, // 'x' + hex_upper, // 'X' + bin_lower, // 'b' + bin_upper, // 'B' + hexfloat_lower, // 'a' + hexfloat_upper, // 'A' + exp_lower, // 'e' + exp_upper, // 'E' + fixed_lower, // 'f' + fixed_upper, // 'F' + general_lower, // 'g' + general_upper, // 'G' + chr, // 'c' + string, // 's' + pointer // 'p' +}; + // Format specifiers for built-in and string types. template struct basic_format_specs { int width; int precision; - char type; + presentation_type type; align_t align : 4; sign_t sign : 3; bool alt : 1; // Alternate form ('#'). @@ -1907,7 +2089,7 @@ template struct basic_format_specs { constexpr basic_format_specs() : width(0), precision(-1), - type(0), + type(presentation_type::none), align(align::none), sign(sign::none), alt(false), @@ -1987,9 +2169,7 @@ template class specs_setter { } FMT_CONSTEXPR void end_precision() {} - FMT_CONSTEXPR void on_type(Char type) { - specs_.type = static_cast(type); - } + FMT_CONSTEXPR void on_type(presentation_type type) { specs_.type = type; } }; // Format spec handler that saves references to arguments representing dynamic @@ -2063,8 +2243,8 @@ constexpr auto to_ascii(Char value) -> template FMT_CONSTEXPR auto code_point_length(const Char* begin) -> int { if (const_check(sizeof(Char) != 1)) return 1; - constexpr char lengths[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 4, 0}; + auto lengths = + "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4"; int len = lengths[static_cast(*begin) >> 3]; // Compute the pointer to the next character early so that the next @@ -2271,6 +2451,48 @@ FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, return begin; } +template +FMT_CONSTEXPR auto parse_presentation_type(Char type) -> presentation_type { + switch (to_ascii(type)) { + case 'd': + return presentation_type::dec; + case 'o': + return presentation_type::oct; + case 'x': + return presentation_type::hex_lower; + case 'X': + return presentation_type::hex_upper; + case 'b': + return presentation_type::bin_lower; + case 'B': + return presentation_type::bin_upper; + case 'a': + return presentation_type::hexfloat_lower; + case 'A': + return presentation_type::hexfloat_upper; + case 'e': + return presentation_type::exp_lower; + case 'E': + return presentation_type::exp_upper; + case 'f': + return presentation_type::fixed_lower; + case 'F': + return presentation_type::fixed_upper; + case 'g': + return presentation_type::general_lower; + case 'G': + return presentation_type::general_upper; + case 'c': + return presentation_type::chr; + case 's': + return presentation_type::string; + case 'p': + return presentation_type::pointer; + default: + return presentation_type::none; + } +} + // Parses standard format specifiers and sends notifications about parsed // components to handler. template @@ -2278,9 +2500,12 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin, const Char* end, SpecHandler&& handler) -> const Char* { - if (begin + 1 < end && begin[1] == '}' && is_ascii_letter(*begin) && + if (1 < end - begin && begin[1] == '}' && is_ascii_letter(*begin) && *begin != 'L') { - handler.on_type(*begin++); + presentation_type type = parse_presentation_type(*begin++); + if (type == presentation_type::none) + handler.on_error("invalid type specifier"); + handler.on_type(type); return begin; } @@ -2334,7 +2559,12 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(const Char* begin, } // Parse type. - if (begin != end && *begin != '}') handler.on_type(*begin++); + if (begin != end && *begin != '}') { + presentation_type type = parse_presentation_type(*begin++); + if (type == presentation_type::none) + handler.on_error("invalid type specifier"); + handler.on_type(type); + } return begin; } @@ -2381,7 +2611,7 @@ FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, template FMT_CONSTEXPR FMT_INLINE void parse_format_string( basic_string_view format_str, Handler&& handler) { - // this is most likely a name-lookup defect in msvc's modules implementation + // Workaround a name-lookup bug in MSVC's modules implementation. using detail::find; auto begin = format_str.data(); @@ -2476,28 +2706,18 @@ class compile_parse_context }; template -FMT_CONSTEXPR void check_int_type_spec(char spec, ErrorHandler&& eh) { - switch (spec) { - case 0: - case 'd': - case 'x': - case 'X': - case 'b': - case 'B': - case 'o': - case 'c': - break; - default: +FMT_CONSTEXPR void check_int_type_spec(presentation_type type, + ErrorHandler&& eh) { + if (type > presentation_type::bin_upper && type != presentation_type::chr) eh.on_error("invalid type specifier"); - break; - } } // Checks char specs and returns true if the type spec is char (and not int). template FMT_CONSTEXPR auto check_char_specs(const basic_format_specs& specs, ErrorHandler&& eh = {}) -> bool { - if (specs.type && specs.type != 'c') { + if (specs.type != presentation_type::none && + specs.type != presentation_type::chr) { check_int_type_spec(specs.type, eh); return false; } @@ -2521,7 +2741,7 @@ struct float_specs { bool upper : 1; bool locale : 1; bool binary32 : 1; - bool use_grisu : 1; + bool fallback : 1; bool showpoint : 1; }; @@ -2533,33 +2753,33 @@ FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs& specs, result.showpoint = specs.alt; result.locale = specs.localized; switch (specs.type) { - case 0: + case presentation_type::none: result.format = float_format::general; break; - case 'G': + case presentation_type::general_upper: result.upper = true; FMT_FALLTHROUGH; - case 'g': + case presentation_type::general_lower: result.format = float_format::general; break; - case 'E': + case presentation_type::exp_upper: result.upper = true; FMT_FALLTHROUGH; - case 'e': + case presentation_type::exp_lower: result.format = float_format::exp; result.showpoint |= specs.precision != 0; break; - case 'F': + case presentation_type::fixed_upper: result.upper = true; FMT_FALLTHROUGH; - case 'f': + case presentation_type::fixed_lower: result.format = float_format::fixed; result.showpoint |= specs.precision != 0; break; - case 'A': + case presentation_type::hexfloat_upper: result.upper = true; FMT_FALLTHROUGH; - case 'a': + case presentation_type::hexfloat_lower: result.format = float_format::hex; break; default: @@ -2569,22 +2789,27 @@ FMT_CONSTEXPR auto parse_float_type_spec(const basic_format_specs& specs, return result; } -template -FMT_CONSTEXPR auto check_cstring_type_spec(Char spec, ErrorHandler&& eh = {}) - -> bool { - if (spec == 0 || spec == 's') return true; - if (spec != 'p') eh.on_error("invalid type specifier"); +template +FMT_CONSTEXPR auto check_cstring_type_spec(presentation_type type, + ErrorHandler&& eh = {}) -> bool { + if (type == presentation_type::none || type == presentation_type::string) + return true; + if (type != presentation_type::pointer) eh.on_error("invalid type specifier"); return false; } -template -FMT_CONSTEXPR void check_string_type_spec(Char spec, ErrorHandler&& eh = {}) { - if (spec != 0 && spec != 's') eh.on_error("invalid type specifier"); +template +FMT_CONSTEXPR void check_string_type_spec(presentation_type type, + ErrorHandler&& eh = {}) { + if (type != presentation_type::none && type != presentation_type::string) + eh.on_error("invalid type specifier"); } -template -FMT_CONSTEXPR void check_pointer_type_spec(Char spec, ErrorHandler&& eh) { - if (spec != 0 && spec != 'p') eh.on_error("invalid type specifier"); +template +FMT_CONSTEXPR void check_pointer_type_spec(presentation_type type, + ErrorHandler&& eh) { + if (type != presentation_type::none && type != presentation_type::pointer) + eh.on_error("invalid type specifier"); } // A parse_format_specs handler that checks if specifiers are consistent with @@ -2645,28 +2870,21 @@ constexpr auto get_arg_index_by_name(basic_string_view name) -> int { if constexpr (detail::is_statically_named_arg()) { if (name == T::name) return N; } - if constexpr (sizeof...(Args) > 0) { + if constexpr (sizeof...(Args) > 0) return get_arg_index_by_name(name); - } else { - (void)name; // Workaround an MSVC bug about "unused" parameter. - return invalid_arg_index; - } + (void)name; // Workaround an MSVC bug about "unused" parameter. + return invalid_arg_index; } #endif template FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS - if constexpr (sizeof...(Args) > 0) { + if constexpr (sizeof...(Args) > 0) return get_arg_index_by_name<0, Args...>(name); - } else { - (void)name; - return invalid_arg_index; - } -#else +#endif (void)name; return invalid_arg_index; -#endif } template @@ -2769,7 +2987,10 @@ struct formatter struct basic_runtime { basic_string_view str; }; +/** A compile-time format string. */ template class basic_format_string { private: basic_string_view str_; @@ -2832,14 +3054,15 @@ template class basic_format_string { template >::value)> - FMT_CONSTEVAL basic_format_string(const S& s) : str_(s) { + FMT_CONSTEVAL FMT_INLINE basic_format_string(const S& s) : str_(s) { static_assert( detail::count< (std::is_base_of>::value && std::is_reference::value)...>() == 0, "passing views as lvalues is disallowed"); #ifdef FMT_HAS_CONSTEVAL - if constexpr (detail::count_named_args() == 0) { + if constexpr (detail::count_named_args() == + detail::count_statically_named_args()) { using checker = detail::format_string_checker...>; detail::parse_format_string(str_, checker(s, {})); @@ -2862,7 +3085,16 @@ template auto runtime(const S& s) -> basic_string_view> { #else template using format_string = basic_format_string...>; -// Creates a runtime format string. +/** + \rst + Creates a runtime format string. + + **Example**:: + + // Check format string at runtime instead of compile-time. + fmt::print(fmt::runtime("{:d}"), "I am not a number"); + \endrst + */ template auto runtime(const S& s) -> basic_runtime> { return {{s}}; } @@ -2878,11 +3110,12 @@ FMT_API auto vformat(string_view fmt, format_args args) -> std::string; **Example**:: #include - std::string message = fmt::format("The answer is {}", 42); + std::string message = fmt::format("The answer is {}.", 42); \endrst */ template -FMT_INLINE auto format(format_string fmt, T&&... args) -> std::string { +FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) + -> std::string { return vformat(fmt, fmt::make_format_args(args...)); } @@ -2892,7 +3125,7 @@ template OutputIt { using detail::get_buffer; auto&& buf = get_buffer(out); - detail::vformat_to(buf, string_view(fmt), args, {}); + detail::vformat_to(buf, fmt, args, {}); return detail::get_iterator(buf); } @@ -2900,7 +3133,7 @@ auto vformat_to(OutputIt out, string_view fmt, format_args args) -> OutputIt { \rst Formats ``args`` according to specifications in ``fmt``, writes the result to the output iterator ``out`` and returns the iterator past the end of the output - range. + range. `format_to` does not append a terminating null character. **Example**:: @@ -2926,9 +3159,8 @@ template ::value)> auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) -> format_to_n_result { - using buffer = - detail::iterator_buffer; - auto buf = buffer(out, n); + using traits = detail::fixed_buffer_traits; + auto buf = detail::iterator_buffer(out, n); detail::vformat_to(buf, fmt, args, {}); return {buf.out(), buf.count()}; } @@ -2938,18 +3170,20 @@ auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) Formats ``args`` according to specifications in ``fmt``, writes up to ``n`` characters of the result to the output iterator ``out`` and returns the total (not truncated) output size and the iterator past the end of the output range. + `format_to_n` does not append a terminating null character. \endrst */ template ::value)> FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, - const T&... args) -> format_to_n_result { + T&&... args) -> format_to_n_result { return vformat_to_n(out, n, fmt, fmt::make_format_args(args...)); } /** Returns the number of chars in the output of ``format(fmt, args...)``. */ template -FMT_INLINE auto formatted_size(format_string fmt, T&&... args) -> size_t { +FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, + T&&... args) -> size_t { auto buf = detail::counting_buffer<>(); detail::vformat_to(buf, string_view(fmt), fmt::make_format_args(args...), {}); return buf.count(); diff --git a/include/spdlog/fmt/bundled/format-inl.h b/include/spdlog/fmt/bundled/format-inl.h index 94a36d1b..2c51c50a 100644 --- a/include/spdlog/fmt/bundled/format-inl.h +++ b/include/spdlog/fmt/bundled/format-inl.h @@ -40,6 +40,10 @@ FMT_FUNC void assert_fail(const char* file, int line, const char* message) { std::terminate(); } +FMT_FUNC void throw_format_error(const char* message) { + FMT_THROW(format_error(message)); +} + #ifndef _MSC_VER # define FMT_SNPRINTF snprintf #else // _MSC_VER @@ -145,14 +149,76 @@ template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) { return i >= 0 ? i * char_digits + count_digits<4, unsigned>(n.value[i]) : 1; } +// log10(2) = 0x0.4d104d427de7fbcc... +static constexpr uint64_t log10_2_significand = 0x4d104d427de7fbcc; + +template struct basic_impl_data { + // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. + // These are generated by support/compute-powers.py. + static constexpr uint64_t pow10_significands[87] = { + 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, + 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, + 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, + 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, + 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, + 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, + 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, + 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, + 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, + 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, + 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, + 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, + 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, + 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, + 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, + 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, + 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, + 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, + 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, + 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, + 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, + 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, + 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, + 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, + 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, + 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, + 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, + 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, + 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, + }; + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wnarrowing" +#endif + // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding + // to significands above. + static constexpr int16_t pow10_exponents[87] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, + -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, + -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, + -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, + -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, + 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, + 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, + 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +# pragma GCC diagnostic pop +#endif + + static constexpr uint64_t power_of_10_64[20] = { + 1, FMT_POWERS_OF_10(1ULL), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; +}; + +// This is a struct rather than an alias to avoid shadowing warnings in gcc. +struct impl_data : basic_impl_data<> {}; + #if __cplusplus < 201703L -template constexpr const char basic_data::digits[][2]; -template constexpr const char basic_data::hex_digits[]; -template constexpr const char basic_data::signs[]; -template constexpr const unsigned basic_data::prefixes[]; -template constexpr const char basic_data::left_padding_shifts[]; template -constexpr const char basic_data::right_padding_shifts[]; +constexpr uint64_t basic_impl_data::pow10_significands[]; +template constexpr int16_t basic_impl_data::pow10_exponents[]; +template constexpr uint64_t basic_impl_data::power_of_10_64[]; #endif template struct bits { @@ -160,93 +226,76 @@ template struct bits { static_cast(sizeof(T) * std::numeric_limits::digits); }; -class fp; -template fp normalize(fp value); - -// Lower (upper) boundary is a value half way between a floating-point value -// and its predecessor (successor). Boundaries have the same exponent as the -// value so only significands are stored. -struct boundaries { - uint64_t lower; - uint64_t upper; -}; - -// A handmade floating-point number f * pow(2, e). -class fp { - private: - using significand_type = uint64_t; - - template - using is_supported_float = bool_constant; +// Returns the number of significand bits in Float excluding the implicit bit. +template constexpr int num_significand_bits() { + // Subtract 1 to account for an implicit most significant bit in the + // normalized form. + return std::numeric_limits::digits - 1; +} - public: - significand_type f; +// A floating-point number f * pow(2, e). +struct fp { + uint64_t f; int e; - // All sizes are in bits. - // Subtract 1 to account for an implicit most significant bit in the - // normalized form. - static FMT_CONSTEXPR_DECL const int double_significand_size = - std::numeric_limits::digits - 1; - static FMT_CONSTEXPR_DECL const uint64_t implicit_bit = - 1ULL << double_significand_size; - static FMT_CONSTEXPR_DECL const int significand_size = - bits::value; + static constexpr const int num_significand_bits = bits::value; - fp() : f(0), e(0) {} - fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} + constexpr fp() : f(0), e(0) {} + constexpr fp(uint64_t f_val, int e_val) : f(f_val), e(e_val) {} - // Constructs fp from an IEEE754 double. It is a template to prevent compile - // errors on platforms where double is not IEEE754. - template explicit fp(Double d) { assign(d); } + // Constructs fp from an IEEE754 floating-point number. It is a template to + // prevent compile errors on systems where n is not IEEE754. + template explicit FMT_CONSTEXPR fp(Float n) { assign(n); } + + template + using is_supported = bool_constant; // Assigns d to this and return true iff predecessor is closer than successor. - template ::value)> - bool assign(Float d) { + template ::value)> + FMT_CONSTEXPR bool assign(Float n) { // Assume float is in the format [sign][exponent][significand]. - using limits = std::numeric_limits; - const int float_significand_size = limits::digits - 1; - const int exponent_size = - bits::value - float_significand_size - 1; // -1 for sign - const uint64_t float_implicit_bit = 1ULL << float_significand_size; - const uint64_t significand_mask = float_implicit_bit - 1; - const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; - const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; + const int num_float_significand_bits = + detail::num_significand_bits(); + const uint64_t implicit_bit = 1ULL << num_float_significand_bits; + const uint64_t significand_mask = implicit_bit - 1; constexpr bool is_double = sizeof(Float) == sizeof(uint64_t); - auto u = bit_cast>(d); + auto u = bit_cast>(n); f = u & significand_mask; + const uint64_t exponent_mask = (~0ULL >> 1) & ~significand_mask; int biased_e = - static_cast((u & exponent_mask) >> float_significand_size); - // Predecessor is closer if d is a normalized power of 2 (f == 0) other than - // the smallest normalized number (biased_e > 1). + static_cast((u & exponent_mask) >> num_float_significand_bits); + // The predecessor is closer if n is a normalized power of 2 (f == 0) other + // than the smallest normalized number (biased_e > 1). bool is_predecessor_closer = f == 0 && biased_e > 1; if (biased_e != 0) - f += float_implicit_bit; + f += implicit_bit; else biased_e = 1; // Subnormals use biased exponent 1 (min exponent). - e = biased_e - exponent_bias - float_significand_size; + const int exponent_bias = std::numeric_limits::max_exponent - 1; + e = biased_e - exponent_bias - num_float_significand_bits; return is_predecessor_closer; } - template ::value)> + template ::value)> bool assign(Float) { - *this = fp(); + FMT_ASSERT(false, ""); return false; } }; // Normalizes the value converted from double and multiplied by (1 << SHIFT). -template fp normalize(fp value) { +template FMT_CONSTEXPR fp normalize(fp value) { // Handle subnormals. - const auto shifted_implicit_bit = fp::implicit_bit << SHIFT; + const uint64_t implicit_bit = 1ULL << num_significand_bits(); + const auto shifted_implicit_bit = implicit_bit << SHIFT; while ((value.f & shifted_implicit_bit) == 0) { value.f <<= 1; --value.e; } // Subtract 1 to account for hidden bit. const auto offset = - fp::significand_size - fp::double_significand_size - SHIFT - 1; + fp::num_significand_bits - num_significand_bits() - SHIFT - 1; value.f <<= offset; value.e -= offset; return value; @@ -255,7 +304,7 @@ template fp normalize(fp value) { inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; } // Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking. -inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { +FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { #if FMT_USE_INT128 auto product = static_cast<__uint128_t>(lhs) * rhs; auto f = static_cast(product >> 64); @@ -272,61 +321,18 @@ inline uint64_t multiply(uint64_t lhs, uint64_t rhs) { #endif } -inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; } +FMT_CONSTEXPR inline fp operator*(fp x, fp y) { + return {multiply(x.f, y.f), x.e + y.e + 64}; +} // Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its // (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`. -inline fp get_cached_power(int min_exponent, int& pow10_exponent) { - // Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340. - // These are generated by support/compute-powers.py. - static constexpr const uint64_t pow10_significands[] = { - 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, - 0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, - 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, - 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, - 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, - 0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7, - 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, - 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, - 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, - 0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053, - 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, - 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, - 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, - 0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb, - 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, - 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, - 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, - 0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, - 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, - 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, - 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, - 0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25, - 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, - 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, - 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, - 0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129, - 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, - 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, - 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b, - }; - - // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding - // to significands above. - static constexpr const int16_t pow10_exponents[] = { - -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, - -927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661, - -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, - -343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77, - -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, - 242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508, - 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, - 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066}; - +FMT_CONSTEXPR inline fp get_cached_power(int min_exponent, + int& pow10_exponent) { const int shift = 32; - const auto significand = static_cast(data::log10_2_significand); + const auto significand = static_cast(log10_2_significand); int index = static_cast( - ((min_exponent + fp::significand_size - 1) * (significand >> shift) + + ((min_exponent + fp::num_significand_bits - 1) * (significand >> shift) + ((int64_t(1) << shift) - 1)) // ceil >> 32 // arithmetic shift ); @@ -336,7 +342,8 @@ inline fp get_cached_power(int min_exponent, int& pow10_exponent) { const int dec_exp_step = 8; index = (index - first_dec_exp - 1) / dec_exp_step + 1; pow10_exponent = first_dec_exp + index * dec_exp_step; - return {pow10_significands[index], pow10_exponents[index]}; + return {impl_data::pow10_significands[index], + impl_data::pow10_exponents[index]}; } // A simple accumulator to hold the sums of terms in bigint::square if uint128_t @@ -345,14 +352,16 @@ struct accumulator { uint64_t lower; uint64_t upper; - accumulator() : lower(0), upper(0) {} - explicit operator uint32_t() const { return static_cast(lower); } + constexpr accumulator() : lower(0), upper(0) {} + constexpr explicit operator uint32_t() const { + return static_cast(lower); + } - void operator+=(uint64_t n) { + FMT_CONSTEXPR void operator+=(uint64_t n) { lower += n; if (lower < n) ++upper; } - void operator>>=(int shift) { + FMT_CONSTEXPR void operator>>=(int shift) { FMT_ASSERT(shift == 32, ""); (void)shift; lower = (upper << 32) | (lower >> 32); @@ -370,27 +379,31 @@ class bigint { basic_memory_buffer bigits_; int exp_; - bigit operator[](int index) const { return bigits_[to_unsigned(index)]; } - bigit& operator[](int index) { return bigits_[to_unsigned(index)]; } + FMT_CONSTEXPR20 bigit operator[](int index) const { + return bigits_[to_unsigned(index)]; + } + FMT_CONSTEXPR20 bigit& operator[](int index) { + return bigits_[to_unsigned(index)]; + } static FMT_CONSTEXPR_DECL const int bigit_bits = bits::value; friend struct formatter; - void subtract_bigits(int index, bigit other, bigit& borrow) { + FMT_CONSTEXPR20 void subtract_bigits(int index, bigit other, bigit& borrow) { auto result = static_cast((*this)[index]) - other - borrow; (*this)[index] = static_cast(result); borrow = static_cast(result >> (bigit_bits * 2 - 1)); } - void remove_leading_zeros() { + FMT_CONSTEXPR20 void remove_leading_zeros() { int num_bigits = static_cast(bigits_.size()) - 1; while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits; bigits_.resize(to_unsigned(num_bigits + 1)); } // Computes *this -= other assuming aligned bigints and *this >= other. - void subtract_aligned(const bigint& other) { + FMT_CONSTEXPR20 void subtract_aligned(const bigint& other) { FMT_ASSERT(other.exp_ >= exp_, "unaligned bigints"); FMT_ASSERT(compare(*this, other) >= 0, ""); bigit borrow = 0; @@ -401,7 +414,7 @@ class bigint { remove_leading_zeros(); } - void multiply(uint32_t value) { + FMT_CONSTEXPR20 void multiply(uint32_t value) { const double_bigit wide_value = value; bigit carry = 0; for (size_t i = 0, n = bigits_.size(); i < n; ++i) { @@ -412,7 +425,7 @@ class bigint { if (carry != 0) bigits_.push_back(carry); } - void multiply(uint64_t value) { + FMT_CONSTEXPR20 void multiply(uint64_t value) { const bigit mask = ~bigit(0); const double_bigit lower = value & mask; const double_bigit upper = value >> bigit_bits; @@ -430,14 +443,16 @@ class bigint { } public: - bigint() : exp_(0) {} + FMT_CONSTEXPR20 bigint() : exp_(0) {} explicit bigint(uint64_t n) { assign(n); } - ~bigint() { FMT_ASSERT(bigits_.capacity() <= bigits_capacity, ""); } + FMT_CONSTEXPR20 ~bigint() { + FMT_ASSERT(bigits_.capacity() <= bigits_capacity, ""); + } bigint(const bigint&) = delete; void operator=(const bigint&) = delete; - void assign(const bigint& other) { + FMT_CONSTEXPR20 void assign(const bigint& other) { auto size = other.bigits_.size(); bigits_.resize(size); auto data = other.bigits_.data(); @@ -445,7 +460,7 @@ class bigint { exp_ = other.exp_; } - void assign(uint64_t n) { + FMT_CONSTEXPR20 void assign(uint64_t n) { size_t num_bigits = 0; do { bigits_[num_bigits++] = n & ~bigit(0); @@ -455,9 +470,11 @@ class bigint { exp_ = 0; } - int num_bigits() const { return static_cast(bigits_.size()) + exp_; } + FMT_CONSTEXPR20 int num_bigits() const { + return static_cast(bigits_.size()) + exp_; + } - FMT_NOINLINE bigint& operator<<=(int shift) { + FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) { FMT_ASSERT(shift >= 0, ""); exp_ += shift / bigit_bits; shift %= bigit_bits; @@ -472,13 +489,13 @@ class bigint { return *this; } - template bigint& operator*=(Int value) { + template FMT_CONSTEXPR20 bigint& operator*=(Int value) { FMT_ASSERT(value > 0, ""); multiply(uint32_or_64_or_128_t(value)); return *this; } - friend int compare(const bigint& lhs, const bigint& rhs) { + friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) { int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits(); if (num_lhs_bigits != num_rhs_bigits) return num_lhs_bigits > num_rhs_bigits ? 1 : -1; @@ -495,8 +512,8 @@ class bigint { } // Returns compare(lhs1 + lhs2, rhs). - friend int add_compare(const bigint& lhs1, const bigint& lhs2, - const bigint& rhs) { + friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2, + const bigint& rhs) { int max_lhs_bigits = (std::max)(lhs1.num_bigits(), lhs2.num_bigits()); int num_rhs_bigits = rhs.num_bigits(); if (max_lhs_bigits + 1 < num_rhs_bigits) return -1; @@ -519,7 +536,7 @@ class bigint { } // Assigns pow(10, exp) to this bigint. - void assign_pow10(int exp) { + FMT_CONSTEXPR20 void assign_pow10(int exp) { FMT_ASSERT(exp >= 0, ""); if (exp == 0) return assign(1); // Find the top bit. @@ -538,7 +555,7 @@ class bigint { *this <<= exp; // Multiply by pow(2, exp) by shifting. } - void square() { + FMT_CONSTEXPR20 void square() { int num_bigits = static_cast(bigits_.size()); int num_result_bigits = 2 * num_bigits; basic_memory_buffer n(std::move(bigits_)); @@ -569,7 +586,7 @@ class bigint { // If this bigint has a bigger exponent than other, adds trailing zero to make // exponents equal. This simplifies some operations such as subtraction. - void align(const bigint& other) { + FMT_CONSTEXPR20 void align(const bigint& other) { int exp_difference = exp_ - other.exp_; if (exp_difference <= 0) return; int num_bigits = static_cast(bigits_.size()); @@ -582,7 +599,7 @@ class bigint { // Divides this bignum by divisor, assigning the remainder to this and // returning the quotient. - int divmod_assign(const bigint& divisor) { + FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) { FMT_ASSERT(this != &divisor, ""); if (compare(*this, divisor) < 0) return 0; FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, ""); @@ -602,8 +619,9 @@ enum class round_direction { unknown, up, down }; // some number v and the error, returns whether v should be rounded up, down, or // whether the rounding direction can't be determined due to error. // error should be less than divisor / 2. -inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder, - uint64_t error) { +FMT_CONSTEXPR inline round_direction get_round_direction(uint64_t divisor, + uint64_t remainder, + uint64_t error) { FMT_ASSERT(remainder < divisor, ""); // divisor - remainder won't overflow. FMT_ASSERT(error < divisor, ""); // divisor - error won't overflow. FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow. @@ -626,19 +644,52 @@ enum result { }; } -inline uint64_t power_of_10_64(int exp) { - static constexpr const uint64_t data[] = {1, FMT_POWERS_OF_10(1), - FMT_POWERS_OF_10(1000000000ULL), - 10000000000000000000ULL}; - return data[exp]; -} +struct gen_digits_handler { + char* buf; + int size; + int precision; + int exp10; + bool fixed; + + FMT_CONSTEXPR digits::result on_digit(char digit, uint64_t divisor, + uint64_t remainder, uint64_t error, + bool integral) { + FMT_ASSERT(remainder < divisor, ""); + buf[size++] = digit; + if (!integral && error >= remainder) return digits::error; + if (size < precision) return digits::more; + if (!integral) { + // Check if error * 2 < divisor with overflow prevention. + // The check is not needed for the integral part because error = 1 + // and divisor > (1 << 32) there. + if (error >= divisor || error >= divisor - error) return digits::error; + } else { + FMT_ASSERT(error == 1 && divisor > 2, ""); + } + auto dir = get_round_direction(divisor, remainder, error); + if (dir != round_direction::up) + return dir == round_direction::down ? digits::done : digits::error; + ++buf[size - 1]; + for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] > '9') { + buf[0] = '1'; + if (fixed) + buf[size++] = '0'; + else + ++exp10; + } + return digits::done; + } +}; // Generates output using the Grisu digit-gen algorithm. // error: the size of the region (lower, upper) outside of which numbers // definitely do not round to value (Delta in Grisu3). -template -FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp, - Handler& handler) { +FMT_INLINE FMT_CONSTEXPR20 digits::result grisu_gen_digits( + fp value, uint64_t error, int& exp, gen_digits_handler& handler) { const fp one(1ULL << -value.e, value.e); // The integral part of scaled value (p1 in Grisu) = value / one. It cannot be // zero because it contains a product of two 64-bit numbers with MSB set (due @@ -649,10 +700,28 @@ FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp, // The fractional part of scaled value (p2 in Grisu) c = value % one. uint64_t fractional = value.f & (one.f - 1); exp = count_digits(integral); // kappa in Grisu. - // Divide by 10 to prevent overflow. - auto result = handler.on_start(power_of_10_64(exp - 1) << -one.e, - value.f / 10, error * 10, exp); - if (result != digits::more) return result; + // Non-fixed formats require at least one digit and no precision adjustment. + if (handler.fixed) { + // Adjust fixed precision by exponent because it is relative to decimal + // point. + int precision_offset = exp + handler.exp10; + if (precision_offset > 0 && + handler.precision > max_value() - precision_offset) { + FMT_THROW(format_error("number is too big")); + } + handler.precision += precision_offset; + // Check if precision is satisfied just by leading zeros, e.g. + // format("{:.2f}", 0.001) gives "0.00" without generating any digits. + if (handler.precision <= 0) { + if (handler.precision < 0) return digits::done; + // Divide by 10 to prevent overflow. + uint64_t divisor = impl_data::power_of_10_64[exp - 1] << -one.e; + auto dir = get_round_direction(divisor, value.f / 10, error * 10); + if (dir == round_direction::unknown) return digits::error; + handler.buf[handler.size++] = dir == round_direction::up ? '1' : '0'; + return digits::done; + } + } // Generate digits for the integral part. This can produce up to 10 digits. do { uint32_t digit = 0; @@ -699,9 +768,9 @@ FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp, } --exp; auto remainder = (static_cast(integral) << -one.e) + fractional; - result = handler.on_digit(static_cast('0' + digit), - power_of_10_64(exp) << -one.e, remainder, error, - exp, true); + auto result = handler.on_digit(static_cast('0' + digit), + impl_data::power_of_10_64[exp] << -one.e, + remainder, error, true); if (result != digits::more) return result; } while (exp > 0); // Generate digits for the fractional part. @@ -711,69 +780,11 @@ FMT_INLINE digits::result grisu_gen_digits(fp value, uint64_t error, int& exp, char digit = static_cast('0' + (fractional >> -one.e)); fractional &= one.f - 1; --exp; - result = handler.on_digit(digit, one.f, fractional, error, exp, false); + auto result = handler.on_digit(digit, one.f, fractional, error, false); if (result != digits::more) return result; } } -// The fixed precision digit handler. -struct fixed_handler { - char* buf; - int size; - int precision; - int exp10; - bool fixed; - - digits::result on_start(uint64_t divisor, uint64_t remainder, uint64_t error, - int& exp) { - // Non-fixed formats require at least one digit and no precision adjustment. - if (!fixed) return digits::more; - // Adjust fixed precision by exponent because it is relative to decimal - // point. - precision += exp + exp10; - // Check if precision is satisfied just by leading zeros, e.g. - // format("{:.2f}", 0.001) gives "0.00" without generating any digits. - if (precision > 0) return digits::more; - if (precision < 0) return digits::done; - auto dir = get_round_direction(divisor, remainder, error); - if (dir == round_direction::unknown) return digits::error; - buf[size++] = dir == round_direction::up ? '1' : '0'; - return digits::done; - } - - digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder, - uint64_t error, int, bool integral) { - FMT_ASSERT(remainder < divisor, ""); - buf[size++] = digit; - if (!integral && error >= remainder) return digits::error; - if (size < precision) return digits::more; - if (!integral) { - // Check if error * 2 < divisor with overflow prevention. - // The check is not needed for the integral part because error = 1 - // and divisor > (1 << 32) there. - if (error >= divisor || error >= divisor - error) return digits::error; - } else { - FMT_ASSERT(error == 1 && divisor > 2, ""); - } - auto dir = get_round_direction(divisor, remainder, error); - if (dir != round_direction::up) - return dir == round_direction::down ? digits::done : digits::error; - ++buf[size - 1]; - for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { - buf[i] = '0'; - ++buf[i - 1]; - } - if (buf[0] > '9') { - buf[0] = '1'; - if (fixed) - buf[size++] = '0'; - else - ++exp10; - } - return digits::done; - } -}; - // A 128-bit integer type used internally, struct uint128_wrapper { uint128_wrapper() = default; @@ -897,8 +908,7 @@ inline uint64_t umul96_lower64(uint32_t x, uint64_t y) FMT_NOEXCEPT { inline int floor_log10_pow2(int e) FMT_NOEXCEPT { FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); const int shift = 22; - return (e * static_cast(data::log10_2_significand >> (64 - shift))) >> - shift; + return (e * static_cast(log10_2_significand >> (64 - shift))) >> shift; } // Various fast log computations. @@ -916,8 +926,7 @@ inline int floor_log10_pow2_minus_log10_4_over_3(int e) FMT_NOEXCEPT { FMT_ASSERT(e <= 1700 && e >= -1700, "too large exponent"); const uint64_t log10_4_over_3_fractional_digits = 0x1ffbfc2bbc780375; const int shift_amount = 22; - return (e * static_cast(data::log10_2_significand >> - (64 - shift_amount)) - + return (e * static_cast(log10_2_significand >> (64 - shift_amount)) - static_cast(log10_4_over_3_fractional_digits >> (64 - shift_amount))) >> shift_amount; @@ -1042,7 +1051,7 @@ template <> struct cache_accessor { static uint64_t get_cached_power(int k) FMT_NOEXCEPT { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); - constexpr const uint64_t pow10_significands[] = { + static constexpr const uint64_t pow10_significands[] = { 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, @@ -2210,24 +2219,21 @@ small_divisor_case_label: } } // namespace dragonbox -// Formats value using a variation of the Fixed-Precision Positive -// Floating-Point Printout ((FPP)^2) algorithm by Steele & White: +// Formats a floating-point number using a variation of the Fixed-Precision +// Positive Floating-Point Printout ((FPP)^2) algorithm by Steele & White: // https://fmt.dev/papers/p372-steele.pdf. -template -void fallback_format(Double d, int num_digits, bool binary32, buffer& buf, - int& exp10) { +FMT_CONSTEXPR20 inline void format_dragon(fp value, bool is_predecessor_closer, + int num_digits, buffer& buf, + int& exp10) { bigint numerator; // 2 * R in (FPP)^2. bigint denominator; // 2 * S in (FPP)^2. // lower and upper are differences between value and corresponding boundaries. bigint lower; // (M^- in (FPP)^2). bigint upper_store; // upper's value if different from lower. bigint* upper = nullptr; // (M^+ in (FPP)^2). - fp value; // Shift numerator and denominator by an extra bit or two (if lower boundary // is closer) to make lower and upper integers. This eliminates multiplication // by 2 during later computations. - const bool is_predecessor_closer = - binary32 ? value.assign(static_cast(d)) : value.assign(d); int shift = is_predecessor_closer ? 2 : 1; uint64_t significand = value.f << shift; if (value.e >= 0) { @@ -2297,9 +2303,9 @@ void fallback_format(Double d, int num_digits, bool binary32, buffer& buf, // Generate the given number of digits. exp10 -= num_digits - 1; if (num_digits == 0) { - buf.try_resize(1); denominator *= 10; - buf[0] = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + auto digit = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0'; + buf.push_back(digit); return; } buf.try_resize(to_unsigned(num_digits)); @@ -2330,9 +2336,12 @@ void fallback_format(Double d, int num_digits, bool binary32, buffer& buf, buf[num_digits - 1] = static_cast('0' + digit); } -template -int format_float(T value, int precision, float_specs specs, buffer& buf) { - static_assert(!std::is_same::value, ""); +template +FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, + float_specs specs, + buffer& buf) { + // float is passed as double to reduce the number of instantiations. + static_assert(!std::is_same::value, ""); FMT_ASSERT(value >= 0, "value is negative"); const bool fixed = specs.format == float_format::fixed; @@ -2342,13 +2351,13 @@ int format_float(T value, int precision, float_specs specs, buffer& buf) { return 0; } buf.try_resize(to_unsigned(precision)); - std::uninitialized_fill_n(buf.data(), precision, '0'); + fill_n(buf.data(), precision, '0'); return -precision; } - if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf); + if (specs.fallback) return snprintf_float(value, precision, specs, buf); - if (precision < 0) { + if (!is_constant_evaluated() && precision < 0) { // Use Dragonbox for the shortest format. if (specs.binary32) { auto dec = dragonbox::to_decimal(static_cast(value)); @@ -2360,26 +2369,37 @@ int format_float(T value, int precision, float_specs specs, buffer& buf) { return dec.exponent; } - // Use Grisu + Dragon4 for the given precision: - // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. int exp = 0; - const int min_exp = -60; // alpha in Grisu. - int cached_exp10 = 0; // K in Grisu. - fp normalized = normalize(fp(value)); - const auto cached_pow = get_cached_power( - min_exp - (normalized.e + fp::significand_size), cached_exp10); - normalized = normalized * cached_pow; - // Limit precision to the maximum possible number of significant digits in an - // IEEE754 double because we don't need to generate zeros. - const int max_double_digits = 767; - if (precision > max_double_digits) precision = max_double_digits; - fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; - if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) { - exp += handler.size - cached_exp10 - 1; - fallback_format(value, handler.precision, specs.binary32, buf, exp); - } else { - exp += handler.exp10; - buf.try_resize(to_unsigned(handler.size)); + bool use_dragon = true; + if (is_fast_float()) { + // Use Grisu + Dragon4 for the given precision: + // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. + const int min_exp = -60; // alpha in Grisu. + int cached_exp10 = 0; // K in Grisu. + fp normalized = normalize(fp(value)); + const auto cached_pow = get_cached_power( + min_exp - (normalized.e + fp::num_significand_bits), cached_exp10); + normalized = normalized * cached_pow; + gen_digits_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; + if (grisu_gen_digits(normalized, 1, exp, handler) != digits::error && + !is_constant_evaluated()) { + exp += handler.exp10; + buf.try_resize(to_unsigned(handler.size)); + use_dragon = false; + } else { + exp += handler.size - cached_exp10 - 1; + precision = handler.precision; + } + } + if (use_dragon) { + auto f = fp(); + bool is_predecessor_closer = + specs.binary32 ? f.assign(static_cast(value)) : f.assign(value); + // Limit precision to the maximum possible number of significant digits in + // an IEEE754 double because we don't need to generate zeros. + const int max_double_digits = 767; + if (precision > max_double_digits) precision = max_double_digits; + format_dragon(f, is_predecessor_closer, precision, buf, exp); } if (!fixed && !specs.showpoint) { // Remove trailing zeros. @@ -2391,7 +2411,7 @@ int format_float(T value, int precision, float_specs specs, buffer& buf) { buf.try_resize(num_digits); } return exp; -} // namespace detail +} template int snprintf_float(T value, int precision, float_specs specs, @@ -2525,8 +2545,8 @@ template <> struct formatter { }; FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { - for_each_codepoint(s, [this](uint32_t cp, int error) { - if (error != 0) FMT_THROW(std::runtime_error("invalid utf8")); + for_each_codepoint(s, [this](uint32_t cp, string_view) { + if (cp == invalid_code_point) FMT_THROW(std::runtime_error("invalid utf8")); if (cp <= 0xFFFF) { buffer_.push_back(static_cast(cp)); } else { @@ -2534,6 +2554,7 @@ FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) { buffer_.push_back(static_cast(0xD800 + (cp >> 10))); buffer_.push_back(static_cast(0xDC00 + (cp & 0x3FF))); } + return true; }); buffer_.push_back(0); } @@ -2549,15 +2570,17 @@ FMT_FUNC void format_system_error(detail::buffer& out, int error_code, format_error_code(out, error_code, message); } -FMT_FUNC void detail::error_handler::on_error(const char* message) { - FMT_THROW(format_error(message)); -} - FMT_FUNC void report_system_error(int error_code, const char* message) FMT_NOEXCEPT { report_error(format_system_error, error_code, message); } +// DEPRECATED! +// This function is defined here and not inline for ABI compatiblity. +FMT_FUNC void detail::error_handler::on_error(const char* message) { + throw_format_error(message); +} + FMT_FUNC std::string vformat(string_view fmt, format_args args) { // Don't optimize the "{}" case to keep the binary size small and because it // can be better optimized in fmt::format anyway. diff --git a/include/spdlog/fmt/bundled/format.h b/include/spdlog/fmt/bundled/format.h index 5398a23a..ee69651c 100644 --- a/include/spdlog/fmt/bundled/format.h +++ b/include/spdlog/fmt/bundled/format.h @@ -41,14 +41,16 @@ #include // std::system_error #include // std::swap +#ifdef __cpp_lib_bit_cast +# include // std::bitcast +#endif + #include "core.h" -#ifdef __INTEL_COMPILER -# define FMT_ICC_VERSION __INTEL_COMPILER -#elif defined(__ICL) -# define FMT_ICC_VERSION __ICL +#if FMT_GCC_VERSION +# define FMT_GCC_VISIBILITY_HIDDEN __attribute__((visibility("hidden"))) #else -# define FMT_ICC_VERSION 0 +# define FMT_GCC_VISIBILITY_HIDDEN #endif #ifdef __NVCC__ @@ -108,17 +110,11 @@ FMT_END_NAMESPACE # define FMT_CATCH(x) if (false) #endif -#ifndef FMT_DEPRECATED -# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900 -# define FMT_DEPRECATED [[deprecated]] +#ifndef FMT_MAYBE_UNUSED +# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused) +# define FMT_MAYBE_UNUSED [[maybe_unused]] # else -# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) -# define FMT_DEPRECATED __attribute__((deprecated)) -# elif FMT_MSC_VER -# define FMT_DEPRECATED __declspec(deprecated) -# else -# define FMT_DEPRECATED /* deprecated */ -# endif +# define FMT_MAYBE_UNUSED # endif #endif @@ -149,18 +145,25 @@ FMT_END_NAMESPACE #endif // __builtin_clz is broken in clang with Microsoft CodeGen: -// https://github.com/fmtlib/fmt/issues/519 -#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clz)) && !FMT_MSC_VER -# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) -#endif -#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_clzll)) && !FMT_MSC_VER -# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) -#endif -#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctz)) -# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) +// https://github.com/fmtlib/fmt/issues/519. +#if !FMT_MSC_VER +# if FMT_HAS_BUILTIN(__builtin_clz) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +# endif +# if FMT_HAS_BUILTIN(__builtin_clzll) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +# endif #endif -#if (FMT_GCC_VERSION || FMT_HAS_BUILTIN(__builtin_ctzll)) -# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) + +// __builtin_ctz is broken in Intel Compiler Classic on Windows: +// https://github.com/fmtlib/fmt/issues/2510. +#ifndef __ICL +# if FMT_HAS_BUILTIN(__builtin_ctz) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CTZ(n) __builtin_ctz(n) +# endif +# if FMT_HAS_BUILTIN(__builtin_ctzll) || FMT_GCC_VERSION || FMT_ICC_VERSION +# define FMT_BUILTIN_CTZLL(n) __builtin_ctzll(n) +# endif #endif #if FMT_MSC_VER @@ -175,7 +178,6 @@ FMT_BEGIN_NAMESPACE namespace detail { // Avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning. # if !defined(__clang__) -# pragma managed(push, off) # pragma intrinsic(_BitScanForward) # pragma intrinsic(_BitScanReverse) # if defined(_WIN64) @@ -237,40 +239,75 @@ inline auto ctzll(uint64_t x) -> int { return static_cast(r); } # define FMT_BUILTIN_CTZLL(n) detail::ctzll(n) -# if !defined(__clang__) -# pragma managed(pop) -# endif } // namespace detail FMT_END_NAMESPACE #endif +#ifdef FMT_HEADER_ONLY +# define FMT_HEADER_ONLY_CONSTEXPR20 FMT_CONSTEXPR20 +#else +# define FMT_HEADER_ONLY_CONSTEXPR20 +#endif + FMT_BEGIN_NAMESPACE namespace detail { -#if __cplusplus >= 202002L || \ - (__cplusplus >= 201709L && FMT_GCC_VERSION >= 1002) -# define FMT_CONSTEXPR20 constexpr -#else -# define FMT_CONSTEXPR20 -#endif +template class formatbuf : public Streambuf { + private: + using char_type = typename Streambuf::char_type; + using streamsize = decltype(std::declval().sputn(nullptr, 0)); + using int_type = typename Streambuf::int_type; + using traits_type = typename Streambuf::traits_type; + + buffer& buffer_; -// An equivalent of `*reinterpret_cast(&source)` that doesn't have -// undefined behavior (e.g. due to type aliasing). -// Example: uint64_t d = bit_cast(2.718); -template -inline auto bit_cast(const Source& source) -> Dest { - static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); - Dest dest; - std::memcpy(&dest, &source, sizeof(dest)); - return dest; + public: + explicit formatbuf(buffer& buf) : buffer_(buf) {} + + protected: + // The put area is always empty. This makes the implementation simpler and has + // the advantage that the streambuf and the buffer are always in sync and + // sputc never writes into uninitialized memory. A disadvantage is that each + // call to sputc always results in a (virtual) call to overflow. There is no + // disadvantage here for sputn since this always results in a call to xsputn. + + auto overflow(int_type ch) -> int_type override { + if (!traits_type::eq_int_type(ch, traits_type::eof())) + buffer_.push_back(static_cast(ch)); + return ch; + } + + auto xsputn(const char_type* s, streamsize count) -> streamsize override { + buffer_.append(s, s + count); + return count; + } +}; + +// Implementation of std::bit_cast for pre-C++20. +template +FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To { + static_assert(sizeof(To) == sizeof(From), "size mismatch"); +#ifdef __cpp_lib_bit_cast + if (is_constant_evaluated()) return std::bit_cast(from); +#endif + auto to = To(); + std::memcpy(&to, &from, sizeof(to)); + return to; } inline auto is_big_endian() -> bool { - const auto u = 1u; +#ifdef _WIN32 + return false; +#elif defined(__BIG_ENDIAN__) + return true; +#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) + return __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__; +#else struct bytes { - char data[sizeof(u)]; + char data[sizeof(int)]; }; - return bit_cast(u).data[0] == 0; + return bit_cast(1).data[0] == 0; +#endif } // A fallback implementation of uintptr_t for systems that lack it. @@ -280,7 +317,7 @@ struct fallback_uintptr { fallback_uintptr() = default; explicit fallback_uintptr(const void* p) { *this = bit_cast(p); - if (is_big_endian()) { + if (const_check(is_big_endian())) { for (size_t i = 0, j = sizeof(void*) - 1; i < j; ++i, --j) std::swap(value[i], value[j]); } @@ -339,12 +376,15 @@ inline auto get_data(Container& c) -> typename Container::value_type* { #if defined(_SECURE_SCL) && _SECURE_SCL // Make a checked iterator to avoid MSVC warnings. template using checked_ptr = stdext::checked_array_iterator; -template auto make_checked(T* p, size_t size) -> checked_ptr { +template +constexpr auto make_checked(T* p, size_t size) -> checked_ptr { return {p, size}; } #else template using checked_ptr = T*; -template inline auto make_checked(T* p, size_t) -> T* { return p; } +template constexpr auto make_checked(T* p, size_t) -> T* { + return p; +} #endif // Attempts to reserve space for n extra characters in the output range. @@ -480,27 +520,38 @@ FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) return next; } +constexpr uint32_t invalid_code_point = ~uint32_t(); + +// Invokes f(cp, sv) for every code point cp in s with sv being the string view +// corresponding to the code point. cp is invalid_code_point on error. template FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { - auto decode = [f](const char* p) { + auto decode = [f](const char* buf_ptr, const char* ptr) { auto cp = uint32_t(); auto error = 0; - p = utf8_decode(p, &cp, &error); - f(cp, error); - return p; + auto end = utf8_decode(buf_ptr, &cp, &error); + bool result = f(error ? invalid_code_point : cp, + string_view(ptr, to_unsigned(end - buf_ptr))); + return result ? end : nullptr; }; auto p = s.data(); const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars. if (s.size() >= block_size) { - for (auto end = p + s.size() - block_size + 1; p < end;) p = decode(p); + for (auto end = p + s.size() - block_size + 1; p < end;) { + p = decode(p, p); + if (!p) return; + } } if (auto num_chars_left = s.data() + s.size() - p) { char buf[2 * block_size - 1] = {}; copy_str(p, p + num_chars_left, buf); - p = buf; + const char* buf_ptr = buf; do { - p = decode(p); - } while (p - buf < num_chars_left); + auto end = decode(buf_ptr, p); + if (!end) return; + p += end - buf_ptr; + buf_ptr = end; + } while (buf_ptr - buf < num_chars_left); } } @@ -515,10 +566,10 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) { // It is not a lambda for compatibility with C++14. struct count_code_points { size_t* count; - FMT_CONSTEXPR void operator()(uint32_t cp, int error) const { + FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool { *count += detail::to_unsigned( 1 + - (error == 0 && cp >= 0x1100 && + (cp >= 0x1100 && (cp <= 0x115f || // Hangul Jamo init. consonants cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET @@ -536,6 +587,7 @@ FMT_CONSTEXPR inline size_t compute_width(string_view s) { (cp >= 0x1f300 && cp <= 0x1f64f) || // Supplemental Symbols and Pictographs: (cp >= 0x1f900 && cp <= 0x1f9ff)))); + return true; } }; for_each_codepoint(s, count_code_points{&num_code_points}); @@ -564,9 +616,10 @@ inline auto code_point_index(basic_string_view s, size_t n) return s.size(); } -template -using is_fast_float = bool_constant::is_iec559 && - sizeof(T) <= sizeof(double)>; +template ::value> +struct is_fast_float : bool_constant::is_iec559 && + sizeof(T) <= sizeof(double)> {}; +template struct is_fast_float : std::false_type {}; #ifndef FMT_USE_FULL_CACHE_DRAGONBOX # define FMT_USE_FULL_CACHE_DRAGONBOX 0 @@ -607,8 +660,8 @@ enum { inline_buffer_size = 500 }; **Example**:: - fmt::memory_buffer out; - format_to(out, "The answer is {}.", 42); + auto out = fmt::memory_buffer(); + format_to(std::back_inserter(out), "The answer is {}.", 42); This will append the following output to the ``out`` object: @@ -629,34 +682,43 @@ class basic_memory_buffer final : public detail::buffer { Allocator alloc_; // Deallocate memory allocated by the buffer. - void deallocate() { + FMT_CONSTEXPR20 void deallocate() { T* data = this->data(); if (data != store_) alloc_.deallocate(data, this->capacity()); } protected: - void grow(size_t size) final FMT_OVERRIDE; + FMT_CONSTEXPR20 void grow(size_t size) override; public: using value_type = T; using const_reference = const T&; - explicit basic_memory_buffer(const Allocator& alloc = Allocator()) + FMT_CONSTEXPR20 explicit basic_memory_buffer( + const Allocator& alloc = Allocator()) : alloc_(alloc) { this->set(store_, SIZE); + if (detail::is_constant_evaluated()) { + detail::fill_n(store_, SIZE, T{}); + } } - ~basic_memory_buffer() { deallocate(); } + FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); } private: // Move data from other to this buffer. - void move(basic_memory_buffer& other) { + FMT_CONSTEXPR20 void move(basic_memory_buffer& other) { alloc_ = std::move(other.alloc_); T* data = other.data(); size_t size = other.size(), capacity = other.capacity(); if (data == other.store_) { this->set(store_, capacity); - std::uninitialized_copy(other.store_, other.store_ + size, - detail::make_checked(store_, capacity)); + if (detail::is_constant_evaluated()) { + detail::copy_str(other.store_, other.store_ + size, + detail::make_checked(store_, capacity)); + } else { + std::uninitialized_copy(other.store_, other.store_ + size, + detail::make_checked(store_, capacity)); + } } else { this->set(data, capacity); // Set pointer to the inline array so that delete is not called @@ -673,7 +735,10 @@ class basic_memory_buffer final : public detail::buffer { of the other object to it. \endrst */ - basic_memory_buffer(basic_memory_buffer&& other) FMT_NOEXCEPT { move(other); } + FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) + FMT_NOEXCEPT { + move(other); + } /** \rst @@ -695,7 +760,7 @@ class basic_memory_buffer final : public detail::buffer { Resizes the buffer to contain *count* elements. If T is a POD type new elements may not be initialized. */ - void resize(size_t count) { this->try_resize(count); } + FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); } /** Increases the buffer capacity to *new_capacity*. */ void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } @@ -709,7 +774,8 @@ class basic_memory_buffer final : public detail::buffer { }; template -void basic_memory_buffer::grow(size_t size) { +FMT_CONSTEXPR20 void basic_memory_buffer::grow( + size_t size) { #ifdef FMT_FUZZ if (size > 5000) throw std::runtime_error("fuzz mode - won't grow that much"); #endif @@ -754,7 +820,7 @@ class FMT_API format_error : public std::runtime_error { format_error& operator=(const format_error&) = default; format_error(format_error&&) = default; format_error& operator=(format_error&&) = default; - ~format_error() FMT_NOEXCEPT FMT_OVERRIDE FMT_MSC_DEFAULT; + ~format_error() FMT_NOEXCEPT override FMT_MSC_DEFAULT; }; /** @@ -807,10 +873,6 @@ constexpr auto compile_string_to_view(detail::std_string_view s) FMT_BEGIN_DETAIL_NAMESPACE -inline void throw_format_error(const char* message) { - FMT_THROW(format_error(message)); -} - template struct is_integral : std::is_integral {}; template <> struct is_integral : std::true_type {}; template <> struct is_integral : std::true_type {}; @@ -853,48 +915,23 @@ using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; (factor)*1000000, (factor)*10000000, (factor)*100000000, \ (factor)*1000000000 -// Static data is placed in this class template for the header-only config. -template struct basic_data { - // log10(2) = 0x0.4d104d427de7fbcc... - static const uint64_t log10_2_significand = 0x4d104d427de7fbcc; - - // GCC generates slightly better code for pairs than chars. - FMT_API static constexpr const char digits[100][2] = { - {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, - {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, - {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, - {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, - {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, - {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, - {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, - {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, - {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, - {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, - {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, - {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, - {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, - {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, - {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, - {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, - {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; - - FMT_API static constexpr const char hex_digits[] = "0123456789abcdef"; - FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '}; - FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', - 0x1000000u | ' '}; - FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1, - 0}; - FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1, - 0}; -}; +// Converts value in the range [0, 100) to a string. +constexpr const char* digits2(size_t value) { + // GCC generates slightly better code when value is pointer-size. + return &"0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"[value * 2]; +} -#ifdef FMT_SHARED -// Required for -flto, -fivisibility=hidden and -shared to work -extern template struct basic_data; +// Sign is a template parameter to workaround a bug in gcc 4.8. +template constexpr Char sign(Sign s) { +#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604 + static_assert(std::is_same::value, ""); #endif - -// This is a struct rather than an alias to avoid shadowing warnings in gcc. -struct data : basic_data<> {}; + return static_cast("\0-+ "[s]); +} template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { int count = 1; @@ -916,23 +953,33 @@ FMT_CONSTEXPR inline auto count_digits(uint128_t n) -> int { } #endif +#ifdef FMT_BUILTIN_CLZLL +// It is a separate function rather than a part of count_digits to workaround +// the lack of static constexpr in constexpr functions. +inline auto do_count_digits(uint64_t n) -> int { + // This has comparable performance to the version by Kendall Willets + // (https://github.com/fmtlib/format-benchmark/blob/master/digits10) + // but uses smaller tables. + // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). + static constexpr uint8_t bsr2log10[] = { + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, + 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; + auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; + static constexpr const uint64_t zero_or_powers_of_10[] = { + 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), + 10000000000000000000ULL}; + return t - (n < zero_or_powers_of_10[t]); +} +#endif + // Returns the number of decimal digits in n. Leading zeros are not counted // except for n == 0 in which case count_digits returns 1. FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { #ifdef FMT_BUILTIN_CLZLL if (!is_constant_evaluated()) { - // https://github.com/fmtlib/format-benchmark/blob/master/digits10 - // Maps bsr(n) to ceil(log10(pow(2, bsr(n) + 1) - 1)). - constexpr uint16_t bsr2log10[] = { - 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, - 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, - 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, - 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; - auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; - constexpr const uint64_t zero_or_powers_of_10[] = { - 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), - 10000000000000000000ULL}; - return t - (n < zero_or_powers_of_10[t]); + return do_count_digits(n); } #endif return count_digits_fallback(n); @@ -945,21 +992,25 @@ FMT_CONSTEXPR auto count_digits(UInt n) -> int { if (num_bits() == 32) return (FMT_BUILTIN_CLZ(static_cast(n) | 1) ^ 31) / BITS + 1; #endif - int num_digits = 0; - do { - ++num_digits; - } while ((n >>= BITS) != 0); - return num_digits; + // Lambda avoids unreachable code warnings from NVHPC. + return [](UInt m) { + int num_digits = 0; + do { + ++num_digits; + } while ((m >>= BITS) != 0); + return num_digits; + }(n); } template <> auto count_digits<4>(detail::fallback_uintptr n) -> int; +#ifdef FMT_BUILTIN_CLZ // It is a separate function rather than a part of count_digits to workaround // the lack of static constexpr in constexpr functions. -FMT_INLINE uint64_t count_digits_inc(int n) { - // An optimization by Kendall Willets from https://bit.ly/3uOIQrB. - // This increments the upper 32 bits (log10(T) - 1) when >= T is added. -#define FMT_INC(T) (((sizeof(#T) - 1ull) << 32) - T) +FMT_INLINE auto do_count_digits(uint32_t n) -> int { +// An optimization by Kendall Willets from https://bit.ly/3uOIQrB. +// This increments the upper 32 bits (log10(T) - 1) when >= T is added. +# define FMT_INC(T) (((sizeof(# T) - 1ull) << 32) - T) static constexpr uint64_t table[] = { FMT_INC(0), FMT_INC(0), FMT_INC(0), // 8 FMT_INC(10), FMT_INC(10), FMT_INC(10), // 64 @@ -973,15 +1024,16 @@ FMT_INLINE uint64_t count_digits_inc(int n) { FMT_INC(1000000000), FMT_INC(1000000000), FMT_INC(1000000000), // 1024M FMT_INC(1000000000), FMT_INC(1000000000) // 4B }; - return table[n]; + auto inc = table[FMT_BUILTIN_CLZ(n | 1) ^ 31]; + return static_cast((n + inc) >> 32); } +#endif // Optional version of count_digits for better performance on 32-bit platforms. FMT_CONSTEXPR20 inline auto count_digits(uint32_t n) -> int { #ifdef FMT_BUILTIN_CLZ if (!is_constant_evaluated()) { - auto inc = count_digits_inc(FMT_BUILTIN_CLZ(n | 1) ^ 31); - return static_cast((n + inc) >> 32); + return do_count_digits(n); } #endif return count_digits_fallback(n); @@ -1032,11 +1084,15 @@ inline auto equal2(const char* lhs, const char* rhs) -> bool { } // Copies two characters from src to dst. -template void copy2(Char* dst, const char* src) { +template +FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) { + if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) { + memcpy(dst, src, 2); + return; + } *dst++ = static_cast(*src++); *dst = static_cast(*src); } -FMT_INLINE void copy2(char* dst, const char* src) { memcpy(dst, src, 2); } template struct format_decimal_result { Iterator begin; @@ -1052,20 +1108,12 @@ FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) FMT_ASSERT(size >= count_digits(value), "invalid digit count"); out += size; Char* end = out; - if (is_constant_evaluated()) { - while (value >= 10) { - *--out = static_cast('0' + value % 10); - value /= 10; - } - *--out = static_cast('0' + value); - return {out, end}; - } while (value >= 100) { // Integer division is slow so do it for a group of two digits instead // of for every digit. The idea comes from the talk by Alexandrescu // "Three Optimization Tips for C++". See speed-test for a comparison. out -= 2; - copy2(out, data::digits[value % 100]); + copy2(out, digits2(static_cast(value % 100))); value /= 100; } if (value < 10) { @@ -1073,7 +1121,7 @@ FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) return {out, end}; } out -= 2; - copy2(out, data::digits[value]); + copy2(out, digits2(static_cast(value))); return {out, end}; } @@ -1093,7 +1141,7 @@ FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, buffer += num_digits; Char* end = buffer; do { - const char* digits = upper ? "0123456789ABCDEF" : data::hex_digits; + const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; unsigned digit = (value & ((1 << BASE_BITS) - 1)); *--buffer = static_cast(BASE_BITS < 4 ? static_cast('0' + digit) : digits[digit]); @@ -1116,7 +1164,7 @@ auto format_uint(Char* buffer, detail::fallback_uintptr n, int num_digits, auto p = buffer; for (int i = 0; i < char_digits; ++i) { unsigned digit = (value & ((1 << BASE_BITS) - 1)); - *--p = static_cast(data::hex_digits[digit]); + *--p = static_cast("0123456789abcdef"[digit]); value >>= BASE_BITS; } } @@ -1226,7 +1274,7 @@ constexpr auto exponent_mask() -> // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. template -auto write_exponent(int exp, It it) -> It { +FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It { FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); if (exp < 0) { *it++ = static_cast('-'); @@ -1235,28 +1283,31 @@ auto write_exponent(int exp, It it) -> It { *it++ = static_cast('+'); } if (exp >= 100) { - const char* top = data::digits[exp / 100]; + const char* top = digits2(to_unsigned(exp / 100)); if (exp >= 1000) *it++ = static_cast(top[0]); *it++ = static_cast(top[1]); exp %= 100; } - const char* d = data::digits[exp]; + const char* d = digits2(to_unsigned(exp)); *it++ = static_cast(d[0]); *it++ = static_cast(d[1]); return it; } template -auto format_float(T value, int precision, float_specs specs, buffer& buf) - -> int; +FMT_HEADER_ONLY_CONSTEXPR20 auto format_float(T value, int precision, + float_specs specs, + buffer& buf) -> int; // Formats a floating-point number with snprintf. template auto snprintf_float(T value, int precision, float_specs specs, buffer& buf) -> int; -template auto promote_float(T value) -> T { return value; } -inline auto promote_float(float value) -> double { +template constexpr auto promote_float(T value) -> T { + return value; +} +constexpr auto promote_float(float value) -> double { return static_cast(value); } @@ -1282,8 +1333,9 @@ FMT_CONSTEXPR auto write_padded(OutputIt out, static_assert(align == align::left || align == align::right, ""); unsigned spec_width = to_unsigned(specs.width); size_t padding = spec_width > width ? spec_width - width : 0; - auto* shifts = align == align::left ? data::left_padding_shifts - : data::right_padding_shifts; + // Shifts are encoded as string literals because static constexpr is not + // supported in constexpr functions. + auto* shifts = align == align::left ? "\x1f\x1f\x00\x01" : "\x00\x1f\x00\x01"; size_t left_padding = padding >> shifts[specs.align]; size_t right_padding = padding - left_padding; auto it = reserve(out, size + padding * specs.fill.size()); @@ -1393,56 +1445,91 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, int num_digits, }); } +template class digit_grouping { + private: + thousands_sep_result sep_; + + struct next_state { + std::string::const_iterator group; + int pos; + }; + next_state initial_state() const { return {sep_.grouping.begin(), 0}; } + + // Returns the next digit group separator position. + int next(next_state& state) const { + if (!sep_.thousands_sep) return max_value(); + if (state.group == sep_.grouping.end()) + return state.pos += sep_.grouping.back(); + if (*state.group <= 0 || *state.group == max_value()) + return max_value(); + state.pos += *state.group++; + return state.pos; + } + + public: + explicit digit_grouping(locale_ref loc, bool localized = true) { + if (localized) + sep_ = thousands_sep(loc); + else + sep_.thousands_sep = Char(); + } + explicit digit_grouping(thousands_sep_result sep) : sep_(sep) {} + + Char separator() const { return sep_.thousands_sep; } + + int count_separators(int num_digits) const { + int count = 0; + auto state = initial_state(); + while (num_digits > next(state)) ++count; + return count; + } + + // Applies grouping to digits and write the output to out. + template + Out apply(Out out, basic_string_view digits) const { + auto num_digits = static_cast(digits.size()); + auto separators = basic_memory_buffer(); + separators.push_back(0); + auto state = initial_state(); + while (int i = next(state)) { + if (i >= num_digits) break; + separators.push_back(i); + } + for (int i = 0, sep_index = static_cast(separators.size() - 1); + i < num_digits; ++i) { + if (num_digits - i == separators[sep_index]) { + *out++ = separator(); + --sep_index; + } + *out++ = static_cast(digits[to_unsigned(i)]); + } + return out; + } +}; + template -auto write_int_localized(OutputIt& out, UInt value, unsigned prefix, - const basic_format_specs& specs, locale_ref loc) - -> bool { +auto write_int_localized(OutputIt out, UInt value, unsigned prefix, + const basic_format_specs& specs, + const digit_grouping& grouping) -> OutputIt { static_assert(std::is_same, UInt>::value, ""); - const auto sep_size = 1; - auto ts = thousands_sep(loc); - if (!ts.thousands_sep) return false; int num_digits = count_digits(value); - int size = num_digits, n = num_digits; - const std::string& groups = ts.grouping; - std::string::const_iterator group = groups.cbegin(); - while (group != groups.cend() && n > *group && *group > 0 && - *group != max_value()) { - size += sep_size; - n -= *group; - ++group; - } - if (group == groups.cend()) size += sep_size * ((n - 1) / groups.back()); char digits[40]; format_decimal(digits, value, num_digits); - basic_memory_buffer buffer; - if (prefix != 0) ++size; - const auto usize = to_unsigned(size); - buffer.resize(usize); - basic_string_view s(&ts.thousands_sep, sep_size); - // Index of a decimal digit with the least significant digit having index 0. - int digit_index = 0; - group = groups.cbegin(); - auto p = buffer.data() + size - 1; - for (int i = num_digits - 1; i > 0; --i) { - *p-- = static_cast(digits[i]); - if (*group <= 0 || ++digit_index % *group != 0 || - *group == max_value()) - continue; - if (group + 1 != groups.cend()) { - digit_index = 0; - ++group; - } - std::uninitialized_copy(s.data(), s.data() + s.size(), - make_checked(p, s.size())); - p -= s.size(); - } - *p-- = static_cast(*digits); - if (prefix != 0) *p = static_cast(prefix); - auto data = buffer.data(); - out = write_padded( - out, specs, usize, usize, [=](reserve_iterator it) { - return copy_str(data, data + size, it); + unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits + + grouping.count_separators(num_digits)); + return write_padded( + out, specs, size, size, [&](reserve_iterator it) { + if (prefix != 0) *it++ = static_cast(prefix); + return grouping.apply(it, string_view(digits, to_unsigned(num_digits))); }); +} + +template +auto write_int_localized(OutputIt& out, UInt value, unsigned prefix, + const basic_format_specs& specs, locale_ref loc) + -> bool { + auto grouping = digit_grouping(loc); + out = write_int_localized(out, value, prefix, specs, grouping); return true; } @@ -1465,7 +1552,9 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) prefix = 0x01000000 | '-'; abs_value = 0 - abs_value; } else { - prefix = data::prefixes[sign]; + constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', + 0x1000000u | ' '}; + prefix = prefixes[sign]; } return {abs_value, prefix}; } @@ -1477,10 +1566,9 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, static_assert(std::is_same>::value, ""); auto abs_value = arg.abs_value; auto prefix = arg.prefix; - auto utype = static_cast(specs.type); switch (specs.type) { - case 0: - case 'd': { + case presentation_type::none: + case presentation_type::dec: { if (specs.localized && write_int_localized(out, static_cast>(abs_value), prefix, specs, loc)) { @@ -1492,52 +1580,61 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, return format_decimal(it, abs_value, num_digits).end; }); } - case 'x': - case 'X': { - if (specs.alt) prefix_append(prefix, (utype << 8) | '0'); - bool upper = specs.type != 'x'; + case presentation_type::hex_lower: + case presentation_type::hex_upper: { + bool upper = specs.type == presentation_type::hex_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0'); int num_digits = count_digits<4>(abs_value); return write_int( out, num_digits, prefix, specs, [=](reserve_iterator it) { return format_uint<4, Char>(it, abs_value, num_digits, upper); }); } - case 'b': - case 'B': { - if (specs.alt) prefix_append(prefix, (utype << 8) | '0'); + case presentation_type::bin_lower: + case presentation_type::bin_upper: { + bool upper = specs.type == presentation_type::bin_upper; + if (specs.alt) + prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0'); int num_digits = count_digits<1>(abs_value); return write_int(out, num_digits, prefix, specs, [=](reserve_iterator it) { return format_uint<1, Char>(it, abs_value, num_digits); }); } - case 'o': { + case presentation_type::oct: { int num_digits = count_digits<3>(abs_value); - if (specs.alt && specs.precision <= num_digits && abs_value != 0) { - // Octal prefix '0' is counted as a digit, so only add it if precision - // is not greater than the number of digits. + // Octal prefix '0' is counted as a digit, so only add it if precision + // is not greater than the number of digits. + if (specs.alt && specs.precision <= num_digits && abs_value != 0) prefix_append(prefix, '0'); - } return write_int(out, num_digits, prefix, specs, [=](reserve_iterator it) { return format_uint<3, Char>(it, abs_value, num_digits); }); } - case 'c': + case presentation_type::chr: return write_char(out, static_cast(abs_value), specs); default: - FMT_THROW(format_error("invalid type specifier")); + throw_format_error("invalid type specifier"); } return out; } +template +FMT_CONSTEXPR FMT_NOINLINE auto write_int_noinline( + OutputIt out, write_int_arg arg, const basic_format_specs& specs, + locale_ref loc) -> OutputIt { + return write_int(out, arg, specs, loc); +} template ::value && !std::is_same::value && std::is_same>::value)> -FMT_CONSTEXPR auto write(OutputIt out, T value, - const basic_format_specs& specs, locale_ref loc) - -> OutputIt { - return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); +FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, + const basic_format_specs& specs, + locale_ref loc) -> OutputIt { + return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, + loc); } // An inlined version of write used in format string compilation. template -auto write_nonfinite(OutputIt out, bool isinf, basic_format_specs specs, - const float_specs& fspecs) -> OutputIt { +FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isinf, + basic_format_specs specs, + const float_specs& fspecs) -> OutputIt { auto str = isinf ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan"); constexpr size_t str_size = 3; @@ -1594,7 +1692,7 @@ auto write_nonfinite(OutputIt out, bool isinf, basic_format_specs specs, specs.fill.size() == 1 && *specs.fill.data() == static_cast('0'); if (is_zero_fill) specs.fill[0] = static_cast(' '); return write_padded(out, specs, size, [=](reserve_iterator it) { - if (sign) *it++ = static_cast(data::signs[sign]); + if (sign) *it++ = detail::sign(sign); return copy_str(str, str + str_size, it); }); } @@ -1606,7 +1704,7 @@ struct big_decimal_fp { int exponent; }; -inline auto get_significand_size(const big_decimal_fp& fp) -> int { +constexpr auto get_significand_size(const big_decimal_fp& fp) -> int { return fp.significand_size; } template @@ -1615,8 +1713,8 @@ inline auto get_significand_size(const dragonbox::decimal_fp& fp) -> int { } template -inline auto write_significand(OutputIt out, const char* significand, - int& significand_size) -> OutputIt { +constexpr auto write_significand(OutputIt out, const char* significand, + int significand_size) -> OutputIt { return copy_str(significand, significand + significand_size, out); } template @@ -1624,6 +1722,19 @@ inline auto write_significand(OutputIt out, UInt significand, int significand_size) -> OutputIt { return format_decimal(out, significand, significand_size).end; } +template +FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, + int significand_size, int exponent, + const Grouping& grouping) -> OutputIt { + if (!grouping.separator()) { + out = write_significand(out, significand, significand_size); + return detail::fill_n(out, exponent, static_cast('0')); + } + auto buffer = memory_buffer(); + write_significand(appender(buffer), significand, significand_size); + detail::fill_n(appender(buffer), exponent, '0'); + return grouping.apply(out, string_view(buffer.data(), buffer.size())); +} template ::value)> @@ -1631,14 +1742,20 @@ inline auto write_significand(Char* out, UInt significand, int significand_size, int integral_size, Char decimal_point) -> Char* { if (!decimal_point) return format_decimal(out, significand, significand_size).end; - auto end = format_decimal(out + 1, significand, significand_size).end; - if (integral_size == 1) { - out[0] = out[1]; - } else { - std::uninitialized_copy_n(out + 1, integral_size, - make_checked(out, to_unsigned(integral_size))); + out += significand_size + 1; + Char* end = out; + int floating_size = significand_size - integral_size; + for (int i = floating_size / 2; i > 0; --i) { + out -= 2; + copy2(out, digits2(significand % 100)); + significand /= 100; + } + if (floating_size % 2 != 0) { + *--out = static_cast('0' + significand % 10); + significand /= 10; } - out[integral_size] = decimal_point; + *--out = decimal_point; + format_decimal(out - integral_size, significand, integral_size); return end; } @@ -1655,9 +1772,9 @@ inline auto write_significand(OutputIt out, UInt significand, } template -inline auto write_significand(OutputIt out, const char* significand, - int significand_size, int integral_size, - Char decimal_point) -> OutputIt { +FMT_CONSTEXPR auto write_significand(OutputIt out, const char* significand, + int significand_size, int integral_size, + Char decimal_point) -> OutputIt { out = detail::copy_str_noinline(significand, significand + integral_size, out); if (!decimal_point) return out; @@ -1666,17 +1783,40 @@ inline auto write_significand(OutputIt out, const char* significand, significand + significand_size, out); } -template -auto write_float(OutputIt out, const DecimalFP& fp, - const basic_format_specs& specs, float_specs fspecs, - Char decimal_point) -> OutputIt { +template +FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, + int significand_size, int integral_size, + Char decimal_point, + const Grouping& grouping) -> OutputIt { + if (!grouping.separator()) { + return write_significand(out, significand, significand_size, integral_size, + decimal_point); + } + auto buffer = basic_memory_buffer(); + write_significand(buffer_appender(buffer), significand, + significand_size, integral_size, decimal_point); + grouping.apply( + out, basic_string_view(buffer.data(), to_unsigned(integral_size))); + return detail::copy_str_noinline(buffer.data() + integral_size, + buffer.end(), out); +} + +template > +FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& fp, + const basic_format_specs& specs, + float_specs fspecs, locale_ref loc) + -> OutputIt { auto significand = fp.significand; int significand_size = get_significand_size(fp); - static const Char zero = static_cast('0'); + constexpr Char zero = static_cast('0'); auto sign = fspecs.sign; size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); using iterator = reserve_iterator; + Char decimal_point = + fspecs.locale ? detail::decimal_point(loc) : static_cast('.'); + int output_exp = fp.exponent + significand_size - 1; auto use_exp_format = [=]() { if (fspecs.format == float_format::exp) return true; @@ -1703,7 +1843,7 @@ auto write_float(OutputIt out, const DecimalFP& fp, size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); char exp_char = fspecs.upper ? 'E' : 'e'; auto write = [=](iterator it) { - if (sign) *it++ = static_cast(data::signs[sign]); + if (sign) *it++ = detail::sign(sign); // Insert a decimal point after the first digit and add an exponent. it = write_significand(it, significand, significand_size, 1, decimal_point); @@ -1728,10 +1868,12 @@ auto write_float(OutputIt out, const DecimalFP& fp, if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1; if (num_zeros > 0) size += to_unsigned(num_zeros) + 1; } + auto grouping = Grouping(loc, fspecs.locale); + size += to_unsigned(grouping.count_separators(significand_size)); return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = static_cast(data::signs[sign]); - it = write_significand(it, significand, significand_size); - it = detail::fill_n(it, fp.exponent, zero); + if (sign) *it++ = detail::sign(sign); + it = write_significand(it, significand, significand_size, + fp.exponent, grouping); if (!fspecs.showpoint) return it; *it++ = decimal_point; return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; @@ -1740,10 +1882,12 @@ auto write_float(OutputIt out, const DecimalFP& fp, // 1234e-2 -> 12.34[0+] int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); + auto grouping = Grouping(loc, fspecs.locale); + size += to_unsigned(grouping.count_separators(significand_size)); return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = static_cast(data::signs[sign]); + if (sign) *it++ = detail::sign(sign); it = write_significand(it, significand, significand_size, exp, - decimal_point); + decimal_point, grouping); return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; }); } @@ -1756,7 +1900,7 @@ auto write_float(OutputIt out, const DecimalFP& fp, bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint; size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros); return write_padded(out, specs, size, [&](iterator it) { - if (sign) *it++ = static_cast(data::signs[sign]); + if (sign) *it++ = detail::sign(sign); *it++ = zero; if (!pointy) return it; *it++ = decimal_point; @@ -1765,26 +1909,97 @@ auto write_float(OutputIt out, const DecimalFP& fp, }); } +template class fallback_digit_grouping { + public: + constexpr fallback_digit_grouping(locale_ref, bool) {} + + constexpr Char separator() const { return Char(); } + + constexpr int count_separators(int) const { return 0; } + + template + constexpr Out apply(Out out, basic_string_view) const { + return out; + } +}; + +template +FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& fp, + const basic_format_specs& specs, + float_specs fspecs, locale_ref loc) + -> OutputIt { + if (is_constant_evaluated()) { + return do_write_float>(out, fp, specs, fspecs, + loc); + } else { + return do_write_float(out, fp, specs, fspecs, loc); + } +} + +template ::value)> +FMT_CONSTEXPR20 bool isinf(T value) { + if (is_constant_evaluated()) { +#if defined(__cpp_if_constexpr) + if constexpr (std::numeric_limits::is_iec559) { + auto bits = detail::bit_cast(static_cast(value)); + constexpr auto significand_bits = + dragonbox::float_info::significand_bits; + return (bits & exponent_mask()) && + !(bits & ((uint64_t(1) << significand_bits) - 1)); + } +#endif + } + return std::isinf(value); +} + +template ::value)> +FMT_CONSTEXPR20 bool isfinite(T value) { + if (is_constant_evaluated()) { +#if defined(__cpp_if_constexpr) + if constexpr (std::numeric_limits::is_iec559) { + auto bits = detail::bit_cast(static_cast(value)); + return (bits & exponent_mask()) != exponent_mask(); + } +#endif + } + return std::isfinite(value); +} + +template ::value)> +FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { + if (is_constant_evaluated()) { +#ifdef __cpp_if_constexpr + if constexpr (std::numeric_limits::is_iec559) { + auto bits = detail::bit_cast(static_cast(value)); + return (bits & (uint64_t(1) << (num_bits() - 1))) != 0; + } +#endif + } + return std::signbit(value); +} + template ::value)> -auto write(OutputIt out, T value, basic_format_specs specs, - locale_ref loc = {}) -> OutputIt { +FMT_CONSTEXPR20 auto write(OutputIt out, T value, + basic_format_specs specs, locale_ref loc = {}) + -> OutputIt { if (const_check(!is_supported_floating_point(value))) return out; float_specs fspecs = parse_float_type_spec(specs); fspecs.sign = specs.sign; - if (std::signbit(value)) { // value < 0 is false for NaN so use signbit. + if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit. fspecs.sign = sign::minus; value = -value; } else if (fspecs.sign == sign::minus) { fspecs.sign = sign::none; } - if (!std::isfinite(value)) - return write_nonfinite(out, std::isinf(value), specs, fspecs); + if (!detail::isfinite(value)) + return write_nonfinite(out, detail::isinf(value), specs, fspecs); if (specs.align == align::numeric && fspecs.sign) { auto it = reserve(out, 1); - *it++ = static_cast(data::signs[fspecs.sign]); + *it++ = detail::sign(fspecs.sign); out = base_iterator(out, it); fspecs.sign = sign::none; if (specs.width != 0) --specs.width; @@ -1792,31 +2007,35 @@ auto write(OutputIt out, T value, basic_format_specs specs, memory_buffer buffer; if (fspecs.format == float_format::hex) { - if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]); + if (fspecs.sign) buffer.push_back(detail::sign(fspecs.sign)); snprintf_float(promote_float(value), specs.precision, fspecs, buffer); return write_bytes(out, {buffer.data(), buffer.size()}, specs); } - int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; + int precision = specs.precision >= 0 || specs.type == presentation_type::none + ? specs.precision + : 6; if (fspecs.format == float_format::exp) { if (precision == max_value()) - FMT_THROW(format_error("number is too big")); + throw_format_error("number is too big"); else ++precision; } if (const_check(std::is_same())) fspecs.binary32 = true; - fspecs.use_grisu = is_fast_float(); + if (!is_fast_float()) fspecs.fallback = true; int exp = format_float(promote_float(value), precision, fspecs, buffer); fspecs.precision = precision; - Char point = - fspecs.locale ? decimal_point(loc) : static_cast('.'); auto fp = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; - return write_float(out, fp, specs, fspecs, point); + return write_float(out, fp, specs, fspecs, loc); } template ::value)> -auto write(OutputIt out, T value) -> OutputIt { +FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { + if (is_constant_evaluated()) { + return write(out, value, basic_format_specs()); + } + if (const_check(!is_supported_floating_point(value))) return out; using floaty = conditional_t::value, double, T>; @@ -1824,19 +2043,18 @@ auto write(OutputIt out, T value) -> OutputIt { auto bits = bit_cast(value); auto fspecs = float_specs(); - auto sign_bit = bits & (uint(1) << (num_bits() - 1)); - if (sign_bit != 0) { + if (detail::signbit(value)) { fspecs.sign = sign::minus; value = -value; } - static const auto specs = basic_format_specs(); + constexpr auto specs = basic_format_specs(); uint mask = exponent_mask(); if ((bits & mask) == mask) return write_nonfinite(out, std::isinf(value), specs, fspecs); auto dec = dragonbox::to_decimal(static_cast(value)); - return write_float(out, dec, specs, fspecs, static_cast('.')); + return write_float(out, dec, specs, fspecs, {}); } template OutputIt { return base_iterator(out, it); } -// FMT_ENABLE_IF() condition separated to workaround MSVC bug +// FMT_ENABLE_IF() condition separated to workaround an MSVC bug. template < typename Char, typename OutputIt, typename T, bool check = @@ -1907,7 +2125,8 @@ template & specs = {}, locale_ref = {}) -> OutputIt { - return specs.type && specs.type != 's' + return specs.type != presentation_type::none && + specs.type != presentation_type::string ? write(out, value ? 1 : 0, specs, {}) : write_bytes(out, value ? "true" : "false", specs); } @@ -1923,10 +2142,9 @@ template FMT_CONSTEXPR_CHAR_TRAITS auto write(OutputIt out, const Char* value) -> OutputIt { if (!value) { - FMT_THROW(format_error("string pointer is null")); + throw_format_error("string pointer is null"); } else { - auto length = std::char_traits::length(value); - out = write(out, basic_string_view(value, length)); + out = write(out, basic_string_view(value)); } return out; } @@ -1940,18 +2158,28 @@ auto write(OutputIt out, const T* value, return write_ptr(out, to_uintptr(value), &specs); } -template -FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> - typename std::enable_if< - mapped_type_constant>::value == - type::custom_type, - OutputIt>::type { - using context_type = basic_format_context; +// A write overload that handles implicit conversions. +template > +FMT_CONSTEXPR auto write(OutputIt out, const T& value) -> enable_if_t< + std::is_class::value && !is_string::value && + !std::is_same::value && + !std::is_same().map(value))>::value, + OutputIt> { + return write(out, arg_mapper().map(value)); +} + +template > +FMT_CONSTEXPR auto write(OutputIt out, const T& value) + -> enable_if_t::value == type::custom_type, + OutputIt> { using formatter_type = - conditional_t::value, - typename context_type::template formatter_type, + conditional_t::value, + typename Context::template formatter_type, fallback_formatter>; - context_type ctx(out, {}, {}); + auto ctx = Context(out, {}, {}); return formatter_type().format(value, ctx); } @@ -2368,6 +2596,7 @@ FMT_FORMAT_AS(unsigned long, unsigned long long); FMT_FORMAT_AS(Char*, const Char*); FMT_FORMAT_AS(std::basic_string, basic_string_view); FMT_FORMAT_AS(std::nullptr_t, const void*); +FMT_FORMAT_AS(detail::byte, unsigned char); FMT_FORMAT_AS(detail::std_string_view, basic_string_view); template @@ -2493,6 +2722,52 @@ template <> struct formatter { } }; +// group_digits_view is not derived from view because it copies the argument. +template struct group_digits_view { T value; }; + +/** + \rst + Returns a view that formats an integer value using ',' as a locale-independent + thousands separator. + + **Example**:: + + fmt::print("{}", fmt::group_digits(12345)); + // Output: "12,345" + \endrst + */ +template auto group_digits(T value) -> group_digits_view { + return {value}; +} + +template struct formatter> : formatter { + private: + detail::dynamic_format_specs specs_; + + public: + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + using handler_type = detail::dynamic_specs_handler; + detail::specs_checker handler(handler_type(specs_, ctx), + detail::type::int_type); + auto it = parse_format_specs(ctx.begin(), ctx.end(), handler); + detail::check_string_type_spec(specs_.type, ctx.error_handler()); + return it; + } + + template + auto format(group_digits_view t, FormatContext& ctx) + -> decltype(ctx.out()) { + detail::handle_dynamic_spec(specs_.width, + specs_.width_ref, ctx); + detail::handle_dynamic_spec( + specs_.precision, specs_.precision_ref, ctx); + return detail::write_int_localized( + ctx.out(), static_cast>(t.value), 0, specs_, + detail::digit_grouping({"\3", ','})); + } +}; + template struct join_view : detail::view { It begin; @@ -2509,7 +2784,12 @@ using arg_join FMT_DEPRECATED_ALIAS = join_view; template struct formatter, Char> { private: - using value_type = typename std::iterator_traits::value_type; + using value_type = +#ifdef __cpp_lib_ranges + std::iter_value_t; +#else + typename std::iterator_traits::value_type; +#endif using context = buffer_context; using mapper = detail::arg_mapper; @@ -2543,11 +2823,13 @@ struct formatter, Char> { auto it = value.begin; auto out = ctx.out(); if (it != value.end) { - out = value_formatter_.format(map(*it++), ctx); + out = value_formatter_.format(map(*it), ctx); + ++it; while (it != value.end) { out = detail::copy_str(value.sep.begin(), value.sep.end(), out); ctx.advance_to(out); - out = value_formatter_.format(map(*it++), ctx); + out = value_formatter_.format(map(*it), ctx); + ++it; } } return out; @@ -2555,8 +2837,8 @@ struct formatter, Char> { }; /** - Returns an object that formats the iterator range `[begin, end)` with - elements separated by `sep`. + Returns a view that formats the iterator range `[begin, end)` with elements + separated by `sep`. */ template auto join(It begin, Sentinel end, string_view sep) -> join_view { @@ -2565,7 +2847,7 @@ auto join(It begin, Sentinel end, string_view sep) -> join_view { /** \rst - Returns an object that formats `range` with elements separated by `sep`. + Returns a view that formats `range` with elements separated by `sep`. **Example**:: @@ -2604,7 +2886,7 @@ inline auto to_string(const T& value) -> std::string { } template ::value)> -inline auto to_string(T value) -> std::string { +FMT_NODISCARD inline auto to_string(T value) -> std::string { // The buffer should be large enough to store the number including the sign // or "false" for bool. constexpr int max_size = detail::digits10() + 2; @@ -2614,7 +2896,7 @@ inline auto to_string(T value) -> std::string { } template -auto to_string(const basic_memory_buffer& buf) +FMT_NODISCARD auto to_string(const basic_memory_buffer& buf) -> std::basic_string { auto size = buf.size(); detail::assume(size < std::basic_string().max_size()); @@ -2756,17 +3038,9 @@ constexpr auto operator"" _a(const char* s, size_t) -> detail::udl_arg { } # endif -/** - \rst - User-defined literal equivalent of :func:`fmt::format`. - - **Example**:: - - using namespace fmt::literals; - std::string message = "The answer is {}"_format(42); - \endrst - */ -constexpr auto operator"" _format(const char* s, size_t n) +// DEPRECATED! +// User-defined literal equivalent of fmt::format. +FMT_DEPRECATED constexpr auto operator"" _format(const char* s, size_t n) -> detail::udl_formatter { return {{s, n}}; } diff --git a/include/spdlog/fmt/bundled/os.h b/include/spdlog/fmt/bundled/os.h index f6c0f329..b64f8bbf 100644 --- a/include/spdlog/fmt/bundled/os.h +++ b/include/spdlog/fmt/bundled/os.h @@ -21,17 +21,20 @@ #include "format.h" +#ifndef FMT_USE_FCNTL // UWP doesn't provide _pipe. -#if FMT_HAS_INCLUDE("winapifamily.h") -# include -#endif -#if (FMT_HAS_INCLUDE() || defined(__APPLE__) || \ - defined(__linux__)) && \ - (!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) -# include // for O_RDONLY -# define FMT_USE_FCNTL 1 -#else -# define FMT_USE_FCNTL 0 +# if FMT_HAS_INCLUDE("winapifamily.h") +# include +# endif +# if (FMT_HAS_INCLUDE() || defined(__APPLE__) || \ + defined(__linux__)) && \ + (!defined(WINAPI_FAMILY) || \ + (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) +# include // for O_RDONLY +# define FMT_USE_FCNTL 1 +# else +# define FMT_USE_FCNTL 0 +# endif #endif #ifndef FMT_POSIX @@ -390,23 +393,26 @@ struct ostream_params { : ostream_params(params...) { this->buffer_size = bs.value; } + +// Intel has a bug that results in failure to deduce a constructor +// for empty parameter packs. +# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000 + ostream_params(int new_oflag) : oflag(new_oflag) {} + ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {} +# endif }; FMT_END_DETAIL_NAMESPACE -constexpr detail::buffer_size buffer_size; +// Added {} below to work around default constructor error known to +// occur in Xcode versions 7.2.1 and 8.2.1. +constexpr detail::buffer_size buffer_size{}; /** A fast output stream which is not thread-safe. */ class FMT_API ostream final : private detail::buffer { private: file file_; - void flush() { - if (size() == 0) return; - file_.write(data(), size()); - clear(); - } - void grow(size_t) override; ostream(cstring_view path, const detail::ostream_params& params) @@ -426,6 +432,12 @@ class FMT_API ostream final : private detail::buffer { delete[] data(); } + void flush() { + if (size() == 0) return; + file_.write(data(), size()); + clear(); + } + template friend ostream output_file(cstring_view path, T... params); @@ -500,7 +512,7 @@ class locale { // Converts string to floating-point number and advances str past the end // of the parsed input. - double strtod(const char*& str) const { + FMT_DEPRECATED double strtod(const char*& str) const { char* end = nullptr; double result = strtod_l(str, &end, locale_); str = end; diff --git a/include/spdlog/fmt/bundled/ostream.h b/include/spdlog/fmt/bundled/ostream.h index d66248a6..3d716ece 100644 --- a/include/spdlog/fmt/bundled/ostream.h +++ b/include/spdlog/fmt/bundled/ostream.h @@ -14,73 +14,20 @@ FMT_BEGIN_NAMESPACE -template class basic_printf_parse_context; template class basic_printf_context; namespace detail { -template class formatbuf : public std::basic_streambuf { - private: - using int_type = typename std::basic_streambuf::int_type; - using traits_type = typename std::basic_streambuf::traits_type; - - buffer& buffer_; - - public: - formatbuf(buffer& buf) : buffer_(buf) {} - - protected: - // The put-area is actually always empty. This makes the implementation - // simpler and has the advantage that the streambuf and the buffer are always - // in sync and sputc never writes into uninitialized memory. The obvious - // disadvantage is that each call to sputc always results in a (virtual) call - // to overflow. There is no disadvantage here for sputn since this always - // results in a call to xsputn. - - int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { - if (!traits_type::eq_int_type(ch, traits_type::eof())) - buffer_.push_back(static_cast(ch)); - return ch; - } - - std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE { - buffer_.append(s, s + count); - return count; - } -}; - -struct converter { - template ::value)> converter(T); -}; - -template struct test_stream : std::basic_ostream { - private: - void_t<> operator<<(converter); -}; - -// Hide insertion operators for built-in types. -template -void_t<> operator<<(std::basic_ostream&, Char); -template -void_t<> operator<<(std::basic_ostream&, char); -template -void_t<> operator<<(std::basic_ostream&, char); -template -void_t<> operator<<(std::basic_ostream&, signed char); -template -void_t<> operator<<(std::basic_ostream&, unsigned char); - -// Checks if T has a user-defined operator<< (e.g. not a member of -// std::ostream). -template class is_streamable { +// Checks if T has a user-defined operator<<. +template +class is_streamable { private: template - static bool_constant&>() - << std::declval()), - void_t<>>::value> - test(int); + static auto test(int) + -> bool_constant&>() + << std::declval()) != 0>; - template static std::false_type test(...); + template static auto test(...) -> std::false_type; using result = decltype(test(0)); @@ -90,7 +37,21 @@ template class is_streamable { static const bool value = result::value; }; +// Formatting of built-in types and arrays is intentionally disabled because +// it's handled by standard (non-ostream) formatters. +template +struct is_streamable< + T, Char, + enable_if_t< + std::is_arithmetic::value || std::is_array::value || + std::is_pointer::value || std::is_same::value || + std::is_same>::value || + std::is_same>::value || + (std::is_convertible::value && !std::is_enum::value)>> + : std::false_type {}; + // Write the content of buf to os. +// It is a separate function rather than a part of vprint to simplify testing. template void write_buffer(std::basic_ostream& os, buffer& buf) { const Char* buf_data = buf.data(); @@ -108,8 +69,8 @@ void write_buffer(std::basic_ostream& os, buffer& buf) { template void format_value(buffer& buf, const T& value, locale_ref loc = locale_ref()) { - formatbuf format_buf(buf); - std::basic_ostream output(&format_buf); + auto&& format_buf = formatbuf>(buf); + auto&& output = std::basic_ostream(&format_buf); #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) if (loc) output.imbue(loc.get()); #endif @@ -122,29 +83,22 @@ void format_value(buffer& buf, const T& value, template struct fallback_formatter::value>> : private formatter, Char> { - FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) - -> decltype(ctx.begin()) { - return formatter, Char>::parse(ctx); - } - template >::value)> - auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); - } + using formatter, Char>::parse; template auto format(const T& value, basic_format_context& ctx) -> OutputIt { - basic_memory_buffer buffer; + auto buffer = basic_memory_buffer(); format_value(buffer, value, ctx.locale()); - basic_string_view str(buffer.data(), buffer.size()); - return formatter, Char>::format(str, ctx); + return formatter, Char>::format( + {buffer.data(), buffer.size()}, ctx); } + + // DEPRECATED! template auto format(const T& value, basic_printf_context& ctx) -> OutputIt { - basic_memory_buffer buffer; + auto buffer = basic_memory_buffer(); format_value(buffer, value, ctx.locale()); return std::copy(buffer.begin(), buffer.end(), ctx.out()); } @@ -155,7 +109,7 @@ FMT_MODULE_EXPORT template void vprint(std::basic_ostream& os, basic_string_view format_str, basic_format_args>> args) { - basic_memory_buffer buffer; + auto buffer = basic_memory_buffer(); detail::vformat_to(buffer, format_str, args); detail::write_buffer(os, buffer); } diff --git a/include/spdlog/fmt/bundled/printf.h b/include/spdlog/fmt/bundled/printf.h index 3a3cd152..19d550f6 100644 --- a/include/spdlog/fmt/bundled/printf.h +++ b/include/spdlog/fmt/bundled/printf.h @@ -233,7 +233,7 @@ class printf_arg_formatter : public arg_formatter { OutputIt write_null_pointer(bool is_string = false) { auto s = this->specs; - s.type = 0; + s.type = presentation_type::none; return write_bytes(this->out, is_string ? "(null)" : "(nil)", s); } @@ -249,8 +249,10 @@ class printf_arg_formatter : public arg_formatter { // std::is_same instead. if (std::is_same::value) { format_specs fmt_specs = this->specs; - if (fmt_specs.type && fmt_specs.type != 'c') + if (fmt_specs.type != presentation_type::none && + fmt_specs.type != presentation_type::chr) { return (*this)(static_cast(value)); + } fmt_specs.sign = sign::none; fmt_specs.alt = false; fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types. @@ -271,13 +273,13 @@ class printf_arg_formatter : public arg_formatter { /** Formats a null-terminated C string. */ OutputIt operator()(const char* value) { if (value) return base::operator()(value); - return write_null_pointer(this->specs.type != 'p'); + return write_null_pointer(this->specs.type != presentation_type::pointer); } /** Formats a null-terminated wide C string. */ OutputIt operator()(const wchar_t* value) { if (value) return base::operator()(value); - return write_null_pointer(this->specs.type != 'p'); + return write_null_pointer(this->specs.type != presentation_type::pointer); } OutputIt operator()(basic_string_view value) { @@ -490,13 +492,13 @@ void vprintf(buffer& buf, basic_string_view format, // Parse type. if (it == end) FMT_THROW(format_error("invalid format string")); - specs.type = static_cast(*it++); + char type = static_cast(*it++); if (arg.is_integral()) { // Normalize type. - switch (specs.type) { + switch (type) { case 'i': case 'u': - specs.type = 'd'; + type = 'd'; break; case 'c': visit_format_arg( @@ -505,6 +507,9 @@ void vprintf(buffer& buf, basic_string_view format, break; } } + specs.type = parse_presentation_type(type); + if (specs.type == presentation_type::none) + parse_ctx.on_error("invalid type specifier"); start = it; diff --git a/include/spdlog/fmt/bundled/ranges.h b/include/spdlog/fmt/bundled/ranges.h index f0390df2..eb9fb8a9 100644 --- a/include/spdlog/fmt/bundled/ranges.h +++ b/include/spdlog/fmt/bundled/ranges.h @@ -13,37 +13,13 @@ #define FMT_RANGES_H_ #include +#include #include #include "format.h" FMT_BEGIN_NAMESPACE -template struct formatting_range { -#ifdef FMT_DEPRECATED_BRACED_RANGES - Char prefix = '{'; - Char postfix = '}'; -#else - Char prefix = '['; - Char postfix = ']'; -#endif - - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); - } -}; - -template struct formatting_tuple { - Char prefix = '('; - Char postfix = ')'; - - template - FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); - } -}; - namespace detail { template @@ -71,7 +47,7 @@ OutputIterator copy(wchar_t ch, OutputIterator out) { return out; } -/// Return true value if T has std::string interface, like std::string_view. +// Returns true if T has a std::string-like interface, like std::string_view. template class is_std_string_like { template static auto check(U* p) @@ -80,12 +56,40 @@ template class is_std_string_like { public: static FMT_CONSTEXPR_DECL const bool value = - is_string::value || !std::is_void(nullptr))>::value; + is_string::value || + std::is_convertible>::value || + !std::is_void(nullptr))>::value; }; template struct is_std_string_like> : std::true_type {}; +template class is_map { + template static auto check(U*) -> typename U::mapped_type; + template static void check(...); + + public: +#ifdef FMT_FORMAT_MAP_AS_LIST + static FMT_CONSTEXPR_DECL const bool value = false; +#else + static FMT_CONSTEXPR_DECL const bool value = + !std::is_void(nullptr))>::value; +#endif +}; + +template class is_set { + template static auto check(U*) -> typename U::key_type; + template static void check(...); + + public: +#ifdef FMT_FORMAT_SET_AS_LIST + static FMT_CONSTEXPR_DECL const bool value = false; +#else + static FMT_CONSTEXPR_DECL const bool value = + !std::is_void(nullptr))>::value && !is_map::value; +#endif +}; + template struct conditional_helper {}; template struct is_range_ : std::false_type {}; @@ -143,16 +147,16 @@ struct has_mutable_begin_end : std::false_type {}; template struct has_const_begin_end< - T, void_t&>())), - decltype(detail::range_begin( - std::declval&>()))>> + T, + void_t< + decltype(detail::range_begin(std::declval&>())), + decltype(detail::range_end(std::declval&>()))>> : std::true_type {}; template struct has_mutable_begin_end< T, void_t())), - decltype(detail::range_begin(std::declval())), + decltype(detail::range_end(std::declval())), enable_if_t::value>>> : std::true_type {}; @@ -160,34 +164,10 @@ template struct is_range_ : std::integral_constant::value || has_mutable_begin_end::value)> {}; - -template struct range_to_view; -template -struct range_to_view::value>> { - struct view_t { - const T* m_range_ptr; - - auto begin() const FMT_DECLTYPE_RETURN(detail::range_begin(*m_range_ptr)); - auto end() const FMT_DECLTYPE_RETURN(detail::range_end(*m_range_ptr)); - }; - static auto view(const T& range) -> view_t { return {&range}; } -}; - -template -struct range_to_view::value && - has_mutable_begin_end::value>> { - struct view_t { - T m_range_copy; - - auto begin() FMT_DECLTYPE_RETURN(detail::range_begin(m_range_copy)); - auto end() FMT_DECLTYPE_RETURN(detail::range_end(m_range_copy)); - }; - static auto view(const T& range) -> view_t { return {range}; } -}; # undef FMT_DECLTYPE_RETURN #endif -/// tuple_size and tuple_element check. +// tuple_size and tuple_element check. template class is_tuple_like_ { template static auto check(U* p) -> decltype(std::tuple_size::value, int()); @@ -251,16 +231,295 @@ template OutputIt write_delimiter(OutputIt out) { return out; } -template < - typename Char, typename OutputIt, typename Arg, - FMT_ENABLE_IF(is_std_string_like::type>::value)> -OutputIt write_range_entry(OutputIt out, const Arg& v) { +struct singleton { + unsigned char upper; + unsigned char lower_count; +}; + +inline auto is_printable(uint16_t x, const singleton* singletons, + size_t singletons_size, + const unsigned char* singleton_lowers, + const unsigned char* normal, size_t normal_size) + -> bool { + auto upper = x >> 8; + auto lower_start = 0; + for (size_t i = 0; i < singletons_size; ++i) { + auto s = singletons[i]; + auto lower_end = lower_start + s.lower_count; + if (upper < s.upper) break; + if (upper == s.upper) { + for (auto j = lower_start; j < lower_end; ++j) { + if (singleton_lowers[j] == (x & 0xff)) return false; + } + } + lower_start = lower_end; + } + + auto xsigned = static_cast(x); + auto current = true; + for (size_t i = 0; i < normal_size; ++i) { + auto v = static_cast(normal[i]); + auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v; + xsigned -= len; + if (xsigned < 0) break; + current = !current; + } + return current; +} + +// Returns true iff the code point cp is printable. +// This code is generated by support/printable.py. +inline auto is_printable(uint32_t cp) -> bool { + static constexpr singleton singletons0[] = { + {0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8}, + {0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13}, + {0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5}, + {0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22}, + {0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3}, + {0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8}, + {0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9}, + }; + static constexpr unsigned char singletons0_lower[] = { + 0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90, + 0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f, + 0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1, + 0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04, + 0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d, + 0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf, + 0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, + 0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d, + 0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d, + 0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d, + 0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5, + 0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7, + 0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49, + 0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7, + 0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7, + 0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e, + 0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16, + 0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e, + 0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f, + 0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf, + 0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0, + 0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27, + 0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91, + 0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7, + 0xfe, 0xff, + }; + static constexpr singleton singletons1[] = { + {0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2}, + {0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5}, + {0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5}, + {0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2}, + {0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5}, + {0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2}, + {0xfa, 2}, {0xfb, 1}, + }; + static constexpr unsigned char singletons1_lower[] = { + 0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07, + 0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36, + 0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87, + 0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a, + 0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b, + 0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9, + 0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66, + 0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27, + 0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc, + 0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7, + 0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6, + 0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c, + 0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66, + 0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0, + 0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93, + }; + static constexpr unsigned char normal0[] = { + 0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04, + 0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0, + 0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01, + 0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03, + 0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03, + 0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a, + 0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15, + 0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f, + 0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80, + 0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07, + 0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06, + 0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04, + 0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac, + 0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c, + 0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11, + 0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c, + 0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b, + 0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6, + 0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03, + 0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80, + 0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06, + 0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c, + 0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17, + 0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80, + 0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80, + 0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d, + }; + static constexpr unsigned char normal1[] = { + 0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f, + 0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e, + 0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04, + 0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09, + 0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16, + 0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f, + 0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36, + 0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33, + 0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08, + 0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e, + 0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41, + 0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03, + 0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22, + 0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04, + 0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45, + 0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03, + 0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81, + 0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75, + 0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1, + 0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a, + 0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11, + 0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09, + 0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89, + 0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6, + 0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09, + 0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50, + 0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05, + 0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83, + 0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05, + 0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80, + 0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80, + 0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07, + 0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e, + 0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07, + 0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06, + }; + auto lower = static_cast(cp); + if (cp < 0x10000) { + return is_printable(lower, singletons0, + sizeof(singletons0) / sizeof(*singletons0), + singletons0_lower, normal0, sizeof(normal0)); + } + if (cp < 0x20000) { + return is_printable(lower, singletons1, + sizeof(singletons1) / sizeof(*singletons1), + singletons1_lower, normal1, sizeof(normal1)); + } + if (0x2a6de <= cp && cp < 0x2a700) return false; + if (0x2b735 <= cp && cp < 0x2b740) return false; + if (0x2b81e <= cp && cp < 0x2b820) return false; + if (0x2cea2 <= cp && cp < 0x2ceb0) return false; + if (0x2ebe1 <= cp && cp < 0x2f800) return false; + if (0x2fa1e <= cp && cp < 0x30000) return false; + if (0x3134b <= cp && cp < 0xe0100) return false; + if (0xe01f0 <= cp && cp < 0x110000) return false; + return cp < 0x110000; +} + +inline auto needs_escape(uint32_t cp) -> bool { + return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' || + !is_printable(cp); +} + +template struct find_escape_result { + const Char* begin; + const Char* end; + uint32_t cp; +}; + +template +auto find_escape(const Char* begin, const Char* end) + -> find_escape_result { + for (; begin != end; ++begin) { + auto cp = static_cast::type>(*begin); + if (sizeof(Char) == 1 && cp >= 0x80) continue; + if (needs_escape(cp)) return {begin, begin + 1, cp}; + } + return {begin, nullptr, 0}; +} + +inline auto find_escape(const char* begin, const char* end) + -> find_escape_result { + if (!is_utf8()) return find_escape(begin, end); + auto result = find_escape_result{end, nullptr, 0}; + for_each_codepoint(string_view(begin, to_unsigned(end - begin)), + [&](uint32_t cp, string_view sv) { + if (needs_escape(cp)) { + result = {sv.begin(), sv.end(), cp}; + return false; + } + return true; + }); + return result; +} + +template +auto write_range_entry(OutputIt out, basic_string_view str) -> OutputIt { *out++ = '"'; - out = write(out, v); + auto begin = str.begin(), end = str.end(); + do { + auto escape = find_escape(begin, end); + out = copy_str(begin, escape.begin, out); + begin = escape.end; + if (!begin) break; + auto c = static_cast(escape.cp); + switch (escape.cp) { + case '\n': + *out++ = '\\'; + c = 'n'; + break; + case '\r': + *out++ = '\\'; + c = 'r'; + break; + case '\t': + *out++ = '\\'; + c = 't'; + break; + case '"': + FMT_FALLTHROUGH; + case '\\': + *out++ = '\\'; + break; + default: + if (is_utf8()) { + if (escape.cp < 0x100) { + out = format_to(out, "\\x{:02x}", escape.cp); + continue; + } + if (escape.cp < 0x10000) { + out = format_to(out, "\\u{:04x}", escape.cp); + continue; + } + if (escape.cp < 0x110000) { + out = format_to(out, "\\U{:08x}", escape.cp); + continue; + } + } + for (Char escape_char : basic_string_view( + escape.begin, to_unsigned(escape.end - escape.begin))) { + out = format_to( + out, "\\x{:02x}", + static_cast::type>(escape_char)); + } + continue; + } + *out++ = c; + } while (begin != end); *out++ = '"'; return out; } +template >::value)> +inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt { + auto sv = std_string_view(str); + return write_range_entry(out, basic_string_view(sv)); +} + template ::value)> OutputIt write_range_entry(OutputIt out, const Arg v) { @@ -288,43 +547,37 @@ template struct is_tuple_like { template struct formatter::value>> { private: - // C++11 generic lambda for format() + // C++11 generic lambda for format(). template struct format_each { template void operator()(const T& v) { if (i > 0) out = detail::write_delimiter(out); out = detail::write_range_entry(out, v); ++i; } - formatting_tuple& formatting; - size_t& i; - typename std::add_lvalue_reference< - decltype(std::declval().out())>::type out; + int i; + typename FormatContext::iterator& out; }; public: - formatting_tuple formatting; - template FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return formatting.parse(ctx); + return ctx.begin(); } template auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { auto out = ctx.out(); - size_t i = 0; - - detail::copy(formatting.prefix, out); - detail::for_each(values, format_each{formatting, i, out}); - detail::copy(formatting.postfix, out); - - return ctx.out(); + *out++ = '('; + detail::for_each(values, format_each{0, out}); + *out++ = ')'; + return out; } }; template struct is_range { static FMT_CONSTEXPR_DECL const bool value = detail::is_range_::value && !detail::is_std_string_like::value && + !detail::is_map::value && !std::is_convertible>::value && !std::is_constructible, T>::value; }; @@ -334,32 +587,80 @@ struct formatter< T, Char, enable_if_t< fmt::is_range::value -// Workaround a bug in MSVC 2017 and earlier. -#if !FMT_MSC_VER || FMT_MSC_VER >= 1927 - && (has_formatter, format_context>::value || +// Workaround a bug in MSVC 2019 and earlier. +#if !FMT_MSC_VER + && (is_formattable, Char>::value || detail::has_fallback_formatter, Char>::value) #endif >> { - formatting_range formatting; - template FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return formatting.parse(ctx); + return ctx.begin(); } - template - typename FormatContext::iterator format(const T& values, FormatContext& ctx) { - auto out = detail::copy(formatting.prefix, ctx.out()); - size_t i = 0; - auto view = detail::range_to_view::view(values); - auto it = view.begin(); - auto end = view.end(); + template < + typename FormatContext, typename U, + FMT_ENABLE_IF( + std::is_same::value, + const T, T>>::value)> + auto format(U& range, FormatContext& ctx) -> decltype(ctx.out()) { +#ifdef FMT_DEPRECATED_BRACED_RANGES + Char prefix = '{'; + Char postfix = '}'; +#else + Char prefix = detail::is_set::value ? '{' : '['; + Char postfix = detail::is_set::value ? '}' : ']'; +#endif + auto out = ctx.out(); + *out++ = prefix; + int i = 0; + auto it = std::begin(range); + auto end = std::end(range); for (; it != end; ++it) { if (i > 0) out = detail::write_delimiter(out); out = detail::write_range_entry(out, *it); ++i; } - return detail::copy(formatting.postfix, out); + *out++ = postfix; + return out; + } +}; + +template +struct formatter< + T, Char, + enable_if_t< + detail::is_map::value +// Workaround a bug in MSVC 2019 and earlier. +#if !FMT_MSC_VER + && (is_formattable, Char>::value || + detail::has_fallback_formatter, Char>::value) +#endif + >> { + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template < + typename FormatContext, typename U, + FMT_ENABLE_IF( + std::is_same::value, + const T, T>>::value)> + auto format(U& map, FormatContext& ctx) -> decltype(ctx.out()) { + auto out = ctx.out(); + *out++ = '{'; + int i = 0; + for (const auto& item : map) { + if (i > 0) out = detail::write_delimiter(out); + out = detail::write_range_entry(out, item.first); + *out++ = ':'; + *out++ = ' '; + out = detail::write_range_entry(out, item.second); + ++i; + } + *out++ = '}'; + return out; } }; @@ -374,46 +675,70 @@ template struct tuple_join_view : detail::view { template using tuple_arg_join = tuple_join_view; +// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers +// support in tuple_join. It is disabled by default because of issues with +// the dynamic width and precision. +#ifndef FMT_TUPLE_JOIN_SPECIFIERS +# define FMT_TUPLE_JOIN_SPECIFIERS 0 +#endif + template struct formatter, Char> { template FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); + return do_parse(ctx, std::integral_constant()); } template - auto format(const tuple_join_view& value, FormatContext& ctx) -> - typename FormatContext::iterator { - return format(value, ctx, detail::make_index_sequence{}); + auto format(const tuple_join_view& value, + FormatContext& ctx) const -> typename FormatContext::iterator { + return do_format(value, ctx, + std::integral_constant()); } private: - template - auto format(const tuple_join_view& value, FormatContext& ctx, - detail::index_sequence) -> - typename FormatContext::iterator { - using std::get; - return format_args(value, ctx, get(value.tuple)...); + std::tuple::type, Char>...> formatters_; + + template + FMT_CONSTEXPR auto do_parse(ParseContext& ctx, + std::integral_constant) + -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template + FMT_CONSTEXPR auto do_parse(ParseContext& ctx, + std::integral_constant) + -> decltype(ctx.begin()) { + auto end = ctx.begin(); +#if FMT_TUPLE_JOIN_SPECIFIERS + end = std::get(formatters_).parse(ctx); + if (N > 1) { + auto end1 = do_parse(ctx, std::integral_constant()); + if (end != end1) + FMT_THROW(format_error("incompatible format specs for tuple elements")); + } +#endif + return end; } template - auto format_args(const tuple_join_view&, FormatContext& ctx) -> + auto do_format(const tuple_join_view&, FormatContext& ctx, + std::integral_constant) const -> typename FormatContext::iterator { - // NOTE: for compilers that support C++17, this empty function instantiation - // can be replaced with a constexpr branch in the variadic overload. return ctx.out(); } - template - auto format_args(const tuple_join_view& value, FormatContext& ctx, - const Arg& arg, const Args&... args) -> + template + auto do_format(const tuple_join_view& value, FormatContext& ctx, + std::integral_constant) const -> typename FormatContext::iterator { - using base = formatter::type, Char>; - auto out = base().format(arg, ctx); - if (sizeof...(Args) > 0) { + auto out = std::get(formatters_) + .format(std::get(value.tuple), ctx); + if (N > 1) { out = std::copy(value.sep.begin(), value.sep.end(), out); ctx.advance_to(out); - return format_args(value, ctx, args...); + return do_format(value, ctx, std::integral_constant()); } return out; } diff --git a/include/spdlog/fmt/bundled/xchar.h b/include/spdlog/fmt/bundled/xchar.h index a0dd032f..55825077 100644 --- a/include/spdlog/fmt/bundled/xchar.h +++ b/include/spdlog/fmt/bundled/xchar.h @@ -5,8 +5,8 @@ // // For the license information refer to format.h. -#ifndef FMT_WCHAR_H_ -#define FMT_WCHAR_H_ +#ifndef FMT_XCHAR_H_ +#define FMT_XCHAR_H_ #include #include @@ -217,11 +217,11 @@ inline void vprint(wstring_view fmt, wformat_args args) { template void print(std::FILE* f, wformat_string fmt, T&&... args) { - return vprint(f, wstring_view(fmt), make_wformat_args(args...)); + return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...)); } template void print(wformat_string fmt, T&&... args) { - return vprint(wstring_view(fmt), make_wformat_args(args...)); + return vprint(wstring_view(fmt), fmt::make_wformat_args(args...)); } /** @@ -233,4 +233,4 @@ template inline auto to_wstring(const T& value) -> std::wstring { FMT_MODULE_EXPORT_END FMT_END_NAMESPACE -#endif // FMT_WCHAR_H_ +#endif // FMT_XCHAR_H_ diff --git a/src/fmt.cpp b/src/fmt.cpp index 536aa675..647189ba 100644 --- a/src/fmt.cpp +++ b/src/fmt.cpp @@ -12,57 +12,118 @@ FMT_BEGIN_NAMESPACE namespace detail { -template -int format_float(char *buf, std::size_t size, const char *format, int precision, T value) -{ -# ifdef FMT_FUZZ - if (precision > 100000) - throw std::runtime_error("fuzz mode - avoid large allocation inside snprintf"); -# endif - // Suppress the warning about nonliteral format string. - 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); +// DEPRECATED! +template struct basic_data { + FMT_API static constexpr const char digits[100][2] = { + {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, + {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, + {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, + {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, + {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, + {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, + {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, + {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, + {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, + {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, + {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, + {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, + {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, + {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, + {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, + {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, + {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; + FMT_API static constexpr const char hex_digits[] = "0123456789abcdef"; + FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '}; + FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1, + 0}; + FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1, + 0}; + FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', + 0x1000000u | ' '}; +}; + +#ifdef FMT_SHARED +// Required for -flto, -fivisibility=hidden and -shared to work +extern template struct basic_data; +#endif + +#if __cplusplus < 201703L +// DEPRECATED! These are here only for ABI compatiblity. +template constexpr const char basic_data::digits[][2]; +template constexpr const char basic_data::hex_digits[]; +template constexpr const char basic_data::signs[]; +template constexpr const char basic_data::left_padding_shifts[]; +template +constexpr const char basic_data::right_padding_shifts[]; +template constexpr const unsigned basic_data::prefixes[]; +#endif + +template +int format_float(char* buf, std::size_t size, const char* format, int precision, + T value) { +#ifdef FMT_FUZZ + if (precision > 100000) + throw std::runtime_error( + "fuzz mode - avoid large allocation inside snprintf"); +#endif + // Suppress the warning about nonliteral format string. + 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); } -template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(float x) FMT_NOEXCEPT; -template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(double x) FMT_NOEXCEPT; -} // namespace detail +template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(float x) + FMT_NOEXCEPT; +template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(double x) + FMT_NOEXCEPT; +} // namespace detail // Workaround a bug in MSVC2013 that prevents instantiation of format_float. -int (*instantiate_format_float)(double, int, detail::float_specs, detail::buffer &) = detail::format_float; +int (*instantiate_format_float)(double, int, detail::float_specs, + detail::buffer&) = detail::format_float; -# ifndef FMT_STATIC_THOUSANDS_SEPARATOR -template FMT_API detail::locale_ref::locale_ref(const std::locale &loc); +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +template FMT_API detail::locale_ref::locale_ref(const std::locale& loc); template FMT_API std::locale detail::locale_ref::get() const; -# endif +#endif // Explicit instantiations for char. -template FMT_API auto detail::thousands_sep_impl(locale_ref) -> thousands_sep_result; +template FMT_API auto detail::thousands_sep_impl(locale_ref) + -> thousands_sep_result; template FMT_API char detail::decimal_point_impl(locale_ref); -template FMT_API void detail::buffer::append(const char *, const char *); +template FMT_API void detail::buffer::append(const char*, const char*); // DEPRECATED! // There is no correspondent extern template in format.h because of // incompatibility between clang and gcc (#2377). template FMT_API void detail::vformat_to( - detail::buffer &, string_view, basic_format_args, detail::locale_ref); - -template FMT_API int detail::snprintf_float(double, int, detail::float_specs, detail::buffer &); -template FMT_API int detail::snprintf_float(long double, int, detail::float_specs, detail::buffer &); -template FMT_API int detail::format_float(double, int, detail::float_specs, detail::buffer &); -template FMT_API int detail::format_float(long double, int, detail::float_specs, detail::buffer &); + detail::buffer&, string_view, + basic_format_args, detail::locale_ref); + +template FMT_API int detail::snprintf_float(double, int, detail::float_specs, + detail::buffer&); +template FMT_API int detail::snprintf_float(long double, int, + detail::float_specs, + detail::buffer&); +template FMT_API int detail::format_float(double, int, detail::float_specs, + detail::buffer&); +template FMT_API int detail::format_float(long double, int, detail::float_specs, + detail::buffer&); // Explicit instantiations for wchar_t. -template FMT_API auto detail::thousands_sep_impl(locale_ref) -> thousands_sep_result; +template FMT_API auto detail::thousands_sep_impl(locale_ref) + -> thousands_sep_result; template FMT_API wchar_t detail::decimal_point_impl(locale_ref); -template FMT_API void detail::buffer::append(const wchar_t *, const wchar_t *); +template FMT_API void detail::buffer::append(const wchar_t*, + const wchar_t*); template struct detail::basic_data; FMT_END_NAMESPACE + #endif // !SPDLOG_FMT_EXTERNAL From c432fdd987b727f6a427b5609fc9714ecf9c8533 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 12 Feb 2022 13:20:15 +0200 Subject: [PATCH 113/121] Bump fmt to version 8.1.1 and run clang-format --- example/example.cpp | 13 +- include/spdlog/async.h | 6 +- include/spdlog/common.h | 2 +- include/spdlog/details/file_helper-inl.h | 2 +- include/spdlog/details/thread_pool-inl.h | 6 +- include/spdlog/fmt/bin_to_hex.h | 9 +- include/spdlog/pattern_formatter.h | 4 +- include/spdlog/sinks/rotating_file_sink-inl.h | 4 +- src/fmt.cpp | 141 ++++++++---------- tests/test_file_helper.cpp | 17 +-- tests/test_macros.cpp | 3 +- 11 files changed, 96 insertions(+), 111 deletions(-) diff --git a/example/example.cpp b/example/example.cpp index c1251b1b..f8ad03c1 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -199,8 +199,8 @@ void vector_example() spdlog::info("Vector example: {}", vec); } -#else -void vector_example() {} +#else +void vector_example() {} #endif // ! DSPDLOG_USE_STD_FORMAT @@ -258,7 +258,8 @@ void multi_sink_example() struct my_type { int i = 0; - explicit my_type(int i): i(i){}; + explicit my_type(int i) + : i(i){}; }; namespace fmt_lib = spdlog::fmt_lib; @@ -266,7 +267,7 @@ template<> struct fmt_lib::formatter : fmt_lib::formatter { auto format(my_type my, format_context &ctx) -> decltype(ctx.out()) - { + { return fmt_lib::format_to(ctx.out(), "[my_type i={}]", my.i); } }; @@ -359,9 +360,9 @@ void replace_default_logger_example() auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true); spdlog::set_default_logger(new_logger); - spdlog::set_level(spdlog::level::info); + spdlog::set_level(spdlog::level::info); spdlog::debug("This message should not be displayed!"); - spdlog::set_level(spdlog::level::trace); + spdlog::set_level(spdlog::level::trace); spdlog::debug("This message should be displayed.."); spdlog::set_default_logger(old_logger); diff --git a/include/spdlog/async.h b/include/spdlog/async.h index 281af697..d6e21349 100644 --- a/include/spdlog/async.h +++ b/include/spdlog/async.h @@ -73,7 +73,8 @@ inline std::shared_ptr create_async_nb(std::string logger_name, } // set global thread pool. -inline void init_thread_pool(size_t q_size, size_t thread_count, std::function on_thread_start, std::function on_thread_stop) +inline void init_thread_pool( + size_t q_size, size_t thread_count, std::function on_thread_start, std::function on_thread_stop) { auto tp = std::make_shared(q_size, thread_count, on_thread_start, on_thread_stop); details::registry::instance().set_tp(std::move(tp)); @@ -86,7 +87,8 @@ inline void init_thread_pool(size_t q_size, size_t thread_count, std::function using enable_if_t = typename std::enable_if::type; template -std::unique_ptr make_unique(Args &&...args) +std::unique_ptr make_unique(Args &&... args) { static_assert(!std::is_array::value, "arrays not supported"); return std::unique_ptr(new T(std::forward(args)...)); diff --git a/include/spdlog/details/file_helper-inl.h b/include/spdlog/details/file_helper-inl.h index fa082253..d4528711 100644 --- a/include/spdlog/details/file_helper-inl.h +++ b/include/spdlog/details/file_helper-inl.h @@ -84,7 +84,7 @@ SPDLOG_INLINE void file_helper::reopen(bool truncate) SPDLOG_INLINE void file_helper::flush() { - if(std::fflush(fd_) != 0) + if (std::fflush(fd_) != 0) { throw_spdlog_ex("Failed flush to file " + os::filename_to_str(filename_), errno); } diff --git a/include/spdlog/details/thread_pool-inl.h b/include/spdlog/details/thread_pool-inl.h index c99af52d..e4b3e1c4 100644 --- a/include/spdlog/details/thread_pool-inl.h +++ b/include/spdlog/details/thread_pool-inl.h @@ -13,7 +13,8 @@ namespace spdlog { namespace details { -SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start, std::function on_thread_stop) +SPDLOG_INLINE thread_pool::thread_pool( + size_t q_max_items, size_t threads_n, std::function on_thread_start, std::function on_thread_stop) : q_(q_max_items) { if (threads_n == 0 || threads_n > 1000) @@ -36,7 +37,8 @@ SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std {} SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n) - : thread_pool(q_max_items, threads_n, [] {}, [] {}) + : thread_pool( + q_max_items, threads_n, [] {}, [] {}) {} // message all threads to terminate gracefully join them diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h index 76dcb836..93e5c28b 100644 --- a/include/spdlog/fmt/bin_to_hex.h +++ b/include/spdlog/fmt/bin_to_hex.h @@ -9,11 +9,11 @@ #include #if defined(__has_include) && __has_include() -#include +# include #endif #if __cpp_lib_span >= 202002L -#include +# include #endif // @@ -78,8 +78,9 @@ inline details::dump_info to_hex(const Conta #if __cpp_lib_span >= 202002L -template -inline details::dump_info::iterator> to_hex(const std::span &container, size_t size_per_line = 32) +template +inline details::dump_info::iterator> to_hex( + const std::span &container, size_t size_per_line = 32) { using Container = std::span; static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1"); diff --git a/include/spdlog/pattern_formatter.h b/include/spdlog/pattern_formatter.h index df046f67..8bcf8e2a 100644 --- a/include/spdlog/pattern_formatter.h +++ b/include/spdlog/pattern_formatter.h @@ -68,7 +68,7 @@ class SPDLOG_API custom_flag_formatter : public details::flag_formatter public: virtual std::unique_ptr clone() const = 0; - void set_padding_info(const details::padding_info& padding) + void set_padding_info(const details::padding_info &padding) { flag_formatter::padinfo_ = padding; } @@ -80,7 +80,7 @@ public: using custom_flags = std::unordered_map>; explicit pattern_formatter(std::string pattern, pattern_time_type time_type = pattern_time_type::local, - std::string eol = spdlog::details::os::default_eol, custom_flags custom_user_flags = custom_flags()); + std::string eol = spdlog::details::os::default_eol, custom_flags custom_user_flags = custom_flags()); // use default pattern is not given explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol); diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h index 050a3e6c..4cb5f9fa 100644 --- a/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -31,7 +31,7 @@ SPDLOG_INLINE rotating_file_sink::rotating_file_sink( , max_files_(max_files) , file_helper_{event_handlers} { - if(max_size == 0) + if (max_size == 0) { throw_spdlog_ex("rotating sink constructor: max_size arg cannot be zero"); } @@ -79,7 +79,7 @@ SPDLOG_INLINE void rotating_file_sink::sink_it_(const details::log_msg &m if (new_size > max_size_) { file_helper_.flush(); - if(file_helper_.size() > 0) + if (file_helper_.size() > 0) { rotate_(); new_size = formatted.size(); diff --git a/src/fmt.cpp b/src/fmt.cpp index 647189ba..8caa18d9 100644 --- a/src/fmt.cpp +++ b/src/fmt.cpp @@ -13,117 +13,98 @@ FMT_BEGIN_NAMESPACE namespace detail { // DEPRECATED! -template struct basic_data { - FMT_API static constexpr const char digits[100][2] = { - {'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, - {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, - {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'}, - {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, - {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'}, - {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, - {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'}, - {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, - {'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, - {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, - {'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, - {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, - {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, - {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, - {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, - {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'}, - {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; - FMT_API static constexpr const char hex_digits[] = "0123456789abcdef"; - FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '}; - FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1, - 0}; - FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1, - 0}; - FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', - 0x1000000u | ' '}; +template +struct basic_data +{ + FMT_API static constexpr const char digits[100][2] = {{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'}, + {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, + {'1', '7'}, {'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, + {'2', '8'}, {'2', '9'}, {'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'}, {'3', '6'}, {'3', '7'}, {'3', '8'}, + {'3', '9'}, {'4', '0'}, {'4', '1'}, {'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'}, {'4', '8'}, {'4', '9'}, + {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'}, {'6', '0'}, + {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'}, {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'}, + {'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'}, {'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, + {'8', '3'}, {'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'}, {'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, + {'9', '4'}, {'9', '5'}, {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}}; + FMT_API static constexpr const char hex_digits[] = "0123456789abcdef"; + FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '}; + FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1, 0}; + FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1, 0}; + FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', 0x1000000u | ' '}; }; -#ifdef FMT_SHARED +# ifdef FMT_SHARED // Required for -flto, -fivisibility=hidden and -shared to work extern template struct basic_data; -#endif +# endif -#if __cplusplus < 201703L +# if __cplusplus < 201703L // DEPRECATED! These are here only for ABI compatiblity. -template constexpr const char basic_data::digits[][2]; -template constexpr const char basic_data::hex_digits[]; -template constexpr const char basic_data::signs[]; -template constexpr const char basic_data::left_padding_shifts[]; -template +template +constexpr const char basic_data::digits[][2]; +template +constexpr const char basic_data::hex_digits[]; +template +constexpr const char basic_data::signs[]; +template +constexpr const char basic_data::left_padding_shifts[]; +template constexpr const char basic_data::right_padding_shifts[]; -template constexpr const unsigned basic_data::prefixes[]; -#endif - -template -int format_float(char* buf, std::size_t size, const char* format, int precision, - T value) { -#ifdef FMT_FUZZ - if (precision > 100000) - throw std::runtime_error( - "fuzz mode - avoid large allocation inside snprintf"); -#endif - // Suppress the warning about nonliteral format string. - 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); +template +constexpr const unsigned basic_data::prefixes[]; +# endif + +template +int format_float(char *buf, std::size_t size, const char *format, int precision, T value) +{ +# ifdef FMT_FUZZ + if (precision > 100000) + throw std::runtime_error("fuzz mode - avoid large allocation inside snprintf"); +# endif + // Suppress the warning about nonliteral format string. + 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); } -template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(float x) - FMT_NOEXCEPT; -template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(double x) - FMT_NOEXCEPT; -} // namespace detail +template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(float x) FMT_NOEXCEPT; +template FMT_API dragonbox::decimal_fp dragonbox::to_decimal(double x) FMT_NOEXCEPT; +} // namespace detail // Workaround a bug in MSVC2013 that prevents instantiation of format_float. -int (*instantiate_format_float)(double, int, detail::float_specs, - detail::buffer&) = detail::format_float; +int (*instantiate_format_float)(double, int, detail::float_specs, detail::buffer &) = detail::format_float; -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR -template FMT_API detail::locale_ref::locale_ref(const std::locale& loc); +# ifndef FMT_STATIC_THOUSANDS_SEPARATOR +template FMT_API detail::locale_ref::locale_ref(const std::locale &loc); template FMT_API std::locale detail::locale_ref::get() const; -#endif +# endif // Explicit instantiations for char. -template FMT_API auto detail::thousands_sep_impl(locale_ref) - -> thousands_sep_result; +template FMT_API auto detail::thousands_sep_impl(locale_ref) -> thousands_sep_result; template FMT_API char detail::decimal_point_impl(locale_ref); -template FMT_API void detail::buffer::append(const char*, const char*); +template FMT_API void detail::buffer::append(const char *, const char *); // DEPRECATED! // There is no correspondent extern template in format.h because of // incompatibility between clang and gcc (#2377). template FMT_API void detail::vformat_to( - detail::buffer&, string_view, - basic_format_args, detail::locale_ref); - -template FMT_API int detail::snprintf_float(double, int, detail::float_specs, - detail::buffer&); -template FMT_API int detail::snprintf_float(long double, int, - detail::float_specs, - detail::buffer&); -template FMT_API int detail::format_float(double, int, detail::float_specs, - detail::buffer&); -template FMT_API int detail::format_float(long double, int, detail::float_specs, - detail::buffer&); + detail::buffer &, string_view, basic_format_args, detail::locale_ref); + +template FMT_API int detail::snprintf_float(double, int, detail::float_specs, detail::buffer &); +template FMT_API int detail::snprintf_float(long double, int, detail::float_specs, detail::buffer &); +template FMT_API int detail::format_float(double, int, detail::float_specs, detail::buffer &); +template FMT_API int detail::format_float(long double, int, detail::float_specs, detail::buffer &); // Explicit instantiations for wchar_t. -template FMT_API auto detail::thousands_sep_impl(locale_ref) - -> thousands_sep_result; +template FMT_API auto detail::thousands_sep_impl(locale_ref) -> thousands_sep_result; template FMT_API wchar_t detail::decimal_point_impl(locale_ref); -template FMT_API void detail::buffer::append(const wchar_t*, - const wchar_t*); +template FMT_API void detail::buffer::append(const wchar_t *, const wchar_t *); template struct detail::basic_data; FMT_END_NAMESPACE - #endif // !SPDLOG_FMT_EXTERNAL diff --git a/tests/test_file_helper.cpp b/tests/test_file_helper.cpp index 01fa48ef..9d459277 100644 --- a/tests/test_file_helper.cpp +++ b/tests/test_file_helper.cpp @@ -110,7 +110,7 @@ TEST_CASE("file_event_handlers", "[file_helper]") after_close }; prepare_logdir(); - + spdlog::filename_t test_filename = SPDLOG_FILENAME_T(TEST_FILENAME); // define event handles that update vector of flags when called std::vector events; @@ -119,13 +119,13 @@ TEST_CASE("file_event_handlers", "[file_helper]") REQUIRE(filename == test_filename); events.push_back(flags::before_open); }; - handlers.after_open = [&](spdlog::filename_t filename, std::FILE* fstream) { + handlers.after_open = [&](spdlog::filename_t filename, std::FILE *fstream) { REQUIRE(filename == test_filename); REQUIRE(fstream); fputs("after_open\n", fstream); events.push_back(flags::after_open); }; - handlers.before_close = [&](spdlog::filename_t filename, std::FILE* fstream) { + handlers.before_close = [&](spdlog::filename_t filename, std::FILE *fstream) { REQUIRE(filename == test_filename); REQUIRE(fstream); fputs("before_close\n", fstream); @@ -141,17 +141,16 @@ TEST_CASE("file_event_handlers", "[file_helper]") helper.open(test_filename); REQUIRE(events == std::vector{flags::before_open, flags::after_open}); - - events.clear(); - helper.close(); + + events.clear(); + helper.close(); REQUIRE(events == std::vector{flags::before_close, flags::after_close}); REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n"); helper.reopen(true); - events.clear(); + events.clear(); } - // make sure that the file_helper destrcutor calls the close callbacks if needed + // make sure that the file_helper destrcutor calls the close callbacks if needed REQUIRE(events == std::vector{flags::before_close, flags::after_close}); REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n"); - } diff --git a/tests/test_macros.cpp b/tests/test_macros.cpp index c9af60f3..2aeeecff 100644 --- a/tests/test_macros.cpp +++ b/tests/test_macros.cpp @@ -27,7 +27,7 @@ TEST_CASE("debug and trace w/o format string", "[macros]]") using spdlog::details::os::default_eol; REQUIRE(ends_with(file_contents(TEST_FILENAME), spdlog::fmt_lib::format("Test message 2{}", default_eol))); REQUIRE(count_lines(TEST_FILENAME) == 1); - + auto orig_default_logger = spdlog::default_logger(); spdlog::set_default_logger(logger); @@ -52,4 +52,3 @@ TEST_CASE("pass logger pointer", "[macros]") SPDLOG_LOGGER_TRACE(&ref, "Test message 1"); SPDLOG_LOGGER_DEBUG(&ref, "Test message 2"); } - From 71105e0b0741416d86d48973c16a6002a4ce7449 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 12 Feb 2022 13:59:12 +0200 Subject: [PATCH 114/121] Fixed #2227 --- example/example.cpp | 2 +- include/spdlog/fmt/ranges.h | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 include/spdlog/fmt/ranges.h diff --git a/example/example.cpp b/example/example.cpp index f8ad03c1..c379c746 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -192,7 +192,7 @@ void binary_example() // Log a vector of numbers #ifndef SPDLOG_USE_STD_FORMAT -# include "spdlog/fmt/bundled/ranges.h" +# include "spdlog/fmt/ranges.h" void vector_example() { std::vector vec = {1, 2, 3}; diff --git a/include/spdlog/fmt/ranges.h b/include/spdlog/fmt/ranges.h new file mode 100644 index 00000000..9103a5f6 --- /dev/null +++ b/include/spdlog/fmt/ranges.h @@ -0,0 +1,22 @@ +// +// Copyright(c) 2016 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once +// +// include bundled or external copy of fmtlib's ranges support +// + +#if !defined(SPDLOG_USE_STD_FORMAT) +# if !defined(SPDLOG_FMT_EXTERNAL) +# ifdef SPDLOG_HEADER_ONLY +# ifndef FMT_HEADER_ONLY +# define FMT_HEADER_ONLY +# endif +# endif +# include +# else +# include +# endif +#endif From 53c9b70ea3e879cefccdab1f1c18157f765d5182 Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 12 Feb 2022 14:06:11 +0200 Subject: [PATCH 115/121] Fix #2211 --- include/spdlog/tweakme.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/spdlog/tweakme.h b/include/spdlog/tweakme.h index ab557051..8fa60e4c 100644 --- a/include/spdlog/tweakme.h +++ b/include/spdlog/tweakme.h @@ -96,8 +96,7 @@ /////////////////////////////////////////////////////////////////////////////// // Uncomment to customize level names (e.g. "MY TRACE") // -// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", -// "MY ERROR", "MY CRITICAL", "OFF" } +// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", "MY ERROR", "MY CRITICAL", "OFF" } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// From 4cea9b87299771c1ad6a65a1291708164a5e900c Mon Sep 17 00:00:00 2001 From: gabime Date: Sat, 12 Feb 2022 14:10:43 +0200 Subject: [PATCH 116/121] Limit max number of rotating files to 200000. Fix #1905 --- include/spdlog/sinks/rotating_file_sink-inl.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h index 4cb5f9fa..808a17f4 100644 --- a/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -35,6 +35,11 @@ SPDLOG_INLINE rotating_file_sink::rotating_file_sink( { throw_spdlog_ex("rotating sink constructor: max_size arg cannot be zero"); } + + if (max_size > 200000) + { + throw_spdlog_ex("rotating sink constructor: max_size arg cannot exceed 2000000"); + } file_helper_.open(calc_filename(base_filename_, 0)); current_size_ = file_helper_.size(); // expensive. called only once if (rotate_on_open && current_size_ > 0) From 4c2ce2c82cad779513cb524c09bf0bbbfc195d88 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sun, 13 Feb 2022 09:41:15 +0200 Subject: [PATCH 117/121] Update rotating_file_sink-inl.h --- include/spdlog/sinks/rotating_file_sink-inl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h index 808a17f4..8ac8cb14 100644 --- a/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -38,7 +38,7 @@ SPDLOG_INLINE rotating_file_sink::rotating_file_sink( if (max_size > 200000) { - throw_spdlog_ex("rotating sink constructor: max_size arg cannot exceed 2000000"); + throw_spdlog_ex("rotating sink constructor: max_size arg cannot exceed 200000"); } file_helper_.open(calc_filename(base_filename_, 0)); current_size_ = file_helper_.size(); // expensive. called only once From a732a0dc852eb054060512fe49d85a30d31813b0 Mon Sep 17 00:00:00 2001 From: Surfy Cui Date: Mon, 14 Feb 2022 15:25:55 +0800 Subject: [PATCH 118/121] Limit max number of rotating files to 200000, not max size --- include/spdlog/sinks/rotating_file_sink-inl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h index 8ac8cb14..350c2127 100644 --- a/include/spdlog/sinks/rotating_file_sink-inl.h +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -36,9 +36,9 @@ SPDLOG_INLINE rotating_file_sink::rotating_file_sink( throw_spdlog_ex("rotating sink constructor: max_size arg cannot be zero"); } - if (max_size > 200000) + if (max_files > 200000) { - throw_spdlog_ex("rotating sink constructor: max_size arg cannot exceed 200000"); + throw_spdlog_ex("rotating sink constructor: max_files arg cannot exceed 200000"); } file_helper_.open(calc_filename(base_filename_, 0)); current_size_ = file_helper_.size(); // expensive. called only once From 9cd9c98f59d628b914bc8f804bddfa482fade144 Mon Sep 17 00:00:00 2001 From: Adrien Bertrand Date: Tue, 15 Feb 2022 11:26:25 -0500 Subject: [PATCH 119/121] pattern_formatter-inl: fix reorder-ctor warning Fix `Wreorder-ctor` warning ``` spdlog/pattern_formatter-inl.h:1028:7: error: field 'custom_handlers_' will be initialized after field 'need_localtime_' [-Werror,-Wreorder-ctor] , custom_handlers_(std::move(custom_user_flags)) ^ ``` Move the initialization of `need_localtime_(true)` right after `pattern_time_type_` as expected. --- include/spdlog/pattern_formatter-inl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/spdlog/pattern_formatter-inl.h b/include/spdlog/pattern_formatter-inl.h index 4d312081..bf9da079 100644 --- a/include/spdlog/pattern_formatter-inl.h +++ b/include/spdlog/pattern_formatter-inl.h @@ -1024,9 +1024,9 @@ SPDLOG_INLINE pattern_formatter::pattern_formatter( : pattern_(std::move(pattern)) , eol_(std::move(eol)) , pattern_time_type_(time_type) + , need_localtime_(false) , last_log_secs_(0) , custom_handlers_(std::move(custom_user_flags)) - , need_localtime_(false) { std::memset(&cached_tm_, 0, sizeof(cached_tm_)); compile_pattern_(pattern_); @@ -1037,8 +1037,8 @@ SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type, : pattern_("%+") , eol_(std::move(eol)) , pattern_time_type_(time_type) - , last_log_secs_(0) , need_localtime_(true) + , last_log_secs_(0) { std::memset(&cached_tm_, 0, sizeof(cached_tm_)); formatters_.push_back(details::make_unique(details::padding_info{})); From 69cac816aa84ce6d208d2f778d067fb41759e51c Mon Sep 17 00:00:00 2001 From: Adam Calhoon Date: Sun, 6 Mar 2022 10:52:09 -0500 Subject: [PATCH 120/121] When built with SPDLOG_FMT_EXTERNAL_HO consumers of the spdlog targets depend on fmt The cmake/spdlogConfig.cmake.in file properly takes into account the fmt package dependency when building with SPDLOG_FMT_EXTERNAL:BOOL=ON but not when built with SPDLOG_FMT_EXTERNAL_HO:BOOL=ON. Prior to these changes SPDLOG_FMT_EXTERNAL_HO:BOOL=ON results in exported targets with INTERFACE_LINK_LIBRARIES that contain fmt::fmt-header-only. As such, the installed spdlogConfig.cmake file should attempt to find that dependency for the consumer. --- cmake/spdlogConfig.cmake.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmake/spdlogConfig.cmake.in b/cmake/spdlogConfig.cmake.in index 89aaab5a..1b85b9e0 100644 --- a/cmake/spdlogConfig.cmake.in +++ b/cmake/spdlogConfig.cmake.in @@ -6,9 +6,10 @@ find_package(Threads REQUIRED) set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@) +set(SPDLOG_FMT_EXTERNAL_HO @SPDLOG_FMT_EXTERNAL_HO@) set(config_targets_file @config_targets_file@) -if(SPDLOG_FMT_EXTERNAL) +if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO) include(CMakeFindDependencyMacro) find_dependency(fmt CONFIG) endif() @@ -16,4 +17,4 @@ endif() include("${CMAKE_CURRENT_LIST_DIR}/${config_targets_file}") -check_required_components(spdlog) \ No newline at end of file +check_required_components(spdlog) From 5ee969e4f6b9bf5bbb9de6fe418095f4d25d73a7 Mon Sep 17 00:00:00 2001 From: Andrey Bugaevskiy Date: Fri, 11 Mar 2022 19:22:45 +0000 Subject: [PATCH 121/121] Fix fopen_s error reporting with PREVENT_CHILD_FD --- include/spdlog/details/os-inl.h | 2 +- tests/test_file_helper.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h index e094e0f7..c3bf691c 100644 --- a/include/spdlog/details/os-inl.h +++ b/include/spdlog/details/os-inl.h @@ -145,7 +145,7 @@ SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename const int fd = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644)); if (fd == -1) { - return false; + return true; } *fp = ::fdopen(fd, mode.c_str()); if (*fp == nullptr) diff --git a/tests/test_file_helper.cpp b/tests/test_file_helper.cpp index 9d459277..1d947078 100644 --- a/tests/test_file_helper.cpp +++ b/tests/test_file_helper.cpp @@ -154,3 +154,15 @@ TEST_CASE("file_event_handlers", "[file_helper]") REQUIRE(events == std::vector{flags::before_close, flags::after_close}); REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n"); } + +TEST_CASE("file_helper_open", "[file_helper]") +{ + prepare_logdir(); + spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME); + file_helper helper; + helper.open(target_filename); + helper.close(); + + target_filename += SPDLOG_FILENAME_T("/invalid"); + REQUIRE_THROWS_AS(helper.open(target_filename), spdlog::spdlog_ex); +}