Massively overengineered logger to support different character types.
This commit is contained in:
parent
061c58ef41
commit
b91eb34789
@ -7,6 +7,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "./boxed_object.hpp"
|
#include "./boxed_object.hpp"
|
||||||
#include "./optional.hpp"
|
#include "./optional.hpp"
|
||||||
|
|
||||||
@ -102,7 +103,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename TKey, typename TValue, typename TKeyAllocator = std::allocator<TKey>, typename TValueAllocator = std::allocator<TValue>>
|
template<typename TKey, typename TValue, typename TKeyAllocator = MIJIN_DEFAULT_ALLOCATOR<TKey>, typename TValueAllocator = MIJIN_DEFAULT_ALLOCATOR<TValue>>
|
||||||
class VectorMap
|
class VectorMap
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -119,12 +120,17 @@ private:
|
|||||||
std::vector<TKey, TKeyAllocator> keys_;
|
std::vector<TKey, TKeyAllocator> keys_;
|
||||||
std::vector<TValue, TValueAllocator> values_;
|
std::vector<TValue, TValueAllocator> values_;
|
||||||
public:
|
public:
|
||||||
VectorMap() noexcept = default;
|
explicit VectorMap(TKeyAllocator keyAllocator = {})
|
||||||
|
MIJIN_NOEXCEPT_IF((std::is_nothrow_move_constructible_v<TKeyAllocator> && std::is_nothrow_constructible_v<TValueAllocator, const TKeyAllocator&>))
|
||||||
|
: keys_(std::move(keyAllocator)), values_(TValueAllocator(keys_.get_allocator())) {}
|
||||||
|
VectorMap(TKeyAllocator keyAllocator, TValueAllocator valueAllocator)
|
||||||
|
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TKeyAllocator> && std::is_nothrow_move_constructible_v<TValueAllocator>)
|
||||||
|
: keys_(std::move(keyAllocator)), values_(std::move(valueAllocator)) {}
|
||||||
VectorMap(const VectorMap&) = default;
|
VectorMap(const VectorMap&) = default;
|
||||||
VectorMap(VectorMap&&) MIJIN_NOEXCEPT = default;
|
VectorMap(VectorMap&&) = default;
|
||||||
|
|
||||||
VectorMap& operator=(const VectorMap&) = default;
|
VectorMap& operator=(const VectorMap&) = default;
|
||||||
VectorMap& operator=(VectorMap&&) MIJIN_NOEXCEPT = default;
|
VectorMap& operator=(VectorMap&&) = default;
|
||||||
auto operator<=>(const VectorMap& other) const noexcept = default;
|
auto operator<=>(const VectorMap& other) const noexcept = default;
|
||||||
|
|
||||||
TValue& operator[](const TKey& key)
|
TValue& operator[](const TKey& key)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "./config.hpp"
|
||||||
|
#include "./helpers.hpp"
|
||||||
#include "./exception.hpp"
|
#include "./exception.hpp"
|
||||||
#include "./version_support.hpp"
|
#include "./version_support.hpp"
|
||||||
|
19
source/mijin/internal/config.hpp
Normal file
19
source/mijin/internal/config.hpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !defined(MIJIN_INTERNAL_CONFIG_HPP_INCLUDED)
|
||||||
|
#define MIJIN_INTERNAL_CONFIG_HPP_INCLUDED 1
|
||||||
|
|
||||||
|
#if defined(MIJIN_CONFIG_HEADER)
|
||||||
|
#include MIJIN_CONFIG_HEADER
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MIJIN_DEFAULT_ALLOCATOR)
|
||||||
|
#define MIJIN_DEFAULT_ALLOCATOR std::allocator
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MIJIN_DEFAULT_CHAR_TYPE)
|
||||||
|
#define MIJIN_DEFAULT_CHAR_TYPE char
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // !defined(MIJIN_INTERNAL_CONFIG_HPP_INCLUDED)
|
57
source/mijin/internal/helpers.hpp
Normal file
57
source/mijin/internal/helpers.hpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !defined(MIJIN_INTERNAL_HELPERS_HPP_INCLUDED)
|
||||||
|
#define MIJIN_INTERNAL_HELPERS_HPP_INCLUDED 1
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include "../util/traits.hpp"
|
||||||
|
|
||||||
|
#define MIJIN_IDENTITY(what) what
|
||||||
|
#define MIJIN_NULLIFY(what)
|
||||||
|
|
||||||
|
#define MIJIN_SMART_QUOTE(chr_type, text) \
|
||||||
|
[]<typename TChar__>(TChar__) consteval \
|
||||||
|
{ \
|
||||||
|
if constexpr (std::is_same_v<TChar__, char>) \
|
||||||
|
{ \
|
||||||
|
return text; \
|
||||||
|
} \
|
||||||
|
else if constexpr (std::is_same_v<TChar__, wchar_t>) \
|
||||||
|
{ \
|
||||||
|
return L ## text; \
|
||||||
|
} \
|
||||||
|
else if constexpr (std::is_same_v<TChar__, char8_t>) \
|
||||||
|
{ \
|
||||||
|
return u8 ## text; \
|
||||||
|
} \
|
||||||
|
else \
|
||||||
|
{ \
|
||||||
|
static_assert(::mijin::always_false_v<TChar__>, "Invalid char type."); \
|
||||||
|
} \
|
||||||
|
}(chr_type())
|
||||||
|
|
||||||
|
#define MIJIN_SMART_STRINGIFY(chr_type, text) MIJIN_SMART_QUOTE(chr_type, #text)
|
||||||
|
|
||||||
|
#define MIJIN_DEFINE_CHAR_VERSIONS_IMPL(type_name, prefix_a, prefix_b, prefix_c, set_args) \
|
||||||
|
prefix_a prefix_b(char) prefix_c \
|
||||||
|
using C ## type_name = Base ## type_name <set_args(char)>; \
|
||||||
|
\
|
||||||
|
prefix_a prefix_b(wchar_t) prefix_c \
|
||||||
|
using W ## type_name = Base ## type_name <set_args(wchar_t)>; \
|
||||||
|
\
|
||||||
|
prefix_a prefix_b(char8_t) prefix_c \
|
||||||
|
using U ## type_name = Base ## type_name <set_args(char8_t)>; \
|
||||||
|
\
|
||||||
|
using type_name = Base ## type_name<>;
|
||||||
|
|
||||||
|
#define MIJIN_DEFINE_CHAR_VERSIONS_TMPL(type_name, remaining_args, set_args) \
|
||||||
|
MIJIN_DEFINE_CHAR_VERSIONS_IMPL(type_name, template<, remaining_args, >, set_args)
|
||||||
|
|
||||||
|
#define MIJIN_DEFINE_CHAR_VERSIONS_CUSTOM(type_name, set_args) \
|
||||||
|
MIJIN_DEFINE_CHAR_VERSIONS_IMPL(type_name, , MIJIN_NULLIFY, , set_args)
|
||||||
|
|
||||||
|
#define MIJIN_DEFINE_CHAR_VERSIONS(type_name) \
|
||||||
|
MIJIN_DEFINE_CHAR_VERSIONS_CUSTOM(type_name, MIJIN_IDENTITY)
|
||||||
|
|
||||||
|
#endif // !defined(MIJIN_INTERNAL_HELPERS_HPP_INCLUDED)
|
@ -8,61 +8,98 @@
|
|||||||
#include <format>
|
#include <format>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include "./logger.hpp"
|
#include "./logger.hpp"
|
||||||
|
#include "../internal/common.hpp"
|
||||||
#include "../memory/dynamic_pointer.hpp"
|
#include "../memory/dynamic_pointer.hpp"
|
||||||
#include "../memory/memutil.hpp"
|
#include "../memory/memutil.hpp"
|
||||||
#include "../memory/virtual_allocator.hpp"
|
#include "../memory/virtual_allocator.hpp"
|
||||||
#include "../util/annot.hpp"
|
#include "../util/annot.hpp"
|
||||||
#include "../util/concepts.hpp"
|
#include "../util/concepts.hpp"
|
||||||
|
#include "../util/string.hpp"
|
||||||
|
|
||||||
namespace mijin
|
namespace mijin
|
||||||
{
|
{
|
||||||
template<allocator_type_for<char> TAllocator = std::allocator<char>>
|
#define FORMATTER_COMMON_ARGS(chr_type) allocator_type_for<chr_type> TAllocator = MIJIN_DEFAULT_ALLOCATOR<chr_type>
|
||||||
class LogFormatter
|
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>,
|
||||||
|
FORMATTER_COMMON_ARGS(TChar)>
|
||||||
|
class BaseLogFormatter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using string_t = std::basic_string<char, std::char_traits<char>, TAllocator>;
|
using char_t = TChar;
|
||||||
|
using traits_t = TTraits;
|
||||||
|
using allocator_t = TAllocator;
|
||||||
|
using string_t = std::basic_string<char_t, traits_t, allocator_t>;
|
||||||
|
|
||||||
virtual ~LogFormatter() noexcept = default;
|
virtual ~BaseLogFormatter() noexcept = default;
|
||||||
|
|
||||||
virtual void format(const LogMessage& message, string_t& outFormatted) noexcept = 0;
|
virtual void format(const LogMessage& message, string_t& outFormatted) noexcept = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<allocator_type_for<char> TAllocator = std::allocator<char>>
|
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>,
|
||||||
class SimpleLogFormatter : public LogFormatter<TAllocator>
|
FORMATTER_COMMON_ARGS(TChar)>
|
||||||
|
class BaseSimpleLogFormatter : public BaseLogFormatter<TChar, TTraits, TAllocator>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using typename LogFormatter<TAllocator>::string_t;
|
using char_t = TChar;
|
||||||
|
using traits_t = TTraits;
|
||||||
|
using allocator_t = TAllocator;
|
||||||
|
using base_t = BaseLogFormatter<char_t, traits_t, allocator_t>;
|
||||||
|
using typename base_t::string_t;
|
||||||
private:
|
private:
|
||||||
string_t mFormat;
|
string_t mFormat;
|
||||||
|
string_t mFormatBuffer;
|
||||||
public:
|
public:
|
||||||
explicit SimpleLogFormatter(string_t format) MIJIN_NOEXCEPT : mFormat(std::move(format)) {}
|
explicit BaseSimpleLogFormatter(string_t format) MIJIN_NOEXCEPT : mFormat(std::move(format)), mFormatBuffer(mFormat.get_allocator()) {}
|
||||||
|
|
||||||
void format(const LogMessage& message, string_t& outFormatted) noexcept override;
|
void format(const LogMessage& message, string_t& outFormatted) noexcept override;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<template<typename> typename TAllocator = std::allocator,
|
#define FORMATTER_SET_ARGS(chr_type) chr_type, std::char_traits<chr_type>, TAllocator
|
||||||
deleter_type<LogFormatter<TAllocator<char>>> TDeleter = AllocatorDeleter<TAllocator<LogFormatter<TAllocator<char>>>>>
|
|
||||||
requires(allocator_type<TAllocator<char>>)
|
MIJIN_DEFINE_CHAR_VERSIONS_TMPL(LogFormatter, FORMATTER_COMMON_ARGS, FORMATTER_SET_ARGS)
|
||||||
class FormattingLogSink : public LogSink
|
MIJIN_DEFINE_CHAR_VERSIONS_TMPL(SimpleLogFormatter, FORMATTER_COMMON_ARGS, FORMATTER_SET_ARGS)
|
||||||
|
|
||||||
|
#undef FORMATTER_COMMON_ARGS
|
||||||
|
#undef FORMATTER_SET_ARGS
|
||||||
|
|
||||||
|
#define MIJIN_FORMATTING_SINK_COMMON_ARGS(chr_type) \
|
||||||
|
typename TTraits = std::char_traits<chr_type>, \
|
||||||
|
template<typename> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR, \
|
||||||
|
deleter_type<BaseLogFormatter<chr_type, TTraits, TAllocator<chr_type>>> TDeleter \
|
||||||
|
= AllocatorDeleter<TAllocator<BaseLogFormatter<chr_type, TTraits, TAllocator<chr_type>>>>
|
||||||
|
|
||||||
|
#define MIJIN_FORMATTING_SINK_TMPL_ARGS_INIT \
|
||||||
|
typename TChar = MIJIN_DEFAULT_CHAR_TYPE, \
|
||||||
|
MIJIN_FORMATTING_SINK_COMMON_ARGS(TChar)
|
||||||
|
|
||||||
|
#define MIJIN_FORMATTING_SINK_TMP_ARG_NAMES TChar, TTraits, TAllocator, TDeleter
|
||||||
|
|
||||||
|
template<MIJIN_FORMATTING_SINK_TMPL_ARGS_INIT>
|
||||||
|
requires(allocator_type<TAllocator<TChar>>)
|
||||||
|
class BaseFormattingLogSink : public BaseLogSink<TChar>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using allocator_t = TAllocator<char>;
|
using base_t = BaseLogSink<TChar>;
|
||||||
using formatter_t = LogFormatter<allocator_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_deleter_t = TDeleter;
|
||||||
using formatter_ptr_t = DynamicPointer<formatter_t, formatter_deleter_t>;
|
using formatter_ptr_t = DynamicPointer<formatter_t, formatter_deleter_t>;
|
||||||
using string_t = formatter_t::string_t;
|
using string_t = formatter_t::string_t;
|
||||||
|
using typename base_t::message_t;
|
||||||
private:
|
private:
|
||||||
not_null_t<formatter_ptr_t> mFormatter;
|
not_null_t<formatter_ptr_t> mFormatter;
|
||||||
string_t mBuffer;
|
string_t mBuffer;
|
||||||
public:
|
public:
|
||||||
explicit FormattingLogSink(not_null_t<formatter_ptr_t> formatter, TAllocator<char> allocator = {})
|
explicit BaseFormattingLogSink(not_null_t<formatter_ptr_t> formatter, allocator_t allocator = {})
|
||||||
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TAllocator<char>>)
|
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<allocator_t>)
|
||||||
: mFormatter(std::move(formatter)), mBuffer(std::move(allocator))
|
: mFormatter(std::move(formatter)), mBuffer(std::move(allocator))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual void handleMessageFormatted(const LogMessage& message, const char* formatted) MIJIN_NOEXCEPT = 0;
|
virtual void handleMessageFormatted(const message_t& message, const char_t* formatted) MIJIN_NOEXCEPT = 0;
|
||||||
|
|
||||||
void handleMessage(const LogMessage& message) noexcept override
|
void handleMessage(const message_t& message) noexcept override
|
||||||
{
|
{
|
||||||
mBuffer.clear();
|
mBuffer.clear();
|
||||||
mFormatter->format(message, mBuffer);
|
mFormatter->format(message, mBuffer);
|
||||||
@ -70,33 +107,42 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<allocator_type_for<char> TAllocator>
|
#define SINK_SET_ARGS(chr_type) chr_type, std::char_traits<chr_type>, TAllocator, TDeleter
|
||||||
void SimpleLogFormatter<TAllocator>::format(const LogMessage& message, string_t& outFormatted) noexcept
|
|
||||||
|
MIJIN_DEFINE_CHAR_VERSIONS_TMPL(FormattingLogSink, MIJIN_FORMATTING_SINK_COMMON_ARGS, SINK_SET_ARGS)
|
||||||
|
|
||||||
|
#undef SINK_SET_ARGS
|
||||||
|
|
||||||
|
template<typename TChar, typename TTraits, allocator_type_for<TChar> TAllocator>
|
||||||
|
void BaseSimpleLogFormatter<TChar, TTraits, TAllocator>::format(const LogMessage& message, string_t& outFormatted) noexcept
|
||||||
{
|
{
|
||||||
|
using string_view_t = std::basic_string_view<char_t, traits_t>;
|
||||||
|
|
||||||
|
mFormatBuffer.clear();
|
||||||
for (auto pos = mFormat.begin(); pos != mFormat.end(); ++pos)
|
for (auto pos = mFormat.begin(); pos != mFormat.end(); ++pos)
|
||||||
{
|
{
|
||||||
if (*pos == '{')
|
if (*pos == MIJIN_SMART_QUOTE(char_t, '{'))
|
||||||
{
|
{
|
||||||
++pos;
|
++pos;
|
||||||
if (*pos == '{')
|
if (*pos == MIJIN_SMART_QUOTE(char_t, '{'))
|
||||||
{
|
{
|
||||||
// double {
|
// double {
|
||||||
outFormatted += '{';
|
outFormatted += MIJIN_SMART_QUOTE(char_t, '{');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto argStart = pos;
|
const auto argStart = pos;
|
||||||
static const std::string_view endChars = ":}";
|
static const string_view_t endChars = MIJIN_SMART_QUOTE(char_t, ":}");
|
||||||
pos = std::find_first_of(pos, mFormat.end(), endChars.begin(), endChars.end());
|
pos = std::find_first_of(pos, mFormat.end(), endChars.begin(), endChars.end());
|
||||||
MIJIN_ASSERT(pos != mFormat.end(), "Invalid format.");
|
MIJIN_ASSERT(pos != mFormat.end(), "Invalid format.");
|
||||||
|
|
||||||
const std::string_view argName(argStart, pos);
|
const string_view_t argName(argStart, pos);
|
||||||
std::string argFormat;
|
string_view_t argFormat;
|
||||||
if (*pos == ':')
|
if (*pos == ':')
|
||||||
{
|
{
|
||||||
const auto formatStart = pos;
|
const auto formatStart = pos;
|
||||||
pos = std::find(pos, mFormat.end(), '}');
|
pos = std::find(pos, mFormat.end(), MIJIN_SMART_QUOTE(char_t, '}'));
|
||||||
MIJIN_ASSERT(pos != mFormat.end(), "Invalid format.");
|
MIJIN_ASSERT(pos != mFormat.end(), "Invalid format.");
|
||||||
argFormat = std::string_view(formatStart, pos);
|
argFormat = string_view_t(formatStart, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// small utility that uses the provided string buffer for storing the format string
|
// small utility that uses the provided string buffer for storing the format string
|
||||||
@ -107,56 +153,102 @@ void SimpleLogFormatter<TAllocator>::format(const LogMessage& message, string_t&
|
|||||||
// if there is no format, just directly print the value
|
// if there is no format, just directly print the value
|
||||||
if (argFormat.empty())
|
if (argFormat.empty())
|
||||||
{
|
{
|
||||||
if constexpr (std::is_arithmetic_v<type_t>)
|
if constexpr (is_char_v<type_t>)
|
||||||
{
|
{
|
||||||
std::format_to(std::back_inserter(outFormatted), "{}", value);
|
convertStringType(string_view_t(&value, 1), outFormatted);
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_arithmetic_v<type_t>)
|
||||||
|
{
|
||||||
|
std::format_to(std::back_inserter(outFormatted), MIJIN_SMART_QUOTE(char_t, "{}"), value);
|
||||||
|
}
|
||||||
|
else if constexpr (is_string_v<type_t> || is_cstring_v<type_t>)
|
||||||
|
{
|
||||||
|
convertStringType(value, outFormatted);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
static_assert(always_false_v<type_t>);
|
||||||
outFormatted += value;
|
outFormatted += value;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// first copy the format string + braces into the buffer
|
// first copy the format string + braces into the buffer
|
||||||
const auto formatStart = outFormatted.size();
|
const auto formatStart = mFormatBuffer.size();
|
||||||
outFormatted += '{';
|
mFormatBuffer += '{';
|
||||||
outFormatted += argFormat;
|
mFormatBuffer += argFormat;
|
||||||
outFormatted += '}';
|
mFormatBuffer += '}';
|
||||||
const auto formatEnd = outFormatted.size();
|
const auto formatEnd = mFormatBuffer.size();
|
||||||
auto format = std::string_view(outFormatted).substr(formatStart, formatEnd - formatStart);
|
|
||||||
|
|
||||||
// then append the formatted text
|
auto doFormatTo = [](string_t& string, string_view_t format, const auto& value)
|
||||||
std::vformat_to(std::back_inserter(outFormatted), format, std::make_format_args(value));
|
{
|
||||||
|
if constexpr (std::is_same_v<char_t, char>)
|
||||||
// and then remove the format from the buffer again
|
{
|
||||||
outFormatted.erase(formatStart, formatEnd - formatStart);
|
std::vformat_to(std::back_inserter(string), format, std::make_format_args(value));
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_same_v<char_t, wchar_t>)
|
||||||
|
{
|
||||||
|
std::vformat_to(std::back_inserter(string), format, std::make_wformat_args(value));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static_assert(always_false_v<char_t>, "Cannot format this char type.");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (argName == "text")
|
auto doFormat = [&](const auto& value)
|
||||||
|
{
|
||||||
|
auto format = string_view_t(mFormatBuffer).substr(formatStart, formatEnd - formatStart);
|
||||||
|
doFormatTo(outFormatted, format, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
if constexpr (is_char_v<type_t> && !std::is_same_v<char_t, type_t>)
|
||||||
|
{
|
||||||
|
static_assert(always_false_v<type_t>, "TODO...");
|
||||||
|
}
|
||||||
|
else if constexpr ((is_string_v<type_t> || is_cstring_v<type_t>) && !std::is_same_v<str_char_type_t<type_t>, char_t>)
|
||||||
|
{
|
||||||
|
// different string type, needs to be converted
|
||||||
|
const auto convertedStart = mFormatBuffer.size();
|
||||||
|
convertStringType(value, mFormatBuffer);
|
||||||
|
const auto convertedEnd = mFormatBuffer.size();
|
||||||
|
|
||||||
|
// then we can format it
|
||||||
|
auto converted = string_view_t(mFormatBuffer).substr(convertedStart, mFormatBuffer.size() - convertedEnd);
|
||||||
|
doFormat(converted);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// nothing special
|
||||||
|
doFormat(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (argName == MIJIN_SMART_QUOTE(char_t, "text"))
|
||||||
{
|
{
|
||||||
formatInline(message.text);
|
formatInline(message.text);
|
||||||
}
|
}
|
||||||
else if (argName == "channel")
|
else if (argName == MIJIN_SMART_QUOTE(char_t, "channel"))
|
||||||
{
|
{
|
||||||
formatInline(message.channel->name);
|
formatInline(message.channel->name);
|
||||||
}
|
}
|
||||||
else if (argName == "file")
|
else if (argName == MIJIN_SMART_QUOTE(char_t, "file"))
|
||||||
{
|
{
|
||||||
formatInline(message.sourceLocation.file_name());
|
formatInline(message.sourceLocation.file_name());
|
||||||
}
|
}
|
||||||
else if (argName == "function")
|
else if (argName == MIJIN_SMART_QUOTE(char_t, "function"))
|
||||||
{
|
{
|
||||||
formatInline(message.sourceLocation.function_name());
|
formatInline(message.sourceLocation.function_name());
|
||||||
}
|
}
|
||||||
else if (argName == "line")
|
else if (argName == MIJIN_SMART_QUOTE(char_t, "line"))
|
||||||
{
|
{
|
||||||
formatInline(message.sourceLocation.line());
|
formatInline(message.sourceLocation.line());
|
||||||
}
|
}
|
||||||
else if (argName == "column")
|
else if (argName == MIJIN_SMART_QUOTE(char_t, "column"))
|
||||||
{
|
{
|
||||||
formatInline(message.sourceLocation.column());
|
formatInline(message.sourceLocation.column());
|
||||||
}
|
}
|
||||||
else if (argName == "level")
|
else if (argName == MIJIN_SMART_QUOTE(char_t, "level"))
|
||||||
{
|
{
|
||||||
formatInline(message.level->name);
|
formatInline(message.level->name);
|
||||||
}
|
}
|
||||||
|
@ -14,69 +14,139 @@
|
|||||||
|
|
||||||
namespace mijin
|
namespace mijin
|
||||||
{
|
{
|
||||||
struct LogLevel
|
|
||||||
|
#if !defined(MIJIN_LOG_LEVEL_VALUE_DEBUG)
|
||||||
|
#define MIJIN_LOG_LEVEL_VALUE_DEBUG -1000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MIJIN_LOG_LEVEL_VALUE_VERBOSE)
|
||||||
|
#define MIJIN_LOG_LEVEL_VALUE_VERBOSE -500
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MIJIN_LOG_LEVEL_VALUE_INFO)
|
||||||
|
#define MIJIN_LOG_LEVEL_VALUE_INFO 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MIJIN_LOG_LEVEL_VALUE_WARNING)
|
||||||
|
#define MIJIN_LOG_LEVEL_VALUE_WARNING 500
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MIJIN_LOG_LEVEL_VALUE_ERROR)
|
||||||
|
#define MIJIN_LOG_LEVEL_VALUE_ERROR 1000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MIJIN_FUNCNAME_GET_LOGGER)
|
||||||
|
#define MIJIN_FUNCNAME_GET_LOGGER mijin__getLogger__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE)
|
||||||
|
#define MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE mijin__getMinLogLevelCompile
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MIJIN_NSNAME_LOG_LEVEL)
|
||||||
|
#define MIJIN_NSNAME_LOG_LEVEL mijin_log_level
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(MIJIN_NSNAME_LOG_CHANNEL)
|
||||||
|
#define MIJIN_NSNAME_LOG_CHANNEL mijin_log_channel
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
|
||||||
|
struct BaseLogLevel
|
||||||
{
|
{
|
||||||
const char* name;
|
using char_t = TChar;
|
||||||
|
|
||||||
|
const char_t* name;
|
||||||
int value;
|
int value;
|
||||||
|
|
||||||
explicit operator int() const MIJIN_NOEXCEPT { return value; }
|
explicit operator int() const MIJIN_NOEXCEPT { return value; }
|
||||||
|
|
||||||
auto operator<=>(const LogLevel& other) const MIJIN_NOEXCEPT { return value <=> other.value; }
|
auto operator<=>(const BaseLogLevel& other) const MIJIN_NOEXCEPT { return value <=> other.value; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LogChannel
|
MIJIN_DEFINE_CHAR_VERSIONS(LogLevel)
|
||||||
|
|
||||||
|
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
|
||||||
|
struct BaseLogChannel
|
||||||
{
|
{
|
||||||
const char* name;
|
using char_t = TChar;
|
||||||
|
|
||||||
|
const char_t* name;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LogMessage
|
MIJIN_DEFINE_CHAR_VERSIONS(LogChannel)
|
||||||
|
|
||||||
|
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
|
||||||
|
struct BaseLogMessage
|
||||||
{
|
{
|
||||||
const char* text;
|
using char_t = TChar;
|
||||||
const LogChannel* channel;
|
|
||||||
const LogLevel* level;
|
const char_t* text;
|
||||||
|
const BaseLogChannel<char_t>* channel;
|
||||||
|
const BaseLogLevel<char_t>* level;
|
||||||
std::source_location sourceLocation;
|
std::source_location sourceLocation;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LogSink
|
MIJIN_DEFINE_CHAR_VERSIONS(LogMessage)
|
||||||
|
|
||||||
|
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE>
|
||||||
|
class BaseLogSink
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~LogSink() noexcept = default;
|
using char_t = TChar;
|
||||||
|
using message_t = BaseLogMessage<char_t>;
|
||||||
|
|
||||||
virtual void handleMessage(const LogMessage& message) MIJIN_NOEXCEPT = 0;
|
virtual ~BaseLogSink() noexcept = default;
|
||||||
|
|
||||||
|
virtual void handleMessage(const message_t& message) MIJIN_NOEXCEPT = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<template<typename T> typename TAllocator = std::allocator>
|
MIJIN_DEFINE_CHAR_VERSIONS(LogSink)
|
||||||
class Logger
|
|
||||||
|
#define LOGGER_COMMON_ARGS(chr_type) template<typename T> typename TAllocator = MIJIN_DEFAULT_ALLOCATOR
|
||||||
|
template<typename TChar = MIJIN_DEFAULT_CHAR_TYPE, typename TTraits = std::char_traits<TChar>, LOGGER_COMMON_ARGS(TChar)>
|
||||||
|
class BaseLogger
|
||||||
{
|
{
|
||||||
private:
|
|
||||||
std::vector<LogSink*, TAllocator<LogSink*>> mSinks;
|
|
||||||
public:
|
public:
|
||||||
explicit Logger(TAllocator<LogSink*> allocator = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TAllocator<LogSink*>>)
|
using char_t = TChar;
|
||||||
|
using traits_t = TTraits;
|
||||||
|
using allocator_t = TAllocator<char_t>;
|
||||||
|
|
||||||
|
using sink_t = BaseLogSink<char_t>;
|
||||||
|
using level_t = BaseLogLevel<char_t>;
|
||||||
|
using channel_t = BaseLogChannel<char_t>;
|
||||||
|
using message_t = BaseLogMessage<char_t>;
|
||||||
|
using string_t = std::basic_string<char_t, traits_t, allocator_t>;
|
||||||
|
private:
|
||||||
|
std::vector<sink_t*, TAllocator<sink_t*>> mSinks;
|
||||||
|
public:
|
||||||
|
explicit BaseLogger(TAllocator<sink_t*> allocator = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TAllocator<sink_t*>>)
|
||||||
: mSinks(std::move(allocator))
|
: mSinks(std::move(allocator))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Logger(const Logger&) = default;
|
BaseLogger(const BaseLogger&) = default;
|
||||||
|
|
||||||
Logger(Logger&&) = default;
|
BaseLogger(BaseLogger&&) = default;
|
||||||
|
|
||||||
Logger& operator=(const Logger&) = default;
|
BaseLogger& operator=(const BaseLogger&) = default;
|
||||||
|
|
||||||
Logger& operator=(Logger&&) = default;
|
BaseLogger& operator=(BaseLogger&&) = default;
|
||||||
|
|
||||||
void addSink(LogSink& sink)
|
void addSink(sink_t& sink)
|
||||||
{
|
{
|
||||||
mSinks.push_back(&sink);
|
mSinks.push_back(&sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
void postMessage(const LogMessage& message) const MIJIN_NOEXCEPT
|
void postMessage(const message_t& message) const MIJIN_NOEXCEPT
|
||||||
{
|
{
|
||||||
for (LogSink* sink: mSinks)
|
for (sink_t* sink: mSinks)
|
||||||
{
|
{
|
||||||
sink->handleMessage(message);
|
sink->handleMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void log(const LogLevel& level, const LogChannel& channel, std::source_location sourceLocation, const char* 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({
|
postMessage({
|
||||||
.text = msg,
|
.text = msg,
|
||||||
@ -87,87 +157,100 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename... TArgs>
|
template<typename... TArgs>
|
||||||
void log(const LogLevel& level, const LogChannel& channel, std::source_location sourceLocation,
|
void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation,
|
||||||
std::format_string<TArgs...> fmt, TArgs&& ... args) const
|
std::basic_format_string<char_t, std::type_identity_t<TArgs>...> fmt, TArgs&& ... args) const
|
||||||
MIJIN_NOEXCEPT_IF(noexcept(std::declval<TAllocator<char>>().allocate(1)))
|
MIJIN_NOEXCEPT_IF(noexcept(std::declval<allocator_t>().allocate(1)))
|
||||||
{
|
{
|
||||||
std::basic_string<char, std::char_traits<char>, TAllocator<char>> buffer(TAllocator<char>(mSinks.get_allocator()));
|
string_t buffer(allocator_t(mSinks.get_allocator()));
|
||||||
std::format_to(std::back_inserter(buffer), fmt, std::forward<TArgs>(args)...);
|
std::format_to(std::back_inserter(buffer), fmt, std::forward<TArgs>(args)...);
|
||||||
log(level, channel, std::move(sourceLocation), buffer.c_str());
|
log(level, channel, std::move(sourceLocation), buffer.c_str());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MIJIN_DECLARE_LOG_CHANNEL(cnlName) \
|
#define LOGGER_SET_ARGS(chr_type) chr_type, std::char_traits<chr_type>, TAllocator
|
||||||
namespace mijin_log_channel \
|
|
||||||
{ \
|
|
||||||
extern const ::mijin::LogChannel cnlName; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MIJIN_DEFINE_LOG_CHANNEL(cnlName) \
|
MIJIN_DEFINE_CHAR_VERSIONS_TMPL(Logger, LOGGER_COMMON_ARGS, LOGGER_SET_ARGS)
|
||||||
namespace mijin_log_channel \
|
|
||||||
|
#undef LOGGER_COMMON_ARGS
|
||||||
|
#undef LOGGER_SET_ARGS
|
||||||
|
|
||||||
|
#define MIJIN_DECLARE_LOG_CHANNEL_BASE(chr_type, cnlName) \
|
||||||
|
namespace MIJIN_NSNAME_LOG_CHANNEL \
|
||||||
{ \
|
{ \
|
||||||
const ::mijin::LogChannel cnlName { \
|
extern const ::mijin::BaseLogChannel<chr_type> cnlName; \
|
||||||
.name = #cnlName \
|
}
|
||||||
|
#define MIJIN_DECLARE_LOG_CHANNEL(cnlName) MIJIN_DECLARE_LOG_CHANNEL_BASE(MIJIN_DEFAULT_CHAR_TYPE, cnlName)
|
||||||
|
|
||||||
|
#define MIJIN_DEFINE_LOG_CHANNEL_BASE(chr_type, cnlName) \
|
||||||
|
namespace MIJIN_NSNAME_LOG_CHANNEL \
|
||||||
|
{ \
|
||||||
|
const ::mijin::BaseLogChannel<chr_type> cnlName { \
|
||||||
|
.name = MIJIN_SMART_STRINGIFY(chr_type, cnlName) \
|
||||||
}; \
|
}; \
|
||||||
}
|
}
|
||||||
|
#define MIJIN_DEFINE_LOG_CHANNEL(cnlName) MIJIN_DEFINE_LOG_CHANNEL_BASE(MIJIN_DEFAULT_CHAR_TYPE, cnlName)
|
||||||
|
|
||||||
#define MIJIN_DEFINE_LOG_LEVEL(lvlName, lvlValue) \
|
#define MIJIN_DEFINE_LOG_LEVEL_BASE(chr_type, lvlName, lvlValue) \
|
||||||
namespace mijin_log_level \
|
namespace MIJIN_NSNAME_LOG_LEVEL \
|
||||||
{ \
|
{ \
|
||||||
inline constexpr ::mijin::LogLevel lvlName{ \
|
inline constexpr ::mijin::BaseLogLevel<chr_type> lvlName{ \
|
||||||
.name = #lvlName, \
|
.name = MIJIN_SMART_STRINGIFY(chr_type, lvlName), \
|
||||||
.value = lvlValue \
|
.value = lvlValue \
|
||||||
}; \
|
}; \
|
||||||
}
|
}
|
||||||
|
#define MIJIN_DEFINE_LOG_LEVEL(lvlName, lvlValue) MIJIN_DEFINE_LOG_LEVEL_BASE(MIJIN_DEFAULT_CHAR_TYPE, lvlName, lvlValue)
|
||||||
|
|
||||||
MIJIN_DECLARE_LOG_CHANNEL(GENERAL)
|
#if defined(MIJIN_MIN_LOGLEVEL_COMPILE)
|
||||||
MIJIN_DEFINE_LOG_LEVEL(DEBUG, -1000)
|
inline constexpr int MIN_LOG_LEVEL_COMPILE = static_cast<int>(MIJIN_MIN_LOGLEVEL_COMPILE);
|
||||||
MIJIN_DEFINE_LOG_LEVEL(VERBOSE, -500)
|
|
||||||
MIJIN_DEFINE_LOG_LEVEL(INFO, 0)
|
|
||||||
MIJIN_DEFINE_LOG_LEVEL(WARNING, 500)
|
|
||||||
MIJIN_DEFINE_LOG_LEVEL(ERROR, 1000)
|
|
||||||
|
|
||||||
#if defined(MIJIN_MIN_LOGLEVEL)
|
|
||||||
inline constexpr int MIN_LOG_LEVEL = static_cast<int>(MIJIN_MIN_LOGLEVEL);
|
|
||||||
#elif defined(MIJIN_DEBUG)
|
#elif defined(MIJIN_DEBUG)
|
||||||
inline constexpr int MIN_LOG_LEVEL = mijin_log_level::DEBUG.value;
|
inline constexpr int MIN_LOG_LEVEL_COMPILE = MIJIN_LOG_LEVEL_VALUE_DEBUG;
|
||||||
#else
|
#else
|
||||||
inline constexpr int MIN_LOG_LEVEL = mijin_log_level::VERBOSE.value;
|
inline constexpr int MIN_LOG_LEVEL_COMPILE = MIJIN_LOG_LEVEL_VALUE_VERBOSE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MIJIN_IMPORT_LOG_DEFAULTS \
|
#define MIJIN_DEFINE_DEFAULT_LOG_EVELS \
|
||||||
namespace mijin_log_channel \
|
MIJIN_DEFINE_LOG_LEVEL(DEBUG, MIJIN_LOG_LEVEL_VALUE_DEBUG) \
|
||||||
{ \
|
MIJIN_DEFINE_LOG_LEVEL(VERBOSE, MIJIN_LOG_LEVEL_VALUE_VERBOSE) \
|
||||||
using ::mijin::mijin_log_channel::GENERAL; \
|
MIJIN_DEFINE_LOG_LEVEL(INFO, MIJIN_LOG_LEVEL_VALUE_INFO) \
|
||||||
} \
|
MIJIN_DEFINE_LOG_LEVEL(WARNING, MIJIN_LOG_LEVEL_VALUE_WARNING) \
|
||||||
\
|
MIJIN_DEFINE_LOG_LEVEL(ERROR, MIJIN_LOG_LEVEL_VALUE_ERROR)
|
||||||
namespace mijin_log_level \
|
|
||||||
{ \
|
|
||||||
using ::mijin::mijin_log_level::DEBUG; \
|
|
||||||
using ::mijin::mijin_log_level::VERBOSE; \
|
|
||||||
using ::mijin::mijin_log_level::INFO; \
|
|
||||||
using ::mijin::mijin_log_level::WARNING; \
|
|
||||||
using ::mijin::mijin_log_level::ERROR; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MIJIN_LOG_ALWAYS(level, channel, ...) mijin__getLogger__().log( \
|
#define MIJIN_LOG_LEVEL_OBJECT(level) MIJIN_NSNAME_LOG_LEVEL::level
|
||||||
mijin_log_level::level, mijin_log_channel::channel, std::source_location::current(), __VA_ARGS__ \
|
#define MIJIN_LOG_CHANNEL_OBJECT(channel) MIJIN_NSNAME_LOG_CHANNEL::channel
|
||||||
|
|
||||||
|
#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(level, channel, ...) \
|
#define MIJIN_LOG(level, channel, ...) \
|
||||||
if constexpr (mijin_log_level::level.value < mijin::MIN_LOG_LEVEL) {} \
|
if constexpr (MIJIN_LOG_LEVEL_OBJECT(level).value < MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE()) {} \
|
||||||
else MIJIN_LOG_ALWAYS(level, channel, __VA_ARGS__)
|
else MIJIN_LOG_ALWAYS(level, channel, __VA_ARGS__)
|
||||||
|
|
||||||
#define MIJIN_SET_CLASS_LOGGER(loggerExpr) \
|
#define MIJIN_SET_CLASS_LOGGER(loggerExpr) \
|
||||||
const auto& mijin__getLogger__() const noexcept \
|
const auto& MIJIN_FUNCNAME_GET_LOGGER() const noexcept \
|
||||||
{ \
|
{ \
|
||||||
return loggerExpr; \
|
return loggerExpr; \
|
||||||
}
|
}
|
||||||
#define MIJIN_SET_SCOPE_LOGGER(loggerExpr) \
|
#define MIJIN_SET_SCOPE_LOGGER(loggerExpr) \
|
||||||
auto mijin__getLogger__ = [&]() -> const auto& \
|
auto MIJIN_FUNCNAME_GET_LOGGER = [&]() -> const auto& \
|
||||||
{ \
|
{ \
|
||||||
return loggerExpr; \
|
return loggerExpr; \
|
||||||
};
|
};
|
||||||
|
#define MIJIN_SET_CLASS_MIN_LOG_LEVEL_COMPILE(level) \
|
||||||
|
int MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE() MIJIN_NOEXCEPT \
|
||||||
|
{ \
|
||||||
|
return MIJIN_LOG_LEVEL_OBJECT(level).value; \
|
||||||
|
}
|
||||||
|
#define MIJIN_SET_SCOPE_MIN_LOG_LEVEL_COMPILE(level) \
|
||||||
|
auto MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE = []() -> int \
|
||||||
|
{ \
|
||||||
|
return MIJIN_LOG_LEVEL_OBJECT(level).value; \
|
||||||
|
};
|
||||||
} // namespace mijin
|
} // namespace mijin
|
||||||
|
|
||||||
|
inline constexpr int MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE() MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
return ::mijin::MIN_LOG_LEVEL_COMPILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif // !defined(MIJIN_LOGGING_LOGGER_HPP_INCLUDED)
|
#endif // !defined(MIJIN_LOGGING_LOGGER_HPP_INCLUDED)
|
||||||
|
@ -5,32 +5,50 @@
|
|||||||
#define MIJIN_LOGGING_STDIO_SINK_HPP_INCLUDED 1
|
#define MIJIN_LOGGING_STDIO_SINK_HPP_INCLUDED 1
|
||||||
|
|
||||||
#include "./formatting.hpp"
|
#include "./formatting.hpp"
|
||||||
|
#include "../util/traits.hpp"
|
||||||
|
|
||||||
namespace mijin
|
namespace mijin
|
||||||
{
|
{
|
||||||
template<template<typename> typename TAllocator = std::allocator,
|
template<MIJIN_FORMATTING_SINK_TMPL_ARGS_INIT>
|
||||||
deleter_type<LogFormatter<TAllocator<char>>> TDeleter = AllocatorDeleter<TAllocator<LogFormatter<TAllocator<char>>>>>
|
requires(allocator_type<TAllocator<TChar>>)
|
||||||
requires(allocator_type<TAllocator<char>>)
|
class StdioSink : public BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG_NAMES>
|
||||||
class StdioSink : public FormattingLogSink<TAllocator, TDeleter>
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using base_t = FormattingLogSink<TAllocator, TDeleter>;
|
using base_t = BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG_NAMES>;
|
||||||
|
using typename base_t::char_t;
|
||||||
|
using typename base_t::allocator_t;
|
||||||
using typename base_t::formatter_ptr_t;
|
using typename base_t::formatter_ptr_t;
|
||||||
|
using typename base_t::message_t;
|
||||||
|
private:
|
||||||
|
int mMinStderrLevel;
|
||||||
|
public:
|
||||||
|
explicit StdioSink(not_null_t<formatter_ptr_t> formatter, allocator_t allocator = {},
|
||||||
|
int minStderrLevel = MIJIN_LOG_LEVEL_VALUE_WARNING)
|
||||||
|
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<allocator_t>)
|
||||||
|
: base_t(std::move(formatter), std::move(allocator)), mMinStderrLevel(minStderrLevel) {}
|
||||||
|
|
||||||
explicit StdioSink(not_null_t<formatter_ptr_t> formatter, TAllocator<char> allocator = {})
|
void handleMessageFormatted(const message_t& message, const char_t* formatted) MIJIN_NOEXCEPT override
|
||||||
MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TAllocator<char>>)
|
|
||||||
: base_t(std::move(formatter), std::move(allocator)) {}
|
|
||||||
|
|
||||||
void handleMessageFormatted(const LogMessage& message, const char* formatted) MIJIN_NOEXCEPT override
|
|
||||||
{
|
{
|
||||||
if (*message.level >= mijin_log_level::WARNING)
|
FILE* stream = (message.level->value >= mMinStderrLevel) ? stderr : stdout;
|
||||||
|
if constexpr (std::is_same_v<char_t, char>)
|
||||||
{
|
{
|
||||||
std::fputs(formatted, stderr);
|
std::fputs(formatted, stream);
|
||||||
std::fputc('\n', stderr);
|
std::fputc('\n', stream);
|
||||||
|
}
|
||||||
|
else if constexpr (std::is_same_v<char_t, wchar_t>)
|
||||||
|
{
|
||||||
|
std::fputws(formatted, stream);
|
||||||
|
std::fputwc(L'\n', stream);
|
||||||
|
}
|
||||||
|
else if constexpr (sizeof(char_t) == sizeof(char))
|
||||||
|
{
|
||||||
|
// char8_t etc.
|
||||||
|
std::fputs(std::bit_cast<const char*>(formatted), stream);
|
||||||
|
std::fputc('\n', stream);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::puts(formatted);
|
static_assert(always_false_v<char_t>, "Character type not supported.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
namespace mijin
|
namespace mijin
|
||||||
{
|
{
|
||||||
template<typename TChar, typename TTraits = std::char_traits<TChar>, typename TAllocator = std::allocator<TChar>>
|
template<typename TChar, typename TTraits = std::char_traits<TChar>, typename TAllocator = MIJIN_DEFAULT_ALLOCATOR<TChar>>
|
||||||
class URLBase
|
class URLBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -33,8 +33,8 @@ public:
|
|||||||
constexpr URLBase(const URLBase&) = default;
|
constexpr URLBase(const URLBase&) = default;
|
||||||
constexpr URLBase(URLBase&&) MIJIN_NOEXCEPT = default;
|
constexpr URLBase(URLBase&&) MIJIN_NOEXCEPT = default;
|
||||||
constexpr URLBase(string_t base) MIJIN_NOEXCEPT : base_(std::move(base)) { parse(); }
|
constexpr URLBase(string_t base) MIJIN_NOEXCEPT : base_(std::move(base)) { parse(); }
|
||||||
constexpr URLBase(string_view_t base) : URLBase(string_t(base.begin(), base.end())) {}
|
constexpr URLBase(string_view_t base, TAllocator allocator = {}) : URLBase(string_t(base.begin(), base.end(), std::move(allocator))) {}
|
||||||
constexpr URLBase(const TChar* base) : URLBase(string_t(base)) {}
|
constexpr URLBase(const TChar* base, TAllocator allocator = {}) : URLBase(string_t(base, std::move(allocator))) {}
|
||||||
|
|
||||||
constexpr URLBase& operator=(const URLBase&) = default;
|
constexpr URLBase& operator=(const URLBase&) = default;
|
||||||
constexpr URLBase& operator=(URLBase&&) MIJIN_NOEXCEPT = default;
|
constexpr URLBase& operator=(URLBase&&) MIJIN_NOEXCEPT = default;
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
@ -17,6 +19,7 @@
|
|||||||
|
|
||||||
#include "./iterators.hpp"
|
#include "./iterators.hpp"
|
||||||
#include "../internal/common.hpp"
|
#include "../internal/common.hpp"
|
||||||
|
#include "../util/traits.hpp"
|
||||||
|
|
||||||
namespace mijin
|
namespace mijin
|
||||||
{
|
{
|
||||||
@ -33,8 +36,56 @@ namespace mijin
|
|||||||
// public traits
|
// public traits
|
||||||
//
|
//
|
||||||
|
|
||||||
template<typename TString>
|
template<typename T>
|
||||||
using char_type_t = decltype(std::string_view(std::declval<TString>()))::value_type;
|
inline constexpr bool is_string_v = is_template_instance_v<std::basic_string, std::remove_cvref_t<T>>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept std_string_type = is_string_v<T>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline constexpr bool is_string_view_v = is_template_instance_v<std::basic_string_view, std::remove_cvref_t<T>>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept std_string_view_type = is_string_view_v<T>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline constexpr bool is_char_v = is_any_type_v<std::remove_cvref_t<T>, char, wchar_t, char8_t, char16_t, char32_t>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept char_type = is_char_v<T>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline constexpr bool is_cstring_v = std::is_pointer_v<T> && is_char_v<std::remove_pointer_t<T>>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept cstring_type = is_cstring_v<T>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct str_char_type
|
||||||
|
{
|
||||||
|
using type = void;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<char_type T>
|
||||||
|
struct str_char_type<T*>
|
||||||
|
{
|
||||||
|
using type = std::remove_cvref_t<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<std_string_view_type T>
|
||||||
|
struct str_char_type<T>
|
||||||
|
{
|
||||||
|
using type = typename std::remove_cvref_t<T>::value_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<std_string_type T>
|
||||||
|
struct str_char_type<T>
|
||||||
|
{
|
||||||
|
using type = typename std::remove_cvref_t<T>::value_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using str_char_type_t = str_char_type<T>::type;
|
||||||
|
|
||||||
//
|
//
|
||||||
// public types
|
// public types
|
||||||
@ -255,7 +306,7 @@ template<typename TString>
|
|||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
auto trimPrefix(TString&& string)
|
auto trimPrefix(TString&& string)
|
||||||
{
|
{
|
||||||
return trimPrefix(string, detail::DEFAULT_TRIM_CHARS<char_type_t<TString>>);
|
return trimPrefix(string, detail::DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TString, typename TChars>
|
template<typename TString, typename TChars>
|
||||||
@ -269,7 +320,7 @@ template<typename TString>
|
|||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
auto trimSuffix(TString&& string)
|
auto trimSuffix(TString&& string)
|
||||||
{
|
{
|
||||||
return trimSuffix(string, detail::DEFAULT_TRIM_CHARS<char_type_t<TString>>);
|
return trimSuffix(string, detail::DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TString, typename TChars>
|
template<typename TString, typename TChars>
|
||||||
@ -283,7 +334,7 @@ template<typename TString>
|
|||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
auto trim(TString&& string)
|
auto trim(TString&& string)
|
||||||
{
|
{
|
||||||
return trim(string, detail::DEFAULT_TRIM_CHARS<char_type_t<TString>>);
|
return trim(string, detail::DEFAULT_TRIM_CHARS<str_char_type_t<TString>>);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename TLeft, typename TRight>
|
template<typename TLeft, typename TRight>
|
||||||
@ -433,6 +484,142 @@ auto operator|(TIterable&& iterable, const Join& joiner)
|
|||||||
{
|
{
|
||||||
return join(std::forward<TIterable>(iterable), joiner.delimiter);
|
return join(std::forward<TIterable>(iterable), joiner.delimiter);
|
||||||
}
|
}
|
||||||
|
} // namespace pipe
|
||||||
|
|
||||||
|
struct [[nodiscard]] ConvertCharTypeResult
|
||||||
|
{
|
||||||
|
unsigned numRead = 0;
|
||||||
|
unsigned numWritten = 0;
|
||||||
|
|
||||||
|
constexpr operator bool() const MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
return numRead != 0 || numWritten != 0;
|
||||||
|
}
|
||||||
|
constexpr bool operator !() const MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
return !static_cast<bool>(*this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename TFrom, typename TTo>
|
||||||
|
ConvertCharTypeResult convertCharType(const TFrom* chrFrom, std::size_t numFrom, TTo* outTo, std::size_t numTo, std::mbstate_t& mbstate) MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<TFrom, char>)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<TTo, wchar_t>)
|
||||||
|
{
|
||||||
|
const std::size_t result = std::mbrtowc(outTo, chrFrom, numFrom, &mbstate);
|
||||||
|
if (result == static_cast<std::size_t>(-1))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
.numRead = static_cast<unsigned>(result),
|
||||||
|
.numWritten = 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<TTo, char8_t>)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<TFrom, wchar_t>)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<TTo, char>)
|
||||||
|
{
|
||||||
|
if (numTo < MB_CUR_MAX)
|
||||||
|
{
|
||||||
|
char tmpBuf[MB_LEN_MAX];
|
||||||
|
const ConvertCharTypeResult result = convertCharType(chrFrom, tmpBuf, mbstate);
|
||||||
|
if (result && result.numWritten <= numTo)
|
||||||
|
{
|
||||||
|
std::memcpy(outTo, tmpBuf, result.numWritten);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const std::size_t result = std::wcrtomb(outTo, *chrFrom, &mbstate);
|
||||||
|
if (result == static_cast<std::size_t>(-1))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
.numRead = 1,
|
||||||
|
.numWritten = static_cast<unsigned>(result)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<TTo, char8_t>)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if constexpr (std::is_same_v<TFrom, char8_t>)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<typename TFrom, typename TTo>
|
||||||
|
ConvertCharTypeResult convertCharType(const TFrom* chrFrom, std::size_t numFrom, TTo* outTo, std::size_t numTo) MIJIN_NOEXCEPT
|
||||||
|
{
|
||||||
|
std::mbstate_t mbstate;
|
||||||
|
return convertCharType(chrFrom, numFrom, outTo, numTo, mbstate);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TIterator>
|
||||||
|
struct [[nodiscard]] ConvertStringTypeResult
|
||||||
|
{
|
||||||
|
TIterator iterator;
|
||||||
|
bool success;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename TTo, typename TFrom, typename TFromTraits, std::output_iterator<TTo> TIterator>
|
||||||
|
ConvertStringTypeResult<TIterator> convertStringType(std::basic_string_view<TFrom, TFromTraits> strFrom, TIterator outIterator)
|
||||||
|
{
|
||||||
|
TTo outBuffer[MB_LEN_MAX];
|
||||||
|
|
||||||
|
std::mbstate_t mbstate = {};
|
||||||
|
for (auto it = strFrom.begin(); it != strFrom.end();)
|
||||||
|
{
|
||||||
|
const std::size_t remaining = std::distance(it, strFrom.end());
|
||||||
|
const ConvertCharTypeResult result = convertCharType(&*it, remaining, outBuffer, MB_LEN_MAX, mbstate);
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
.iterator = outIterator,
|
||||||
|
.success = false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for (unsigned pos = 0; pos < result.numWritten; ++pos)
|
||||||
|
{
|
||||||
|
*outIterator = outBuffer[pos];
|
||||||
|
++outIterator;
|
||||||
|
}
|
||||||
|
it = std::next(it, result.numRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
.iterator = outIterator,
|
||||||
|
.success = true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TFrom, typename TFromTraits, typename TTo, typename TToTraits, typename TToAllocator>
|
||||||
|
bool convertStringType(std::basic_string_view<TFrom, TFromTraits> strFrom, std::basic_string<TTo, TToTraits, TToAllocator>& outString)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_same_v<TTo, TFrom>)
|
||||||
|
{
|
||||||
|
outString += strFrom;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return convertStringType<TTo>(strFrom, std::back_inserter(outString)).success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename TFrom, typename TTo, typename TToTraits, typename TToAllocator>
|
||||||
|
bool convertStringType(const TFrom* strFrom, std::basic_string<TTo, TToTraits, TToAllocator>& outString)
|
||||||
|
{
|
||||||
|
return convertStringType(std::basic_string_view<TFrom>(strFrom), outString);
|
||||||
}
|
}
|
||||||
} // namespace mijin
|
} // namespace mijin
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user