From 44a4517e2b1189bde950b86cbf6c2c5943a5a55a Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Sat, 13 Nov 2021 11:29:05 -0500 Subject: [PATCH 01/21] 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 | 103 ++++++++++++------ 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, 445 insertions(+), 143 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) - { - log(source_loc{}, lvl, fmt, std::forward(args)...); - } - - template - void log(source_loc loc, 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_(loc, lvl, fmt, std::forward(args)...); } template - void trace(fmt::wformat_string fmt, Args &&...args) + void log(level::level_enum lvl, wformat_string_t fmt, Args &&...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(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); + error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&format_message_result.hlocal_, 0, nullptr); - if (format_message_succeeded && format_message_result) + if (format_message_succeeded && format_message_result.hlocal_) { - system_message = fmt::format(" ({})", format_message_result); + system_message = fmt_lib::format(" ({})", (LPSTR)format_message_result.hlocal_); } - if (format_message_result) - { - LocalFree((HLOCAL)format_message_result); - } - - 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 02/21] 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 03/21] 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 04/21] 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 05/21] 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 06/21] 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 07/21] 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 d75de3d3b25ad53b44002fa7760782e3025513a7 Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Sun, 14 Nov 2021 02:33:15 -0500 Subject: [PATCH 08/21] 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 849e90bd017e4b29d2dc691f6a0f33864cabb628 Mon Sep 17 00:00:00 2001 From: Charless Milette Date: Mon, 15 Nov 2021 13:36:29 -0500 Subject: [PATCH 09/21] 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 10/21] 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 11/21] 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 12/21] 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 13/21] 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 14/21] 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 15/21] 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 16/21] 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 17/21] 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 5d6af189f13678ae1becad9b1438455a25256102 Mon Sep 17 00:00:00 2001 From: Charles Milette Date: Tue, 16 Nov 2021 09:59:48 -0500 Subject: [PATCH 18/21] 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 19/21] 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 20/21] 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 21/21] 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 }