From 29254e6ce554f6eb31da8c005c55dceaff503086 Mon Sep 17 00:00:00 2001 From: Odysseas Georgoudis Date: Fri, 16 Aug 2024 15:13:38 +0100 Subject: [PATCH] support std::string with custom allocator --- CHANGELOG.md | 3 +- include/quill/core/Codec.h | 19 +++++++-- test/integration_tests/StringLoggingTest.cpp | 45 +++++++++++++++++++- 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6b4bb72..a3cb3414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,7 +78,8 @@ - Simplified the log tags API. The `Tags` class has been removed. You now pass a `char const*` directly to the macros. Additionally, macros previously named `WITH_TAGS` have been renamed to `_TAGS`. For example, `LOG_INFO_WITH_TAGS` is now `LOG_INFO_TAGS`. - Renamed `backend_cpu_affinity` to `cpu_affinity` in `BackendOptions` to improve consistency. - Simplified project structure by removing the extra quill directory and made minor CMake improvements; `include/quill` is now directly in the root. - +- Added support for `std::string` with custom allocator ([#524](https://github.com/odygrd/quill/issues/524)) + ## v6.1.2 - Fix pkg-config file on windows diff --git a/include/quill/core/Codec.h b/include/quill/core/Codec.h index d939307c..628c4435 100644 --- a/include/quill/core/Codec.h +++ b/include/quill/core/Codec.h @@ -49,6 +49,17 @@ constexpr auto strnlen = ::strnlen #endif ; + +/** std string detection, ignoring the Allocator type **/ +template +struct is_std_string : std::false_type +{ +}; + +template +struct is_std_string, Allocator>> : std::true_type +{ +}; } // namespace detail /** typename = void for specializations with enable_if **/ @@ -75,7 +86,7 @@ struct Codec conditional_arg_size_cache.push_back(static_cast(strlen(arg) + 1u)); return conditional_arg_size_cache.back(); } - else if constexpr (std::disjunction_v, std::is_same>) + else if constexpr (std::disjunction_v, std::is_same>) { // for std::string we also need to store the size in order to correctly retrieve it // the reason for this is that if we create e.g: @@ -126,7 +137,7 @@ struct Codec std::memcpy(buffer, arg, len); buffer += len; } - else if constexpr (std::disjunction_v, std::is_same>) + else if constexpr (std::disjunction_v, std::is_same>) { // for std::string we store the size first, in order to correctly retrieve it // Copy the length first and then the actual string @@ -170,7 +181,7 @@ struct Codec buffer += strlen(arg) + 1; // for c_strings we add +1 to the length as we also want to copy the null terminated char return arg; } - else if constexpr (std::disjunction_v, std::is_same>) + else if constexpr (std::disjunction_v, std::is_same>) { // for std::string we first need to retrieve the length size_t len; @@ -204,7 +215,7 @@ struct Codec // for std::string_view we would need fmt/format.h args_store->push_back(fmtquill::string_view{arg, strlen(arg)}); } - else if constexpr (std::disjunction_v, std::is_same>) + else if constexpr (std::disjunction_v, std::is_same>) { std::string_view arg = decode_arg(buffer); diff --git a/test/integration_tests/StringLoggingTest.cpp b/test/integration_tests/StringLoggingTest.cpp index 5566eed6..088af573 100644 --- a/test/integration_tests/StringLoggingTest.cpp +++ b/test/integration_tests/StringLoggingTest.cpp @@ -7,12 +7,49 @@ #include "quill/sinks/FileSink.h" #include +#include #include #include #include using namespace quill; +template +class CustomAllocator : public std::allocator +{ +public: + using value_type = T; + + CustomAllocator() noexcept : std::allocator() {} + + template + explicit CustomAllocator(const CustomAllocator& other) noexcept : std::allocator(other) + { + } + + [[nodiscard]] T* allocate(std::size_t n) { return std::allocator::allocate(n); } + + void deallocate(T* p, std::size_t n) noexcept { std::allocator::deallocate(p, n); } + + template + struct rebind + { + using other = CustomAllocator; + }; +}; + +template +bool operator==(const CustomAllocator&, const CustomAllocator&) +{ + return true; +} + +template +bool operator!=(const CustomAllocator&, const CustomAllocator&) +{ + return false; +} + /***/ TEST_CASE("string_logging") { @@ -39,6 +76,8 @@ TEST_CASE("string_logging") Logger* logger = Frontend::create_or_get_logger(logger_name, std::move(file_sink)); { + std::basic_string, CustomAllocator> cas = + "custom allocator string"; std::string s = "adipiscing"; std::string const& scr = s; std::string& sr = s; @@ -62,6 +101,7 @@ TEST_CASE("string_logging") const char* npcs = "Example\u0003String\u0004"; LOG_INFO(logger, "non printable cs [{}]", npcs); + LOG_INFO(logger, "cas [{}]", cas); LOG_INFO(logger, "s [{}]", s); LOG_INFO(logger, "scr [{}]", scr); LOG_INFO(logger, "sr [{}]", sr); @@ -110,7 +150,10 @@ TEST_CASE("string_logging") // Read file and check std::vector const file_contents = quill::testing::file_contents(filename); - REQUIRE_EQ(file_contents.size(), number_of_messages + 15); + REQUIRE_EQ(file_contents.size(), number_of_messages + 16); + + REQUIRE(quill::testing::file_contains( + file_contents, std::string{"LOG_INFO " + logger_name + " cas [custom allocator string]"})); REQUIRE(quill::testing::file_contains( file_contents, std::string{"LOG_INFO " + logger_name + " non printable cs [Example\\x03String\\x04]"}));