Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/gabime/spdlog.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharless Milette <charles.milette@gmail.com>2021-11-15 22:57:13 +0300
committerCharless Milette <charles.milette@gmail.com>2021-11-15 22:57:13 +0300
commitf6901606f5a36047036072d0c6c58f0c9d90bb9f (patch)
tree1a5b137d8bfc637185406b362df205660cd6eb44
parent849e90bd017e4b29d2dc691f6a0f33864cabb628 (diff)
Add std::tm formatter, fix spdlog::stopwatch formatter, conditionally use fmt::runtime in test_errors
-rw-r--r--include/spdlog/fmt/chrono.h86
-rw-r--r--include/spdlog/stopwatch.h11
-rw-r--r--tests/test_errors.cpp12
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 <cassert>
+#include <ctime>
+#include <format>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <spdlog/common.h>
+
+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 <typename Int>
+ SPDLOG_CONSTEXPR auto to_unsigned(Int value) -> typename std::make_unsigned<Int>::type
+ {
+ assert(value >= 0, "negative value");
+ return static_cast<typename std::make_unsigned<Int>::type>(value);
+ }
+}
+
+template <typename Char>
+struct std::formatter<std::tm, Char>
+{
+ template <typename ParseContext>
+ 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 <typename FormatContext>
+ auto format(const std::tm &tm, FormatContext &ctx) const -> decltype(ctx.out()) {
+ basic_string<Char> 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<Char> 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<Char> 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<spdlog::stopwatch> : formatter<double>
{
@@ -58,4 +65,4 @@ struct formatter<spdlog::stopwatch> : formatter<double>
return formatter<double>::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<spdlog::sinks::basic_file_sink_mt>("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
}