Skip to content
67 changes: 64 additions & 3 deletions src/main/cpp/asyncbuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

#include <log4cxx/helpers/asyncbuffer.h>
#include <log4cxx/helpers/transcoder.h>
#if defined(__cpp_concepts) && 202002 <= __cpp_concepts
#include <variant>
#endif // defined(__cpp_concepts) && 202002 <= __cpp_concepts

namespace LOG4CXX_NS
{
Expand All @@ -26,9 +29,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 }
{}

Expand Down Expand Up @@ -127,7 +135,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())
Expand Down Expand Up @@ -171,6 +206,31 @@ void AsyncBuffer::clear()
}
}

#if defined(__cpp_concepts) && 202002 <= __cpp_concepts
/**
* Append \c function to this buffer.
*/
void AsyncBuffer::append(const MessageBufferAppender& f)
{
if (!m_priv)
m_priv = std::make_unique<Private>(f);
else
m_priv->data.push_back(f);
}

#if 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 // LOG4CXX_WCHAR_T_API
#else // !(defined(__cpp_concepts) && 202002 <= __cpp_concepts
/**
* Append \c function to this buffer.
*/
Expand All @@ -181,6 +241,7 @@ void AsyncBuffer::append(const MessageBufferAppender& f)
else
m_priv->data.push_back(f);
}
#endif // !(defined(__cpp_concepts) && 202002 <= __cpp_concepts

} // namespace helpers
} // namespace LOG4CXX_NS
Expand Down
110 changes: 106 additions & 4 deletions src/main/include/log4cxx/helpers/asyncbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -59,13 +62,53 @@ class LOG4CXX_EXPORT AsyncBuffer
* @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 LOG4CXX_LOGCHAR_IS_UTF8
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 // !LOG4CXX_LOGCHAR_IS_UTF8
if constexpr (requires(std::wostream& buf, T v) { buf << v; })
{
append([value](WideMessageBuffer& msgBuf)
{
msgBuf << value;
});
}
else if constexpr (requires(std::ostream& buf, T v) { buf << v; })
{
append([value](CharMessageBuffer& msgBuf)
{
msgBuf << value;
});
}
else
static_assert(false, "operator<<(std::wostream&) overload must be provided");
#endif // !LOG4CXX_LOGCHAR_IS_UTF8
#else // !(defined(__cpp_concepts) && 202002 <= __cpp_concepts)
append([value](LogCharMessageBuffer& msgBuf)
{
msgBuf << value;
});
#endif // !(defined(__cpp_concepts) && 202002 <= __cpp_concepts)
return *this;
}

Expand All @@ -74,16 +117,57 @@ class LOG4CXX_EXPORT AsyncBuffer
* @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 LOG4CXX_LOGCHAR_IS_UTF8
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 // !LOG4CXX_LOGCHAR_IS_UTF8
if constexpr (requires(std::wostream& buf, T v) { buf << v; })
{
append([value = std::move(rvalue)](WideMessageBuffer& msgBuf)
{
msgBuf << value;
});
}
else if constexpr (requires(std::ostream& buf, T v) { buf << v; })
{
append([value = std::move(rvalue)](CharMessageBuffer& msgBuf)
{
msgBuf << value;
});
}
else
static_assert(false, "operator<<(std::wostream&) overload must be provided");
#endif // !LOG4CXX_LOGCHAR_IS_UTF8
#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
/**
Expand Down Expand Up @@ -131,12 +215,30 @@ class LOG4CXX_EXPORT AsyncBuffer
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);
Expand Down
45 changes: 30 additions & 15 deletions src/test/cpp/asyncappendertestcase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,27 +238,42 @@ class AsyncAppenderTestCase : public AppenderSkeletonTestCase
r->getRootLogger()->addAppender(vectorAppender);
r->setConfigured(true);
});
LogString logStr = LOG4CXX_STR("Some logchar string ");
#if LOG4CXX_LOGCHAR_IS_UTF8
std::wstring otherStr(L"Some non-logchar string ");
#else // !LOG4CXX_LOGCHAR_IS_UTF8
std::string otherStr("Some non-logchar string ");
#endif // !LOG4CXX_LOGCHAR_IS_UTF8

// 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
LOG4CXX_INFO(root, logStr << 42);
LOG4CXX_INFO_ASYNC(root, logStr << 42);
int expectedEventCount = 2;
#if !LOG4CXX_LOGCHAR_IS_UTF8 || LOG4CXX_WCHAR_T_API
LOG4CXX_INFO(root, otherStr << 42);
++expectedEventCount;
#if defined(__cpp_concepts) && 202002 <= __cpp_concepts
LOG4CXX_INFO_ASYNC(root, otherStr << 42);
++expectedEventCount;
#endif // defined(__cpp_concepts) && 202002 <= __cpp_concepts
#endif // !LOG4CXX_LOGCHAR_IS_UTF8 || 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 (4 <= expectedEventCount)
LOGUNIT_ASSERT_EQUAL(v[2]->getRenderedMessage(), v[3]->getRenderedMessage());
#ifdef GENERATE_THE_STATIC_ASSERT_COMPILATION_ERROR
struct AStruct
{
int value;
} data{ 43 };
LOG4CXX_INFO_ASYNC(root, "data.value=" << data);
#endif
}

// this test checks all messages are delivered when an AsyncAppender is closed
Expand Down