From 0e1964ba25e603ab564276292606c2e61e841e15 Mon Sep 17 00:00:00 2001 From: Patrick Wuttke Date: Tue, 28 Oct 2025 12:01:50 +0100 Subject: [PATCH] Logging: made LogMessage contain a string view instead of const char*, added BufferSink, changed message parameter of formatted parameter to be string instead of const char*. --- source/mijin/logging/buffer_sink.hpp | 376 +++++++++++++++++++++ source/mijin/logging/debug_output_sink.hpp | 9 +- source/mijin/logging/filters.hpp | 6 +- source/mijin/logging/formatting.hpp | 16 +- source/mijin/logging/logger.hpp | 60 +++- source/mijin/logging/stdio_sink.hpp | 17 +- source/mijin/logging/stream_sink.hpp | 6 +- 7 files changed, 450 insertions(+), 40 deletions(-) create mode 100644 source/mijin/logging/buffer_sink.hpp diff --git a/source/mijin/logging/buffer_sink.hpp b/source/mijin/logging/buffer_sink.hpp new file mode 100644 index 0000000..056e1c6 --- /dev/null +++ b/source/mijin/logging/buffer_sink.hpp @@ -0,0 +1,376 @@ + +#pragma once + +#if !defined(MIJIN_LOGGING_BUFFER_SINK_HPP_INCLUDED) +#define MIJIN_LOGGING_BUFFER_SINK_HPP_INCLUDED 1 + +#include +#include +#include +#include +#include +#include "./logger.hpp" + +namespace mijin +{ +inline constexpr std::size_t BUFFER_SINK_DEFAULT_SIZE = 10 * 1024 * 1024; // default 10 MiB buffer + +template> +class BaseBufferSink; + +namespace impl +{ +static constexpr std::uint32_t INVALID_BUFFER_INDEX = std::numeric_limits::max(); + +template +struct BufferedMessageHeader +{ + const BaseLogChannel* channel; + const BaseLogLevel* level; + std::source_location sourceLocation; + std::uint32_t nextIdx = INVALID_BUFFER_INDEX; + std::uint32_t prevIdx = INVALID_BUFFER_INDEX; + std::uint32_t numChars = 0; +}; + +template +struct MessageBuffer +{ + using char_t = TChar; + using traits_t = TTraits; + using message_t = BaseLogMessage; + using header_t = BufferedMessageHeader; + static constexpr std::size_t BUFFER_SIZE = VBufferSize; + + std::array bytes; + mutable std::mutex mutex; + + [[nodiscard]] + header_t& messageAt(std::uint32_t idx) MIJIN_NOEXCEPT + { + return *reinterpret_cast(bytes.data() + idx); + } + + [[nodiscard]] + std::span messageText(header_t& header) MIJIN_NOEXCEPT + { + return {reinterpret_cast(reinterpret_cast(&header)) + sizeof(header_t), header.numChars}; + } + + [[nodiscard]] + const header_t& messageAt(std::uint32_t idx) const MIJIN_NOEXCEPT + { + return *reinterpret_cast(bytes.data() + idx); + } + + [[nodiscard]] + std::span messageText(const header_t& header) const MIJIN_NOEXCEPT + { + return {reinterpret_cast(reinterpret_cast(&header)) + sizeof(header_t), header.numChars}; + } + + static std::uint32_t messageBytes(std::uint32_t numChars) MIJIN_NOEXCEPT + { + return static_cast(sizeof(header_t)) + (numChars * static_cast(sizeof(char_t))); + } +}; + +template +class BufferSinkRange; + +template +class LockedMessageBuffer +{ +public: + using char_t = TChar; + using traits_t = TTraits; + + static constexpr std::size_t BUFFER_SIZE = VBufferSize; + + using range_t = impl::BufferSinkRange; +private: + using buffer_t = MessageBuffer; + + const buffer_t* buffer_ = nullptr; + std::uint32_t firstIdx_ = INVALID_BUFFER_INDEX; + std::uint32_t lastIdx_ = INVALID_BUFFER_INDEX; + + LockedMessageBuffer(const buffer_t& buffer, std::uint32_t firstIdx, std::uint32_t lastIdx) MIJIN_NOEXCEPT + : buffer_(&buffer), firstIdx_(firstIdx), lastIdx_(lastIdx) { + buffer_->mutex.lock(); + } +public: + LockedMessageBuffer() noexcept = default; + LockedMessageBuffer(const LockedMessageBuffer&) = delete; + LockedMessageBuffer(LockedMessageBuffer&& other) noexcept + : buffer_(std::exchange(other.buffer_, nullptr)), firstIdx_(other.firstIdx_), lastIdx_(other.lastIdx_) {} + + ~LockedMessageBuffer() noexcept + { + reset(); + } + + LockedMessageBuffer& operator=(const LockedMessageBuffer&) = delete; + LockedMessageBuffer& operator=(LockedMessageBuffer&& other) noexcept + { + if (this != &other) + { + reset(); + buffer_ = std::exchange(other.buffer_, nullptr); + firstIdx_ = other.firstIdx_; + lastIdx_ = other.lastIdx_; + } + return *this; + } + + [[nodiscard]] + range_t getMessages() const MIJIN_NOEXCEPT; +private: + void reset() + { + if (buffer_ != nullptr) + { + buffer_->mutex.unlock(); + buffer_ = nullptr; + } + } + + template + friend class mijin::BaseBufferSink; +}; + +template +class BufferSinkIterator +{ +public: + using char_t = TChar; + using traits_t = TTraits; + using message_t = BaseLogMessage; + static constexpr std::size_t BUFFER_SIZE = VBufferSize; + + using difference_type = std::ptrdiff_t; + using value_type = const message_t; + using pointer = void; + using reference = value_type; + using iterator_category = std::bidirectional_iterator_tag; +private: + using buffer_t = MessageBuffer; + using header_t = BufferedMessageHeader; + using string_view_t = message_t::string_view_t; + + const buffer_t* buffer_ = nullptr; + std::uint32_t idx_ = INVALID_BUFFER_INDEX; + std::uint32_t prevIdx_ = INVALID_BUFFER_INDEX; + + BufferSinkIterator(const buffer_t& buffer, std::uint32_t idx, std::uint32_t prevIdx) MIJIN_NOEXCEPT : buffer_(&buffer), idx_(idx), prevIdx_(prevIdx) {} +public: + BufferSinkIterator() MIJIN_NOEXCEPT = default; + BufferSinkIterator(const BufferSinkIterator&) MIJIN_NOEXCEPT = default; + + BufferSinkIterator& operator=(const BufferSinkIterator&) MIJIN_NOEXCEPT = default; + + auto operator<=>(const BufferSinkIterator&) const noexcept = default; + + reference operator*() const MIJIN_NOEXCEPT + { + MIJIN_ASSERT(idx_ != INVALID_BUFFER_INDEX, "Attempting to dereference an invalid iterator."); + const header_t& header = buffer_->messageAt(idx_); + const string_view_t text(buffer_->messageText(header)); + return { + .text = text, + .channel = header.channel, + .level = header.level, + .sourceLocation = header.sourceLocation + }; + } + + BufferSinkIterator& operator++() MIJIN_NOEXCEPT + { + MIJIN_ASSERT(idx_ != INVALID_BUFFER_INDEX, "Attempting to increment an invalid iterator."); + prevIdx_ = idx_; + idx_ = buffer_->messageAt(idx_).nextIdx; + return *this; + } + + BufferSinkIterator operator++(int) MIJIN_NOEXCEPT + { + BufferSinkIterator copy(*this); + operator++(); + return copy; + } + + BufferSinkIterator& operator--() MIJIN_NOEXCEPT + { + MIJIN_ASSERT(prevIdx_ != INVALID_BUFFER_INDEX, "Attempting to decrement an invalid iterator."); + idx_ = prevIdx_; + prevIdx_ = buffer_->messageAt(idx_).prevIdx; + return *this; + } + + BufferSinkIterator operator--(int) MIJIN_NOEXCEPT + { + BufferSinkIterator copy(*this); + operator--(); + return copy; + } + + template + friend class BufferSinkRange; +}; +static_assert(std::forward_iterator, BUFFER_SINK_DEFAULT_SIZE>>); + +template +class BufferSinkRange +{ +public: + using char_t = TChar; + using traits_t = TTraits; + static constexpr std::size_t BUFFER_SIZE = VBufferSize; + + using iterator = BufferSinkIterator; + using const_iterator = iterator; +private: + using buffer_t = MessageBuffer; + + const buffer_t* buffer_ = nullptr; + std::uint32_t firstIdx_ = INVALID_BUFFER_INDEX; + std::uint32_t lastIdx_ = INVALID_BUFFER_INDEX; + + BufferSinkRange(const buffer_t& buffer, std::uint32_t firstIdx, std::uint32_t lastIdx) MIJIN_NOEXCEPT + : buffer_(&buffer), firstIdx_(firstIdx), lastIdx_(lastIdx) { + } +public: + BufferSinkRange() noexcept = default; + BufferSinkRange(const BufferSinkRange&) MIJIN_NOEXCEPT = default; + BufferSinkRange& operator=(const BufferSinkRange&) MIJIN_NOEXCEPT = default; + + [[nodiscard]] + iterator begin() const MIJIN_NOEXCEPT { return {*buffer_, firstIdx_, INVALID_BUFFER_INDEX}; } + + [[nodiscard]] + iterator end() const MIJIN_NOEXCEPT { return {*buffer_, INVALID_BUFFER_INDEX, lastIdx_}; } + + [[nodiscard]] + const_iterator cbegin() const MIJIN_NOEXCEPT { return {*buffer_, firstIdx_, INVALID_BUFFER_INDEX}; } + + [[nodiscard]] + const_iterator cend() const MIJIN_NOEXCEPT { return {*buffer_, INVALID_BUFFER_INDEX, lastIdx_}; } + + friend class LockedMessageBuffer; +}; + +static_assert(std::bidirectional_iterator, 100>>); +static_assert(std::ranges::bidirectional_range, 100>>); + +template +auto LockedMessageBuffer::getMessages() const MIJIN_NOEXCEPT -> range_t +{ + return range_t(*buffer_, firstIdx_, lastIdx_); +} +} + +using impl::LockedMessageBuffer; + +template +class BaseBufferSink : public BaseLogSink +{ +public: + using base_t = BaseLogSink; + using typename base_t::char_t; + using typename base_t::traits_t; + using typename base_t::message_t; + + static constexpr std::size_t BUFFER_SIZE = VBufferSize; + + using locked_buffer_t = LockedMessageBuffer; +private: + using buffer_t = impl::MessageBuffer; + using header_t = impl::BufferedMessageHeader; + + buffer_t buffer_; + std::uint32_t firstIdx_ = impl::INVALID_BUFFER_INDEX; + std::uint32_t lastIdx_ = impl::INVALID_BUFFER_INDEX; +public: + void handleMessage(const message_t& message) MIJIN_NOEXCEPT override + { + const std::uint32_t numChars = static_cast(message.text.size()); + const std::uint32_t totalBytes = buffer_t::messageBytes(numChars); + + std::scoped_lock _(buffer_.mutex); // TODO: use a message queue and try_lock here + if (lastIdx_ == impl::INVALID_BUFFER_INDEX) + { + // no message yet + insertMessageAt(0, message); + firstIdx_ = 0; + return; + } + + header_t& lastHeader = buffer_.messageAt(lastIdx_); + const std::uint32_t newIdx = lastIdx_ + buffer_t::messageBytes(lastHeader.numChars); + if (newIdx + totalBytes < BUFFER_SIZE) + { + // enough space in the buffer, can append + insertMessageAt(newIdx, message); + } + else + { + // not enough space, put at front + insertMessageAt(0, message); + } + } + + [[nodiscard]] + locked_buffer_t lockBuffer() const MIJIN_NOEXCEPT { return locked_buffer_t(buffer_, firstIdx_, lastIdx_); } +private: + void insertMessageAt(std::uint32_t idx, const message_t& message) + { + const std::uint32_t numChars = static_cast(message.text.size()); + freeSpace(idx, idx + buffer_t::messageBytes(numChars)); + + if (lastIdx_ != impl::INVALID_BUFFER_INDEX) { + buffer_.messageAt(lastIdx_).nextIdx = idx; + } + + header_t& newHeader = buffer_.messageAt(idx); + ::new (&newHeader) header_t({ + .channel = message.channel, + .level = message.level, + .sourceLocation = message.sourceLocation, + .nextIdx = impl::INVALID_BUFFER_INDEX, + .prevIdx = lastIdx_, + .numChars = numChars + }); + lastIdx_ = idx; + std::ranges::copy(message.text, buffer_.messageText(newHeader).begin()); + } + + void freeSpace(std::uint32_t startIdx, std::uint32_t endIdx) + { + while (firstIdx_ != impl::INVALID_BUFFER_INDEX && firstIdx_ >= startIdx && firstIdx_ < endIdx) + { + header_t& message = buffer_.messageAt(firstIdx_); + firstIdx_ = message.nextIdx; + message.~header_t(); + } + + // cleared everything? + if (firstIdx_ == impl::INVALID_BUFFER_INDEX) { + lastIdx_ = impl::INVALID_BUFFER_INDEX; + } + else { + buffer_.messageAt(firstIdx_).prevIdx = impl::INVALID_BUFFER_INDEX; + } + } +}; + +#define SINK_COMMON_ARGS(chr_type) std::size_t BUFFER_SIZE +#define SINK_SET_ARGS(chr_type) BUFFER_SIZE, chr_type + +MIJIN_DEFINE_CHAR_VERSIONS_TMPL(BufferSink, SINK_COMMON_ARGS, SINK_SET_ARGS) + +#undef SINK_COMMON_ARGS +#undef SINK_SET_ARGS +} // namespace mijin + + +#endif // !defined(MIJIN_LOGGING_STDIO_SINK_HPP_INCLUDED) diff --git a/source/mijin/logging/debug_output_sink.hpp b/source/mijin/logging/debug_output_sink.hpp index 54c900e..da6d51f 100644 --- a/source/mijin/logging/debug_output_sink.hpp +++ b/source/mijin/logging/debug_output_sink.hpp @@ -26,27 +26,28 @@ public: using typename base_t::allocator_t; using typename base_t::formatter_ptr_t; using typename base_t::message_t; + using typename base_t::string_t; public: explicit BaseDebugOutputSink(formatter_ptr_t formatter, allocator_t allocator = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v) : base_t(std::move(formatter), std::move(allocator)) {} - void handleMessageFormatted(const message_t&, const char_t* formatted) MIJIN_NOEXCEPT override + void handleMessageFormatted(const message_t&, const string_t& formatted) MIJIN_NOEXCEPT override { if constexpr (std::is_same_v) { - OutputDebugStringA(formatted); + OutputDebugStringA(formatted.c_str()); OutputDebugStringA("\n"); } else if constexpr (std::is_same_v) { - OutputDebugStringW(formatted); + OutputDebugStringW(formatted.c_str()); OutputDebugStringW(L"\n"); } else if constexpr (sizeof(char_t) == sizeof(char)) { // char8_t etc. - OutputDebugStringA(std::bit_cast(formatted)); + OutputDebugStringA(std::bit_cast(formatted.c_str())); OutputDebugStringA("\n"); } else diff --git a/source/mijin/logging/filters.hpp b/source/mijin/logging/filters.hpp index 0f2b2b3..bc2e9db 100644 --- a/source/mijin/logging/filters.hpp +++ b/source/mijin/logging/filters.hpp @@ -8,11 +8,11 @@ namespace mijin { -template -class BaseLevelFilter : public BaseLogFilter +template> +class BaseLevelFilter : public BaseLogFilter { public: - using base_t = BaseLogFilter; + using base_t = BaseLogFilter; using typename base_t::char_t; using typename base_t::message_t; private: diff --git a/source/mijin/logging/formatting.hpp b/source/mijin/logging/formatting.hpp index cae956d..395be21 100644 --- a/source/mijin/logging/formatting.hpp +++ b/source/mijin/logging/formatting.hpp @@ -78,19 +78,19 @@ MIJIN_DEFINE_CHAR_VERSIONS_TMPL(SimpleLogFormatter, FORMATTER_COMMON_ARGS, FORMA template requires(allocator_type>) -class BaseFormattingLogSink : public BaseLogSink +class BaseFormattingLogSink : public BaseLogSink { public: - using base_t = BaseLogSink; + using base_t = BaseLogSink; - using char_t = TChar; - using traits_t = TTraits; + using typename base_t::char_t; + using typename base_t::message_t; + using typename base_t::traits_t; using allocator_t = TAllocator; using formatter_t = BaseLogFormatter; using formatter_deleter_t = TDeleter; using formatter_ptr_t = DynamicPointer; using string_t = formatter_t::string_t; - using typename base_t::message_t; private: formatter_ptr_t mFormatter; string_t mBuffer; @@ -100,13 +100,13 @@ public: : mFormatter(std::move(formatter)), mBuffer(std::move(allocator)) {} - virtual void handleMessageFormatted(const message_t& message, const char_t* formatted) MIJIN_NOEXCEPT = 0; + virtual void handleMessageFormatted(const message_t& message, const string_t& formatted) MIJIN_NOEXCEPT = 0; void handleMessage(const message_t& message) noexcept override { mBuffer.clear(); mFormatter->format(message, mBuffer); - handleMessageFormatted(message, mBuffer.c_str()); + handleMessageFormatted(message, mBuffer); } }; @@ -162,7 +162,7 @@ void BaseSimpleLogFormatter::format(const LogMessage { std::format_to(std::back_inserter(outFormatted), MIJIN_SMART_QUOTE(char_t, "{}"), value); } - else if constexpr (is_string_v || is_cstring_v) + else if constexpr (is_string_v || is_string_view_v || is_cstring_v) { convertStringType(value, outFormatted); } diff --git a/source/mijin/logging/logger.hpp b/source/mijin/logging/logger.hpp index 5de4045..94bd571 100644 --- a/source/mijin/logging/logger.hpp +++ b/source/mijin/logging/logger.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "../internal/common.hpp" #include "../util/annot.hpp" @@ -78,12 +79,14 @@ struct BaseLogChannel MIJIN_DEFINE_CHAR_VERSIONS(LogChannel) -template +template> struct BaseLogMessage { using char_t = TChar; + using traits_t = TTraits; + using string_view_t = std::basic_string_view; - const char_t* text; + string_view_t text; const BaseLogChannel* channel; const BaseLogLevel* level; std::source_location sourceLocation; @@ -91,12 +94,13 @@ struct BaseLogMessage MIJIN_DEFINE_CHAR_VERSIONS(LogMessage) -template +template> class BaseLogSink { public: using char_t = TChar; - using message_t = BaseLogMessage; + using traits_t = TTraits; + using message_t = BaseLogMessage; virtual ~BaseLogSink() noexcept = default; @@ -105,12 +109,13 @@ public: MIJIN_DEFINE_CHAR_VERSIONS(LogSink) -template +template> class BaseLogFilter { public: using char_t = TChar; - using message_t = BaseLogMessage; + using traits_t = TTraits; + using message_t = BaseLogMessage; virtual ~BaseLogFilter() noexcept = default; @@ -128,12 +133,13 @@ public: using traits_t = TTraits; using allocator_t = TAllocator; - using sink_t = BaseLogSink; - using filter_t = BaseLogFilter; + using sink_t = BaseLogSink; + using filter_t = BaseLogFilter; using level_t = BaseLogLevel; using channel_t = BaseLogChannel; - using message_t = BaseLogMessage; + using message_t = BaseLogMessage; using string_t = std::basic_string; + using string_view_t = std::basic_string_view; private: struct SinkEntry { @@ -146,13 +152,10 @@ public: explicit BaseLogger(TAllocator allocator = {}) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v, TAllocator&&>)) : mSinks(TAllocator(std::move(allocator))) {} - BaseLogger(const BaseLogger&) = default; - BaseLogger(BaseLogger&&) = default; BaseLogger& operator=(const BaseLogger&) = default; - BaseLogger& operator=(BaseLogger&&) = default; void addSink(sink_t& sink) @@ -167,6 +170,19 @@ public: mSinks.push_back({&sink, &filter}); } + bool removeSink(sink_t& sink) + { + std::unique_lock _(mMutex); + auto it = std::ranges::find_if(mSinks, [&](const SinkEntry& entry) { + return entry.sink == &sink; + }); + if (it == mSinks.end()) { + return false; + } + mSinks.erase(it); + return true; + } + void postMessage(const message_t& message) const MIJIN_NOEXCEPT { std::unique_lock _(mMutex); @@ -179,7 +195,7 @@ public: } } - void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation, const char_t* msg) const MIJIN_NOEXCEPT + void logUnformatted(const level_t& level, const channel_t& channel, std::source_location sourceLocation, string_view_t msg) const MIJIN_NOEXCEPT { postMessage({ .text = msg, @@ -189,6 +205,11 @@ public: }); } + void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation, std::basic_format_string msg) const MIJIN_NOEXCEPT + { + logUnformatted(level, channel, sourceLocation, msg.get()); + } + template void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation, std::basic_format_string...> fmt, TArgs&& ... args) const @@ -196,7 +217,7 @@ public: { string_t buffer(allocator_t(mSinks.get_allocator())); std::format_to(std::back_inserter(buffer), fmt, std::forward(args)...); - log(level, channel, std::move(sourceLocation), buffer.c_str()); + logUnformatted(level, channel, std::move(sourceLocation), buffer); } }; @@ -254,6 +275,9 @@ MIJIN_DEFINE_LOG_LEVEL(ERROR, MIJIN_LOG_LEVEL_VALUE_ERROR) #define MIJIN_LOG_ALWAYS(level, channel, ...) MIJIN_FUNCNAME_GET_LOGGER().log( \ MIJIN_LOG_LEVEL_OBJECT(level), MIJIN_LOG_CHANNEL_OBJECT(channel), std::source_location::current(), __VA_ARGS__ \ ) +#define MIJIN_LOG_ALWAYS_RAW(level, channel, ...) MIJIN_FUNCNAME_GET_LOGGER().logUnformatted( \ + MIJIN_LOG_LEVEL_OBJECT(level), MIJIN_LOG_CHANNEL_OBJECT(channel), std::source_location::current(), __VA_ARGS__ \ +) #define MIJIN_LOG(level, channel, ...) \ if constexpr (MIJIN_LOG_LEVEL_OBJECT(level).value < MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE()) {} \ else MIJIN_LOG_ALWAYS(level, channel, __VA_ARGS__) @@ -262,6 +286,14 @@ else MIJIN_LOG_ALWAYS(level, channel, __VA_ARGS__) if constexpr (MIJIN_LOG_LEVEL_OBJECT(level).value < MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE()) {} \ else if (cond) MIJIN_LOG_ALWAYS(level, channel, __VA_ARGS__) +#define MIJIN_LOG_RAW(level, channel, ...) \ +if constexpr (MIJIN_LOG_LEVEL_OBJECT(level).value < MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE()) {} \ +else MIJIN_LOG_ALWAYS_RAW(level, channel, __VA_ARGS__) + +#define MIJIN_LOG_IF_RAW(cond, level, channel, ...) \ +if constexpr (MIJIN_LOG_LEVEL_OBJECT(level).value < MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE()) {} \ +else if (cond) MIJIN_LOG_ALWAYS_RAW(level, channel, __VA_ARGS__) + #define MIJIN_SET_CLASS_LOGGER(loggerExpr) \ const auto& MIJIN_FUNCNAME_GET_LOGGER() const noexcept \ { \ diff --git a/source/mijin/logging/stdio_sink.hpp b/source/mijin/logging/stdio_sink.hpp index 423806a..b00609d 100644 --- a/source/mijin/logging/stdio_sink.hpp +++ b/source/mijin/logging/stdio_sink.hpp @@ -19,6 +19,7 @@ public: using typename base_t::allocator_t; using typename base_t::formatter_ptr_t; using typename base_t::message_t; + using typename base_t::string_t; private: int mMinStderrLevel = MIJIN_LOG_LEVEL_VALUE_WARNING; public: @@ -33,30 +34,30 @@ public: void setMinStderrLevel(const BaseLogLevel& level) MIJIN_NOEXCEPT { mMinStderrLevel = level.value; } - void handleMessageFormatted(const message_t& message, const char_t* formatted) MIJIN_NOEXCEPT override + void handleMessageFormatted(const message_t& message, const string_t& formatted) MIJIN_NOEXCEPT override { FILE* stream = (message.level->value >= mMinStderrLevel) ? stderr : stdout; if constexpr (std::is_same_v) { - std::fputs(formatted, stream); - std::fputc('\n', stream); + (void) std::fputs(formatted.c_str(), stream); + (void) std::fputc('\n', stream); } else if constexpr (std::is_same_v) { - std::fputws(formatted, stream); - std::fputwc(L'\n', stream); + (void) std::fputws(formatted.c_str(), stream); + (void) std::fputwc(L'\n', stream); } else if constexpr (sizeof(char_t) == sizeof(char)) { // char8_t etc. - std::fputs(std::bit_cast(formatted), stream); - std::fputc('\n', stream); + (void) std::fputs(std::bit_cast(formatted.c_str()), stream); + (void) std::fputc('\n', stream); } else { static_assert(always_false_v, "Character type not supported."); } - std::fflush(stream); + (void) std::fflush(stream); } }; diff --git a/source/mijin/logging/stream_sink.hpp b/source/mijin/logging/stream_sink.hpp index 9dd5523..12f70d3 100644 --- a/source/mijin/logging/stream_sink.hpp +++ b/source/mijin/logging/stream_sink.hpp @@ -6,7 +6,6 @@ #include "./formatting.hpp" #include "../io/stream.hpp" -#include "../util/traits.hpp" namespace mijin { @@ -20,6 +19,7 @@ public: using typename base_t::allocator_t; using typename base_t::formatter_ptr_t; using typename base_t::message_t; + using typename base_t::string_t; using stream_ptr_t = DynamicPointer; private: stream_ptr_t mStream; @@ -36,12 +36,12 @@ public: mStream = std::move(stream).release(); } - void handleMessageFormatted(const message_t& /* message */, const char_t* formatted) MIJIN_NOEXCEPT override + void handleMessageFormatted(const message_t& /* message */, const string_t& formatted) MIJIN_NOEXCEPT override { if (!mStream) { return; } - (void) mStream->writeSpan(std::basic_string_view(formatted)); + (void) mStream->writeSpan(formatted); (void) mStream->write('\n'); mStream->flush(); }