From ebf276ad80b18c6d27f40f435b27f4a00d7cde88 Mon Sep 17 00:00:00 2001 From: ZHUO Qiang Date: Mon, 18 Sep 2023 19:36:04 +0800 Subject: [PATCH] better char array support --- examples/example_trivial.cpp | 13 ++++ quill/include/quill/detail/Serialize.h | 48 ++++++++++++++- .../quill/detail/misc/TypeTraitsCopyable.h | 3 + quill/test/LogTest.cpp | 60 ++++++++++++++++++- quill/test/TypeTraitsCopyableTest.cpp | 11 +++- 5 files changed, 131 insertions(+), 4 deletions(-) diff --git a/examples/example_trivial.cpp b/examples/example_trivial.cpp index 3f5dec4a..6d055472 100644 --- a/examples/example_trivial.cpp +++ b/examples/example_trivial.cpp @@ -42,6 +42,19 @@ int main() std::array arr = {1, 2, 3, 4}; LOG_INFO(logger, "This is a log info example {}", arr); + union + { + char no_0[2]; + char mid_0[6]{'1', '2', '3', '4', '\0', 6}; + } char_arrays; + + // only output "12" even if there's no '\0' at the end + LOG_INFO(logger, R"(This is a log info example for char array without '\0': {})", char_arrays.no_0); + + // output "1234" until the '\0' + LOG_INFO(logger, R"(This is a log info example for char array with '\0' in middle: {})", + char_arrays.mid_0); + // Using a dynamic runtime log level std::array const runtime_log_levels = { quill::LogLevel::Debug, quill::LogLevel::Info, quill::LogLevel::Warning, quill::LogLevel::Error}; diff --git a/quill/include/quill/detail/Serialize.h b/quill/include/quill/detail/Serialize.h index 27a26d03..97b6ddbd 100644 --- a/quill/include/quill/detail/Serialize.h +++ b/quill/include/quill/detail/Serialize.h @@ -5,6 +5,11 @@ #pragma once +#ifndef __STDC_WANT_LIB_EXT1__ + #define __STDC_WANT_LIB_EXT1__ 1 + #include +#endif + #include "misc/Utilities.h" #include "quill/LogLevel.h" #include "quill/MacroMetadata.h" @@ -23,9 +28,25 @@ namespace quill namespace detail { +constexpr auto strnlen = +#ifdef __STDC_LIB_EXT1__ + ::strnlen_s +#else + ::strnlen +#endif + ; + /** Forward declaration **/ class LoggerDetails; +template +QUILL_NODISCARD constexpr bool is_type_of_c_array() +{ + using ArgType = detail::remove_cvref_t; + return std::is_array::value && + std::is_same::type>, char>::value; +} + template QUILL_NODISCARD constexpr bool is_type_of_c_string() { @@ -177,7 +198,13 @@ template QUILL_NODISCARD QUILL_ATTRIBUTE_HOT constexpr size_t get_args_sizes(size_t* c_string_sizes, Arg const& arg, Args const&... args) { - if constexpr (is_type_of_c_string()) + if constexpr (is_type_of_c_array()) + { + size_t const len = strnlen(arg, detail::array_size_v) + 1; + c_string_sizes[CstringIdx] = len; + return len + get_args_sizes(c_string_sizes, args...); + } + else if constexpr (is_type_of_c_string()) { size_t const len = strlen(arg) + 1; c_string_sizes[CstringIdx] = len; @@ -225,7 +252,24 @@ template QUILL_NODISCARD QUILL_ATTRIBUTE_HOT constexpr std::byte* encode_args(size_t* c_string_sizes, std::byte* out, Arg&& arg, Args&&... args) { - if constexpr (is_type_of_c_string()) + if constexpr (is_type_of_c_array()) + { + const auto size = c_string_sizes[CstringIdx]; + constexpr auto array_size = detail::array_size_v; + if (QUILL_UNLIKELY(size > array_size)) + { + // no '\0' in c array + assert(size == array_size + 1); + std::memcpy(out, arg, array_size); + out[size - 1] = std::byte{'\0'}; + } + else + { + std::memcpy(out, arg, size); + } + return encode_args(c_string_sizes, out + size, std::forward(args)...); + } + else if constexpr (is_type_of_c_string()) { std::memcpy(out, arg, c_string_sizes[CstringIdx]); return encode_args(c_string_sizes, out + c_string_sizes[CstringIdx], diff --git a/quill/include/quill/detail/misc/TypeTraitsCopyable.h b/quill/include/quill/detail/misc/TypeTraitsCopyable.h index 827c9adb..d5b88c7d 100644 --- a/quill/include/quill/detail/misc/TypeTraitsCopyable.h +++ b/quill/include/quill/detail/misc/TypeTraitsCopyable.h @@ -60,6 +60,9 @@ struct remove_cvref template< class T > using remove_cvref_t = typename remove_cvref::type; +template +constexpr size_t array_size_v = std::extent>::value; + /** * fmtquill::streamed detection */ diff --git a/quill/test/LogTest.cpp b/quill/test/LogTest.cpp index 01c6db27..f7b615b9 100644 --- a/quill/test/LogTest.cpp +++ b/quill/test/LogTest.cpp @@ -245,6 +245,64 @@ TEST_CASE("default_logger_ints_and_large_string") quill::detail::remove_file(filename); } +/***/ +TEST_CASE("default_logger_ints_and_c_array") +{ + fs::path const filename{"test_default_logger_ints_and_c_array"}; + { + LogManager lm; + + quill::Config cfg; + cfg.default_handlers.emplace_back(lm.handler_collection().create_handler( + filename.string(), + []() + { + quill::FileHandlerConfig cfg; + cfg.set_open_mode('w'); + return cfg; + }(), + FileEventNotifier{})); + lm.configure(cfg); + + lm.start_backend_worker(false, std::initializer_list{}); + + std::thread frontend( + [&lm]() + { + Logger* default_logger = lm.logger_collection().get_logger(); + + union + { + char no_0[2]; + char mid_0[6]{'a', 'b', 'c', 'd', '\0', 'e'}; + } v; + + // log an array so the log message is pushed to the queue + for (int i = 0; i < 2000; ++i) + { + v.no_0[0] = std::to_string(i).back(); + LOG_INFO(default_logger, "Logging int: {}, int: {}, no_0: {}, mid_0: {}", i, i * 10, v.no_0, v.mid_0); + } + + // Let all log get flushed to the file + lm.flush(); + }); + + frontend.join(); + + std::vector const file_contents = quill::testing::file_contents(filename); + + REQUIRE_EQ(file_contents.size(), 2000); + REQUIRE(quill::testing::file_contains( + file_contents, std::string{"LOG_INFO root Logging int: 0, int: 0, no_0: 0b, mid_0: 0bcd"})); + REQUIRE(quill::testing::file_contains( + file_contents, std::string{"LOG_INFO root Logging int: 1999, int: 19990, no_0: 9b, mid_0: 9bcd"})); + + lm.stop_backend_worker(); + } + // quill::detail::remove_file(filename); +} + /***/ TEST_CASE("default_logger_ints_and_large_string_dynamic_log_level") { @@ -2273,4 +2331,4 @@ TEST_CASE("default_logger_with_very_large_random_strings") quill::detail::remove_file(filename); } -TEST_SUITE_END(); \ No newline at end of file +TEST_SUITE_END(); diff --git a/quill/test/TypeTraitsCopyableTest.cpp b/quill/test/TypeTraitsCopyableTest.cpp index 9163ff65..f80bfbad 100644 --- a/quill/test/TypeTraitsCopyableTest.cpp +++ b/quill/test/TypeTraitsCopyableTest.cpp @@ -199,4 +199,13 @@ TEST_CASE("are_copyable") static_assert(!are_copyable_v, "_"); } -TEST_SUITE_END(); \ No newline at end of file +TEST_CASE("array_size_v") +{ + char a[3]; + static_assert(array_size_v == 3, "_"); + + using B = char(&)[1]; + static_assert(array_size_v == 1, "_"); +} + +TEST_SUITE_END();