1 Commits

Author SHA1 Message Date
eda08e509e (WIP) Process stream reimplementation. 2025-11-22 12:49:15 +01:00
14 changed files with 64 additions and 575 deletions

View File

@@ -112,8 +112,6 @@ public:
return !state_.expired();
}
[[nodiscard]] const void* getState() const MIJIN_NOEXCEPT { return state_.lock().get(); }
inline void cancel() const MIJIN_NOEXCEPT;
[[nodiscard]] inline Optional<std::source_location> getLocation() const MIJIN_NOEXCEPT;
#if MIJIN_COROUTINE_ENABLE_DEBUG_INFO
@@ -193,10 +191,10 @@ struct TaskReturn<void, TPromise>
};
}
template<typename TValue = void, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
template<typename TValue, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
using TaskFuture = Future<TValue, TAllocator, MIJIN_COROUTINE_ENABLE_EXCEPTIONS>;
template<typename TValue = void, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
template<typename TValue, template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR>
using TaskFuturePtr = FuturePtr<TValue, TAllocator, MIJIN_COROUTINE_ENABLE_EXCEPTIONS>;
template<typename TValue, template<typename> typename TAllocator>
@@ -966,7 +964,7 @@ template<typename TResult>
}
template<template<typename> typename TAllocator>
inline std::suspend_always c_switchContext(TaskLoop<TAllocator>& taskLoop)
inline std::suspend_always switchContext(TaskLoop<TAllocator>& taskLoop)
{
TaskLoop<TAllocator>& currentTaskLoop = TaskLoop<TAllocator>::current();
if (&currentTaskLoop == &taskLoop) {
@@ -976,12 +974,6 @@ inline std::suspend_always c_switchContext(TaskLoop<TAllocator>& taskLoop)
return {};
}
template<template<typename> typename TAllocator>
inline std::suspend_always switchContext(TaskLoop<TAllocator>& taskLoop)
{
return c_switchContext(taskLoop);
}
template<template<typename> typename TAllocator>
void BaseSimpleTaskLoop<TAllocator>::transferCurrentTask(TaskLoop<TAllocator>& otherLoop) MIJIN_NOEXCEPT
{
@@ -1237,7 +1229,7 @@ void BaseMultiThreadedTaskLoop<TAllocator>::workerThread(std::stop_token stopTok
// run it
getCurrentTask() = &*task;
impl::gCurrentTaskState = task->task->sharedState();
base_t::tickTask(*task);
tickTask(*task);
getCurrentTask() = nullptr;
impl::gCurrentTaskState = nullptr;

View File

@@ -32,10 +32,6 @@ inline constexpr signal_token_t INVALID_SIGNAL_TOKEN = std::numeric_limits<signa
//
MIJIN_DEFINE_FLAG(Oneshot);
MIJIN_DEFINE_FLAG(DisconnectSignal);
template<template<typename> typename TAllocator, typename... TArgs>
class SignalAutoToken;
template<template<typename> typename TAllocator, typename... TArgs>
class BaseSignal
@@ -43,7 +39,6 @@ class BaseSignal
public:
using handler_t = std::function<void(TArgs...)>; // TODO: write a custom function wrapper with allocator support
using token_t = signal_token_t;
using auto_token_t = SignalAutoToken<TAllocator, TArgs...>;
private:
struct RegisteredHandler
{
@@ -80,71 +75,10 @@ public:
template<typename... TArgs>
using Signal = BaseSignal<MIJIN_DEFAULT_ALLOCATOR, TArgs...>;
template<template<typename> typename TAllocator, typename... TArgs>
class SignalAutoToken
{
public:
using signal_t = BaseSignal<TAllocator, TArgs...>;
private:
signal_t* signal_ = nullptr;
signal_token_t token_ = INVALID_SIGNAL_TOKEN;
public:
SignalAutoToken() = default;
SignalAutoToken(const SignalAutoToken&) = delete;
SignalAutoToken(SignalAutoToken&& other) MIJIN_NOEXCEPT
: signal_(std::exchange(other.signal_, nullptr)), token_(std::exchange(other.token_, INVALID_SIGNAL_TOKEN)) {}
template<typename THandler>
SignalAutoToken(signal_t& signal, THandler&& handler, Oneshot oneshot = Oneshot::NO) MIJIN_NOEXCEPT
: signal_(&signal), token_(signal.connect(std::forward<THandler>(handler), oneshot))
{}
template<typename TObject>
SignalAutoToken(signal_t& signal, TObject& object, void (TObject::* handler)(TArgs...), Oneshot oneshot = Oneshot::NO) MIJIN_NOEXCEPT
: signal_(&signal), token_(signal.connect(object, handler, oneshot)) {}
template<typename TObject>
SignalAutoToken(signal_t& signal, TObject& object, void (TObject::* handler)(TArgs...) const, Oneshot oneshot = Oneshot::NO) MIJIN_NOEXCEPT
: signal_(&signal), token_(signal.connect(object, handler, oneshot)) {}
~SignalAutoToken() noexcept
{
reset();
}
SignalAutoToken& operator=(const SignalAutoToken&) = delete;
SignalAutoToken& operator=(SignalAutoToken&& other) MIJIN_NOEXCEPT
{
if (this != &other)
{
reset();
signal_ = std::exchange(other.signal_, nullptr);
token_ = std::exchange(other.token_, INVALID_SIGNAL_TOKEN);
}
return *this;
}
void reset(DisconnectSignal disconnect = DisconnectSignal::YES) MIJIN_NOEXCEPT;
friend signal_t;
};
//
// public functions
//
template<template<typename> typename TAllocator, typename... TArgs>
void SignalAutoToken<TAllocator, TArgs...>::reset(DisconnectSignal disconnect) MIJIN_NOEXCEPT
{
if (signal_ != nullptr && token_ != INVALID_SIGNAL_TOKEN)
{
if (disconnect) {
signal_->disconnect(token_);
}
signal_ = nullptr;
token_ = INVALID_SIGNAL_TOKEN;
}
}
template<template<typename> typename TAllocator, typename... TArgs>
template<typename THandler, typename TWeak>
inline auto BaseSignal<TAllocator, TArgs...>::connect(THandler handler, Oneshot oneshot, std::weak_ptr<TWeak> referenced) MIJIN_NOEXCEPT -> token_t

View File

@@ -41,9 +41,6 @@ concept RWMemoryViewable = MemoryViewable<T> && requires(T& object)
template<typename TConcrete>
class MixinMemoryView
{
private:
MixinMemoryView() = default;
public:
static constexpr bool WRITABLE = requires(TConcrete& object) { { object.data() } -> std::convertible_to<void*>; };
@@ -85,8 +82,6 @@ private:
{
return static_cast<const TConcrete*>(this)->byteSize();
}
friend TConcrete;
};
class MemoryView : public MixinMemoryView<MemoryView>

View File

@@ -168,12 +168,17 @@ std::string shellEscape(const std::string& arg) MIJIN_NOEXCEPT
return oss.str();
}
std::string makeShellCommand(const std::vector<std::string>& args) MIJIN_NOEXCEPT
std::string makeShellCommand(std::span<std::string_view> args) MIJIN_NOEXCEPT
{
(void) args;
MIJIN_ERROR("TBD");
return {};
#if 0
using namespace mijin::pipe;
return args
| Map(&shellEscape)
| Join(" ");
#endif
}
} // namespace mijin
#endif // MIJIN_TARGET_OS == MIJIN_OS_LINUX || MIJIN_TARGET_OS == MIJIN_OS_OSX

View File

@@ -3,7 +3,7 @@
#if !defined(MIJIN_IO_PROCESS_HPP_INCLUDED)
#define MIJIN_IO_PROCESS_HPP_INCLUDED 1
#include <vector>
#include <span>
#include "./stream.hpp"
#include "../internal/common.hpp"
@@ -22,7 +22,7 @@ public:
inline StreamError open(const std::string& command, FileOpenMode mode_) {
return open(command.c_str(), mode_);
}
inline StreamError open(const std::vector<std::string>& args, FileOpenMode mode_);
inline StreamError open(std::span<std::string_view> args, FileOpenMode mode_);
int close();
[[nodiscard]] inline bool isOpen() const { return handle != nullptr; }
@@ -36,13 +36,19 @@ public:
StreamFeatures getFeatures() override;
};
[[nodiscard]] std::string shellEscape(const std::string& arg) MIJIN_NOEXCEPT;
[[nodiscard]] std::string makeShellCommand(const std::vector<std::string>& args) MIJIN_NOEXCEPT;
[[nodiscard]] std::string shellEscape(std::string_view arg) MIJIN_NOEXCEPT;
[[nodiscard]] std::string makeShellCommand(std::span<std::string_view> args) MIJIN_NOEXCEPT;
StreamError ProcessStream::open(const std::vector<std::string>& args, FileOpenMode mode_)
StreamError ProcessStream::open(std::span<std::string_view> args, FileOpenMode mode_)
{
return open(makeShellCommand(args), mode_);
}
template<typename... TTypes>
std::array<std::string_view, sizeof...(TTypes)> makeSVList(TTypes&&... args)
{
return std::array{std::string_view(std::forward<TTypes>(args))...};
}
}
#endif // MIJIN_IO_PROCESS_HPP_INCLUDED

