This is an automated email from the ASF dual-hosted git repository. swebb2066 pushed a commit to branch enhance_async_macros in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git
commit 2ed31f280c26f83d78e17a0796c0e0583a97d657 Author: Stephen Webb <[email protected]> AuthorDate: Tue Oct 28 15:39:47 2025 +1100 Allow use of wide strings in LOG4CXX_XXXXX_ASYNC macros --- src/main/cpp/asyncbuffer.cpp | 52 +++++++++++++++++-- src/main/include/log4cxx/helpers/asyncbuffer.h | 72 ++++++++++++++++++++++++-- src/test/cpp/asyncappendertestcase.cpp | 26 +++++----- 3 files changed, 130 insertions(+), 20 deletions(-) diff --git a/src/main/cpp/asyncbuffer.cpp b/src/main/cpp/asyncbuffer.cpp index 4a146bad..4de3d8b1 100644 --- a/src/main/cpp/asyncbuffer.cpp +++ b/src/main/cpp/asyncbuffer.cpp @@ -17,6 +17,7 @@ #include <log4cxx/helpers/asyncbuffer.h> #include <log4cxx/helpers/transcoder.h> +#include <variant> namespace LOG4CXX_NS { @@ -26,9 +27,14 @@ namespace helpers struct AsyncBuffer::Private { - std::vector<MessageBufferAppender> data; - - Private(const MessageBufferAppender& f) +#if defined(__cpp_concepts) && 202002 <= __cpp_concepts && LOG4CXX_WCHAR_T_API + using value_t = std::variant<MessageBufferAppender, WideMessageBufferAppender>; +#else // !(defined(__cpp_concepts) && 202002 <= __cpp_concepts && LOG4CXX_WCHAR_T_API) + using value_t = MessageBufferAppender; +#endif // !(defined(__cpp_concepts) && 202002 <= __cpp_concepts && LOG4CXX_WCHAR_T_API) + std::vector<value_t> data; + + Private(const value_t& f) : data{ f } {} @@ -127,7 +133,34 @@ void AsyncBuffer::renderMessage(LogCharMessageBuffer& msg) const if (m_priv) { for (auto& renderer : m_priv->data) +#if defined(__cpp_concepts) && 202002 <= __cpp_concepts && LOG4CXX_WCHAR_T_API + { +#if LOG4CXX_LOGCHAR_IS_UTF8 + if (auto pRenderer = std::get_if<MessageBufferAppender>(&renderer)) + (*pRenderer)(msg); + else + { + WideMessageBuffer wideBuf; + std::get<WideMessageBufferAppender>(renderer)(wideBuf); + LOG4CXX_DECODE_WCHAR(lsMsg, wideBuf.extract_str(wideBuf)); + msg << lsMsg; + } +#else // !LOG4CXX_LOGCHAR_IS_UTF8 + if (auto pRenderer = std::get_if<WideMessageBufferAppender>(&renderer)) + (*pRenderer)(msg); + else + { + CharMessageBuffer narrowBuf; + std::get<MessageBufferAppender>(renderer)(narrowBuf); + LOG4CXX_DECODE_CHAR(lsMsg, narrowBuf.extract_str(narrowBuf)); + msg << lsMsg; + } +#endif // !LOG4CXX_LOGCHAR_IS_UTF8 + } +#else // !(defined(__cpp_concepts) && 202002 <= __cpp_concepts && LOG4CXX_WCHAR_T_API) renderer(msg); +#endif // !(defined(__cpp_concepts) && 202002 <= __cpp_concepts && LOG4CXX_WCHAR_T_API) + #if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT #if LOG4CXX_LOGCHAR_IS_UTF8 if (0 < m_priv->fmt_string.size()) @@ -182,6 +215,19 @@ void AsyncBuffer::append(const MessageBufferAppender& f) m_priv->data.push_back(f); } +#if defined(__cpp_concepts) && 202002 <= __cpp_concepts || LOG4CXX_WCHAR_T_API +/** + * Append \c function to this buffer. + */ +void AsyncBuffer::append(const WideMessageBufferAppender& f) +{ + if (!m_priv) + m_priv = std::make_unique<Private>(f); + else + m_priv->data.push_back(f); +} +#endif // defined(__cpp_concepts) && 202002 <= __cpp_concepts || LOG4CXX_WCHAR_T_API + } // namespace helpers } // namespace LOG4CXX_NS diff --git a/src/main/include/log4cxx/helpers/asyncbuffer.h b/src/main/include/log4cxx/helpers/asyncbuffer.h index 44110ebf..d03fc1f7 100644 --- a/src/main/include/log4cxx/helpers/asyncbuffer.h +++ b/src/main/include/log4cxx/helpers/asyncbuffer.h @@ -27,6 +27,9 @@ #include <fmt/xchar.h> #endif // LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR #endif // LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT +#if defined(__cpp_concepts) && 202002 <= __cpp_concepts +#include <concepts> +#endif namespace LOG4CXX_NS { @@ -59,13 +62,34 @@ public: // Operators * @param value type must be copy-constructable * @return this buffer. */ - template<typename T> + template <typename T> AsyncBuffer& operator<<(const T& value) { +#if defined(__cpp_concepts) && 202002 <= __cpp_concepts + if constexpr (requires(std::ostream& buf, T v) { buf << v; }) + { + append([value](CharMessageBuffer& msgBuf) + { + msgBuf << value; + }); + } +#if LOG4CXX_WCHAR_T_API + else if constexpr (requires(std::wostream& buf, T v) { buf << v; }) + { + append([value](WideMessageBuffer& msgBuf) + { + msgBuf << value; + }); + } +#endif // LOG4CXX_WCHAR_T_API + else + static_assert(false, "operator<<(std::ostream&) overload must be provided"); +#else // !(defined(__cpp_concepts) && 202002 <= __cpp_concepts) append([value](LogCharMessageBuffer& msgBuf) { msgBuf << value; }); +#endif // !(defined(__cpp_concepts) && 202002 <= __cpp_concepts) return *this; } @@ -74,16 +98,38 @@ public: // Operators * @param value type must be move-constructable * @return this buffer. */ - template<typename T> + template <typename T> AsyncBuffer& operator<<(const T&& rvalue) { +#if defined(__cpp_concepts) && 202002 <= __cpp_concepts + if constexpr (requires(std::ostream& buf, T v) { buf << v; }) + { + append([value = std::move(rvalue)](CharMessageBuffer& msgBuf) + { + msgBuf << value; + }); + } +#if LOG4CXX_WCHAR_T_API + else if constexpr (requires(std::wostream& buf, T v) { buf << v; }) + { + append([value = std::move(rvalue)](WideMessageBuffer& msgBuf) + { + msgBuf << value; + }); + } +#endif // LOG4CXX_WCHAR_T_API + else + static_assert(false, "operator<<(std::ostream&) overload must be provided"); +#else // !(defined(__cpp_concepts) && 202002 <= __cpp_concepts) append([value = std::move(rvalue)](LogCharMessageBuffer& msgBuf) { msgBuf << value; }); +#endif // !(defined(__cpp_concepts) && 202002 <= __cpp_concepts) return *this; } -#endif + +#endif // __cpp_init_captures public: // Accessors /** @@ -131,12 +177,30 @@ private: AsyncBuffer& operator=(const AsyncBuffer&) = delete; LOG4CXX_DECLARE_PRIVATE_MEMBER_PTR(Private, m_priv) +#if defined(__cpp_concepts) && 202002 <= __cpp_concepts + using MessageBufferAppender = std::function<void(CharMessageBuffer&)>; + + /** + * Append \c f to this buffer. + */ + void append(const MessageBufferAppender& f); + +#if LOG4CXX_WCHAR_T_API + using WideMessageBufferAppender = std::function<void(WideMessageBuffer&)>; + + /** + * Append \c f to this buffer. + */ + void append(const WideMessageBufferAppender& f); +#endif // LOG4CXX_WCHAR_T_API +#else // !(defined(__cpp_concepts) && 202002 <= __cpp_concepts) using MessageBufferAppender = std::function<void(LogCharMessageBuffer&)>; /** - * Append \c function to this buffer. + * Append \c f to this buffer. */ void append(const MessageBufferAppender& f); +#endif // !(defined(__cpp_concepts) && 202002 <= __cpp_concepts) #if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT void initializeForFmt(StringViewType&& format_string, FmtArgStore&& args); diff --git a/src/test/cpp/asyncappendertestcase.cpp b/src/test/cpp/asyncappendertestcase.cpp index b9dd8bcc..d9ebd06d 100644 --- a/src/test/cpp/asyncappendertestcase.cpp +++ b/src/test/cpp/asyncappendertestcase.cpp @@ -241,24 +241,24 @@ class AsyncAppenderTestCase : public AppenderSkeletonTestCase // Log some messages auto root = r->getRootLogger(); -#if LOG4CXX_LOGCHAR_IS_UTF8 - LOG4CXX_INFO(root, L"Some wide string " << 42); -#else LOG4CXX_INFO(root, "Some narrow string " << 42); -#endif - int expectedMessageCount = 1; -#ifdef LOG4CXX_XXXX_ASYNC_MACROS_WORK_WITH_ANY_CHAR_TYPE - ++expectedMessageCount -#if LOG4CXX_LOGCHAR_IS_UTF8 - LOG4CXX_INFO_ASYNC(root, L"Some wide string " << 42); -#else LOG4CXX_INFO_ASYNC(root, "Some narrow string " << 42); -#endif -#endif // LOG4CXX_XXXX_ASYNC_MACROS_WORK_WITH_ANY_CHAR_TYPE + int expectedEventCount = 2; +#if LOG4CXX_WCHAR_T_API + LOG4CXX_INFO(root, L"Some wide string " << 42); + LOG4CXX_INFO_ASYNC(root, L"Some wide string " << 42); + expectedEventCount += 2; +#endif // LOG4CXX_WCHAR_T_API // Check all messages were received auto& v = vectorAppender->getVector(); - LOGUNIT_ASSERT_EQUAL(expectedMessageCount, int(v.size())); + for (auto& pEvent : v) + LogLog::debug(pEvent->getRenderedMessage()); + LOGUNIT_ASSERT_EQUAL(expectedEventCount, int(v.size())); + LOGUNIT_ASSERT_EQUAL(v[0]->getRenderedMessage(), v[1]->getRenderedMessage()); +#if LOG4CXX_WCHAR_T_API + LOGUNIT_ASSERT_EQUAL(v[2]->getRenderedMessage(), v[3]->getRenderedMessage()); +#endif // LOG4CXX_WCHAR_T_API } // this test checks all messages are delivered when an AsyncAppender is closed