View File

@@ -1,376 +0,0 @@
#pragma once
#if !defined(MIJIN_LOGGING_BUFFER_SINK_HPP_INCLUDED)
#define MIJIN_LOGGING_BUFFER_SINK_HPP_INCLUDED 1
#include <array>
#include <cstdint>
#include <cstring>
#include <mutex>
#include <span>
#include "./logger.hpp"
namespace mijin
{
inline constexpr std::size_t BUFFER_SINK_DEFAULT_SIZE = 10 * 1024 * 1024; // default 10 MiB buffer
template<std::size_t VBufferSize = BUFFER_SINK_DEFAULT_SIZE, typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>>
class BaseBufferSink;
namespace impl
{
static constexpr std::uint32_t INVALID_BUFFER_INDEX = std::numeric_limits<std::uint32_t>::max();
template<typename TChar>
struct BufferedMessageHeader
{
const BaseLogChannel<TChar>* channel;
const BaseLogLevel<TChar>* 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<typename TChar, typename TTraits, std::size_t VBufferSize>
struct MessageBuffer
{
using char_t = TChar;
using traits_t = TTraits;
using message_t = BaseLogMessage<char_t, traits_t>;
using header_t = BufferedMessageHeader<TChar>;
static constexpr std::size_t BUFFER_SIZE = VBufferSize;
std::array<std::uint8_t, BUFFER_SIZE> bytes;
mutable std::mutex mutex;
[[nodiscard]]
header_t& messageAt(std::uint32_t idx) MIJIN_NOEXCEPT
{
return *reinterpret_cast<header_t*>(bytes.data() + idx);
}
[[nodiscard]]
std::span<char_t> messageText(header_t& header) MIJIN_NOEXCEPT
{
return {reinterpret_cast<char_t*>(reinterpret_cast<std::uint8_t*>(&header)) + sizeof(header_t), header.numChars};
}
[[nodiscard]]
const header_t& messageAt(std::uint32_t idx) const MIJIN_NOEXCEPT
{
return *reinterpret_cast<const header_t*>(bytes.data() + idx);
}
[[nodiscard]]
std::span<const char_t> messageText(const header_t& header) const MIJIN_NOEXCEPT
{
return {reinterpret_cast<const char_t*>(reinterpret_cast<const std::uint8_t*>(&header)) + sizeof(header_t), header.numChars};
}
static std::uint32_t messageBytes(std::uint32_t numChars) MIJIN_NOEXCEPT
{
return static_cast<std::uint32_t>(sizeof(header_t)) + (numChars * static_cast<std::uint32_t>(sizeof(char_t)));
}
};
template<typename TChar, typename TTraits, std::size_t VBufferSize>
class BufferSinkRange;
template<typename TChar, typename TTraits, std::size_t VBufferSize>
class LockedMessageBuffer
{
public:
using char_t = TChar;
using traits_t = TTraits;
static constexpr std::size_t BUFFER_SIZE = VBufferSize;
using range_t = impl::BufferSinkRange<char_t, traits_t, BUFFER_SIZE>;
private:
using buffer_t = MessageBuffer<char_t, traits_t, BUFFER_SIZE>;
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<std::size_t, typename, typename>
friend class mijin::BaseBufferSink;
};
template<typename TChar, typename TTraits, std::size_t VBufferSize>
class BufferSinkIterator
{
public:
using char_t = TChar;
using traits_t = TTraits;
using message_t = BaseLogMessage<char_t, traits_t>;
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<char_t, traits_t, BUFFER_SIZE>;
using header_t = BufferedMessageHeader<char_t>;
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<typename, typename, std::size_t>
friend class BufferSinkRange;
};
static_assert(std::forward_iterator<BufferSinkIterator<char, std::char_traits<char>, BUFFER_SINK_DEFAULT_SIZE>>);
template<typename TChar, typename TTraits, std::size_t VBufferSize>
class BufferSinkRange
{
public:
using char_t = TChar;
using traits_t = TTraits;
static constexpr std::size_t BUFFER_SIZE = VBufferSize;
using iterator = BufferSinkIterator<char_t, traits_t, BUFFER_SIZE>;
using const_iterator = iterator;
private:
using buffer_t = MessageBuffer<char_t, traits_t, BUFFER_SIZE>;
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<char_t, traits_t, BUFFER_SIZE>;
};
static_assert(std::bidirectional_iterator<BufferSinkIterator<char, std::char_traits<char>, 100>>);
static_assert(std::ranges::bidirectional_range<BufferSinkRange<char, std::char_traits<char>, 100>>);
template<typename TChar, typename TTraits, std::size_t VBufferSize>
auto LockedMessageBuffer<TChar, TTraits, VBufferSize>::getMessages() const MIJIN_NOEXCEPT -> range_t
{
return range_t(*buffer_, firstIdx_, lastIdx_);
}
}
using impl::LockedMessageBuffer;
template<std::size_t VBufferSize, typename TChar, typename TTraits>
class BaseBufferSink : public BaseLogSink<TChar, TTraits>
{
public:
using base_t = BaseLogSink<TChar>;
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<char_t, traits_t, BUFFER_SIZE>;
private:
using buffer_t = impl::MessageBuffer<char_t, traits_t, BUFFER_SIZE>;
using header_t = impl::BufferedMessageHeader<char_t>;
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<std::uint32_t>(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<std::uint32_t>(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)

View File

@@ -26,28 +26,27 @@ 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<allocator_t>)
: base_t(std::move(formatter), std::move(allocator)) {}
void handleMessageFormatted(const message_t&, const string_t& formatted) MIJIN_NOEXCEPT override
void handleMessageFormatted(const message_t&, const char_t* formatted) MIJIN_NOEXCEPT override
{
if constexpr (std::is_same_v<char_t, char>)
{
OutputDebugStringA(formatted.c_str());
OutputDebugStringA(formatted);
OutputDebugStringA("\n");
}
else if constexpr (std::is_same_v<char_t, wchar_t>)
{
OutputDebugStringW(formatted.c_str());
OutputDebugStringW(formatted);
OutputDebugStringW(L"\n");
}
else if constexpr (sizeof(char_t) == sizeof(char))
{
// char8_t etc.
OutputDebugStringA(std::bit_cast<const char*>(formatted.c_str()));
OutputDebugStringA(std::bit_cast<const char*>(formatted));
OutputDebugStringA("\n");
}
else

View File

@@ -8,11 +8,11 @@
namespace mijin
{
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>>
class BaseLevelFilter : public BaseLogFilter<TChar, TTraits>
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
class BaseLevelFilter : public BaseLogFilter<TChar>
{
public:
using base_t = BaseLogFilter<TChar, TTraits>;
using base_t = BaseLogFilter<TChar>;
using typename base_t::char_t;
using typename base_t::message_t;
private:

View File

@@ -64,15 +64,6 @@ MIJIN_DEFINE_CHAR_VERSIONS_TMPL(SimpleLogFormatter, FORMATTER_COMMON_ARGS, FORMA
#undef FORMATTER_COMMON_ARGS
#undef FORMATTER_SET_ARGS
template<typename TChar, typename TTraits, typename TAllocator, typename TFormatter = BaseSimpleLogFormatter<TChar, TTraits, TAllocator>>
DynamicPointer<TFormatter> makeLogFormatter(typename TFormatter::string_t&& format)
{
return makeDynamic<TFormatter>(std::move(format));
}
template<typename TChar, typename TTraits, typename TAllocator, typename TFormatter = BaseSimpleLogFormatter<TChar, TTraits, TAllocator>>
inline TFormatter DEFAULT_FORMATTER("[{level}] {text}");
#define MIJIN_FORMATTING_SINK_COMMON_ARGS(chr_type) \
typename TTraits = std::char_traits<chr_type>, \
template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR, \
@@ -87,36 +78,35 @@ inline TFormatter DEFAULT_FORMATTER("[{level}] {text}");
template<MIJIN_FORMATTING_SINK_TMPL_ARGS_INIT>
requires(allocator_type<TAllocator<TChar>>)
class BaseFormattingLogSink : public BaseLogSink<TChar, TTraits>
class BaseFormattingLogSink : public BaseLogSink<TChar>
{
public:
using base_t = BaseLogSink<TChar, TTraits>;
using base_t = BaseLogSink<TChar>;
using typename base_t::char_t;
using typename base_t::message_t;
using typename base_t::traits_t;
using char_t = TChar;
using traits_t = TTraits;
using allocator_t = TAllocator<TChar>;
using formatter_t = BaseLogFormatter<char_t, traits_t, allocator_t>;
using formatter_deleter_t = TDeleter;
using formatter_ptr_t = DynamicPointer<formatter_t, formatter_deleter_t>;
using string_t = formatter_t::string_t;
using typename base_t::message_t;
private:
formatter_ptr_t mFormatter;
string_t mBuffer;
public:
explicit BaseFormattingLogSink(formatter_ptr_t formatter = wrapDynamic(&DEFAULT_FORMATTER<char_t, traits_t, allocator_t>), allocator_t allocator = {})
explicit BaseFormattingLogSink(formatter_ptr_t formatter, allocator_t allocator = {})
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<allocator_t>)
: mFormatter(std::move(formatter)), mBuffer(std::move(allocator))
{
}
{}
virtual void handleMessageFormatted(const message_t& message, const string_t& formatted) MIJIN_NOEXCEPT = 0;
virtual void handleMessageFormatted(const message_t& message, const char_t* formatted) MIJIN_NOEXCEPT = 0;
void handleMessage(const message_t& message) noexcept override
{
mBuffer.clear();
mFormatter->format(message, mBuffer);
handleMessageFormatted(message, mBuffer);
handleMessageFormatted(message, mBuffer.c_str());
}
};
@@ -172,7 +162,7 @@ void BaseSimpleLogFormatter<TChar, TTraits, TAllocator>::format(const LogMessage
{
std::format_to(std::back_inserter(outFormatted), MIJIN_SMART_QUOTE(char_t, "{}"), value);
}
else if constexpr (is_string_v<type_t> || is_string_view_v<type_t> || is_cstring_v<type_t>)
else if constexpr (is_string_v<type_t> || is_cstring_v<type_t>)
{
convertStringType(value, outFormatted);
}

View File

@@ -9,7 +9,6 @@
#include <mutex>
#include <source_location>
#include <string>
#include <string_view>
#include <vector>
#include "../internal/common.hpp"
#include "../util/annot.hpp"
@@ -79,14 +78,12 @@ struct BaseLogChannel
MIJIN_DEFINE_CHAR_VERSIONS(LogChannel)
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>>
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
struct BaseLogMessage
{
using char_t = TChar;
using traits_t = TTraits;
using string_view_t = std::basic_string_view<char_t, traits_t>;
string_view_t text;
const char_t* text;
const BaseLogChannel<char_t>* channel;
const BaseLogLevel<char_t>* level;
std::source_location sourceLocation;
@@ -94,13 +91,12 @@ struct BaseLogMessage
MIJIN_DEFINE_CHAR_VERSIONS(LogMessage)
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>>
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
class BaseLogSink
{
public:
using char_t = TChar;
using traits_t = TTraits;
using message_t = BaseLogMessage<char_t, traits_t>;
using message_t = BaseLogMessage<char_t>;
virtual ~BaseLogSink() noexcept = default;
@@ -109,13 +105,12 @@ public:
MIJIN_DEFINE_CHAR_VERSIONS(LogSink)
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>>
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
class BaseLogFilter
{
public:
using char_t = TChar;
using traits_t = TTraits;
using message_t = BaseLogMessage<char_t, traits_t>;
using message_t = BaseLogMessage<char_t>;
virtual ~BaseLogFilter() noexcept = default;
@@ -133,13 +128,12 @@ public:
using traits_t = TTraits;
using allocator_t = TAllocator<char_t>;
using sink_t = BaseLogSink<char_t, traits_t>;
using filter_t = BaseLogFilter<char_t, traits_t>;
using sink_t = BaseLogSink<char_t>;
using filter_t = BaseLogFilter<char_t>;
using level_t = BaseLogLevel<char_t>;
using channel_t = BaseLogChannel<char_t>;
using message_t = BaseLogMessage<char_t, traits_t>;
using message_t = BaseLogMessage<char_t>;
using string_t = std::basic_string<char_t, traits_t, allocator_t>;
using string_view_t = std::basic_string_view<char_t, traits_t>;
private:
struct SinkEntry
{
@@ -152,10 +146,13 @@ public:
explicit BaseLogger(TAllocator<void> allocator = {}) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<TAllocator<SinkEntry>, TAllocator<void>&&>))
: mSinks(TAllocator<SinkEntry>(std::move(allocator)))
{}
BaseLogger(const BaseLogger&) = default;
BaseLogger(BaseLogger&&) = default;
BaseLogger& operator=(const BaseLogger&) = default;
BaseLogger& operator=(BaseLogger&&) = default;
void addSink(sink_t& sink)
@@ -170,19 +167,6 @@ 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);
@@ -195,7 +179,7 @@ public:
}
}
void logUnformatted(const level_t& level, const channel_t& channel, std::source_location sourceLocation, string_view_t msg) const MIJIN_NOEXCEPT
void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation, const char_t* msg) const MIJIN_NOEXCEPT
{
postMessage({
.text = msg,
@@ -205,11 +189,6 @@ public:
});
}
void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation, std::basic_format_string<char_t> msg) const MIJIN_NOEXCEPT
{
logUnformatted(level, channel, sourceLocation, msg.get());
}
template<typename... TArgs>
void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation,
std::basic_format_string<char_t, std::type_identity_t<TArgs>...> fmt, TArgs&& ... args) const
@@ -217,7 +196,7 @@ public:
{
string_t buffer(allocator_t(mSinks.get_allocator()));
std::format_to(std::back_inserter(buffer), fmt, std::forward<TArgs>(args)...);
logUnformatted(level, channel, std::move(sourceLocation), buffer);
log(level, channel, std::move(sourceLocation), buffer.c_str());
}
};
@@ -275,9 +254,6 @@ 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__)
@@ -286,14 +262,6 @@ 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 \
{ \

View File

@@ -9,9 +9,6 @@
namespace mijin
{
template<typename TChar, typename TTraits, typename TAllocator, typename TFormatter = BaseSimpleLogFormatter<TChar, TTraits, TAllocator>>
inline TFormatter DEFAULT_STDIO_FORMATTER("[{ansi:level_color}{level}{ansi:reset}] {text} <{file}:{line}>");
template<MIJIN_FORMATTING_SINK_TMPL_ARGS_INIT>
requires(allocator_type<TAllocator<TChar>>)
class BaseStdioSink : public BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG_NAMES>
@@ -19,15 +16,13 @@ class BaseStdioSink : public BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG
public:
using base_t = BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG_NAMES>;
using typename base_t::char_t;
using typename base_t::traits_t;
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:
explicit BaseStdioSink(formatter_ptr_t formatter = wrapDynamic(&DEFAULT_STDIO_FORMATTER<char_t, traits_t, allocator_t>), allocator_t allocator = {})
explicit BaseStdioSink(formatter_ptr_t formatter, allocator_t allocator = {})
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<allocator_t>)
: base_t(std::move(formatter), std::move(allocator)) {}
@@ -38,30 +33,30 @@ public:
void setMinStderrLevel(const BaseLogLevel<char_t>& level) MIJIN_NOEXCEPT { mMinStderrLevel = level.value; }
void handleMessageFormatted(const message_t& message, const string_t& formatted) MIJIN_NOEXCEPT override
void handleMessageFormatted(const message_t& message, const char_t* formatted) MIJIN_NOEXCEPT override
{
FILE* stream = (message.level->value >= mMinStderrLevel) ? stderr : stdout;
if constexpr (std::is_same_v<char_t, char>)
{
(void) std::fputs(formatted.c_str(), stream);
(void) std::fputc('\n', stream);
std::fputs(formatted, stream);
std::fputc('\n', stream);
}
else if constexpr (std::is_same_v<char_t, wchar_t>)
{
(void) std::fputws(formatted.c_str(), stream);
(void) std::fputwc(L'\n', stream);
std::fputws(formatted, stream);
std::fputwc(L'\n', stream);
}
else if constexpr (sizeof(char_t) == sizeof(char))
{
// char8_t etc.
(void) std::fputs(std::bit_cast<const char*>(formatted.c_str()), stream);
(void) std::fputc('\n', stream);
std::fputs(std::bit_cast<const char*>(formatted), stream);
std::fputc('\n', stream);
}
else
{
static_assert(always_false_v<char_t>, "Character type not supported.");
}
(void) std::fflush(stream);
std::fflush(stream);
}
};

View File

@@ -6,6 +6,7 @@
#include "./formatting.hpp"
#include "../io/stream.hpp"
#include "../util/traits.hpp"
namespace mijin
{
@@ -19,7 +20,6 @@ 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<Stream>;
private:
stream_ptr_t mStream;
@@ -36,12 +36,12 @@ public:
mStream = std::move(stream).release();
}
void handleMessageFormatted(const message_t& /* message */, const string_t& formatted) MIJIN_NOEXCEPT override
void handleMessageFormatted(const message_t& /* message */, const char_t* formatted) MIJIN_NOEXCEPT override
{
if (!mStream) {
return;
}
(void) mStream->writeSpan(formatted);
(void) mStream->writeSpan(std::basic_string_view(formatted));
(void) mStream->write('\n');
mStream->flush();
}

View File

@@ -864,12 +864,6 @@ inline auto findIgnoreCase(std::string_view haystack, std::string_view needle)
return std::ranges::search(haystack, needle, &compareIgnoreCase<char>);
}
[[nodiscard]]
inline bool containsIgnoreCase(std::string_view haystack, std::string_view needle)
{
return findIgnoreCase(haystack, needle).begin() != haystack.end();
}
[[nodiscard]]
inline bool startsWithIgnoreCase(std::string_view string, std::string_view part)
{

View File

@@ -300,19 +300,6 @@ static_assert(union_type<MyTemplate<int, int>, MyTemplate<double, double>, MyTem
static_assert(!union_type<int*, int>);
static_assert(union_type<int*, std::is_pointer<Type_>>);
static_assert(!union_type<int, std::is_pointer<Type_>>);
struct DetectNo {};
struct DetectYes {
using some_type = double;
};
template<typename T>
using detect_some_type = typename T::some_type;
template<typename T>
using some_type = detect_or_t<int, detect_some_type, T>;
static_assert(std::is_same_v<some_type<DetectNo>, int>);
static_assert(std::is_same_v<some_type<DetectYes>, double>);
}
#endif