diff --git a/source/mijin/container/vector_map.hpp b/source/mijin/container/vector_map.hpp index a675e48..53e5fb3 100644 --- a/source/mijin/container/vector_map.hpp +++ b/source/mijin/container/vector_map.hpp @@ -7,6 +7,7 @@ #include #include #include + #include "./boxed_object.hpp" #include "./optional.hpp" @@ -102,7 +103,7 @@ public: } }; -template, typename TValueAllocator = std::allocator> +template, typename TValueAllocator = MIJIN_DEFAULT_ALLOCATOR> class VectorMap { public: @@ -119,12 +120,17 @@ private: std::vector keys_; std::vector values_; public: - VectorMap() noexcept = default; + explicit VectorMap(TKeyAllocator keyAllocator = {}) + MIJIN_NOEXCEPT_IF((std::is_nothrow_move_constructible_v && std::is_nothrow_constructible_v)) + : keys_(std::move(keyAllocator)), values_(TValueAllocator(keys_.get_allocator())) {} + VectorMap(TKeyAllocator keyAllocator, TValueAllocator valueAllocator) + MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : keys_(std::move(keyAllocator)), values_(std::move(valueAllocator)) {} VectorMap(const VectorMap&) = default; - VectorMap(VectorMap&&) MIJIN_NOEXCEPT = default; + VectorMap(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; TValue& operator[](const TKey& key) diff --git a/source/mijin/internal/common.hpp b/source/mijin/internal/common.hpp index 474aca7..26a2a30 100644 --- a/source/mijin/internal/common.hpp +++ b/source/mijin/internal/common.hpp @@ -1,5 +1,7 @@ #pragma once +#include "./config.hpp" +#include "./helpers.hpp" #include "./exception.hpp" #include "./version_support.hpp" diff --git a/source/mijin/internal/config.hpp b/source/mijin/internal/config.hpp new file mode 100644 index 0000000..454e69f --- /dev/null +++ b/source/mijin/internal/config.hpp @@ -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) diff --git a/source/mijin/internal/helpers.hpp b/source/mijin/internal/helpers.hpp new file mode 100644 index 0000000..1f16253 --- /dev/null +++ b/source/mijin/internal/helpers.hpp @@ -0,0 +1,57 @@ + +#pragma once + +#if !defined(MIJIN_INTERNAL_HELPERS_HPP_INCLUDED) +#define MIJIN_INTERNAL_HELPERS_HPP_INCLUDED 1 + +#include +#include "../util/traits.hpp" + +#define MIJIN_IDENTITY(what) what +#define MIJIN_NULLIFY(what) + +#define MIJIN_SMART_QUOTE(chr_type, text) \ +[](TChar__) consteval \ +{ \ + if constexpr (std::is_same_v) \ + { \ + return text; \ + } \ + else if constexpr (std::is_same_v) \ + { \ + return L ## text; \ + } \ + else if constexpr (std::is_same_v) \ + { \ + return u8 ## text; \ + } \ + else \ + { \ + static_assert(::mijin::always_false_v, "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 ; \ + \ +prefix_a prefix_b(wchar_t) prefix_c \ +using W ## type_name = Base ## type_name ; \ + \ +prefix_a prefix_b(char8_t) prefix_c \ +using U ## type_name = Base ## type_name ; \ + \ +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) diff --git a/source/mijin/logging/formatting.hpp b/source/mijin/logging/formatting.hpp index 53a4e01..fe15d32 100644 --- a/source/mijin/logging/formatting.hpp +++ b/source/mijin/logging/formatting.hpp @@ -8,61 +8,98 @@ #include #include #include "./logger.hpp" +#include "../internal/common.hpp" #include "../memory/dynamic_pointer.hpp" #include "../memory/memutil.hpp" #include "../memory/virtual_allocator.hpp" #include "../util/annot.hpp" #include "../util/concepts.hpp" +#include "../util/string.hpp" namespace mijin { -template TAllocator = std::allocator> -class LogFormatter +#define FORMATTER_COMMON_ARGS(chr_type) allocator_type_for TAllocator = MIJIN_DEFAULT_ALLOCATOR +template, + FORMATTER_COMMON_ARGS(TChar)> +class BaseLogFormatter { public: - using string_t = std::basic_string, TAllocator>; + using char_t = TChar; + using traits_t = TTraits; + using allocator_t = TAllocator; + using string_t = std::basic_string; - virtual ~LogFormatter() noexcept = default; + virtual ~BaseLogFormatter() noexcept = default; virtual void format(const LogMessage& message, string_t& outFormatted) noexcept = 0; }; -template TAllocator = std::allocator> -class SimpleLogFormatter : public LogFormatter +template, + FORMATTER_COMMON_ARGS(TChar)> +class BaseSimpleLogFormatter : public BaseLogFormatter { public: - using typename LogFormatter::string_t; + using char_t = TChar; + using traits_t = TTraits; + using allocator_t = TAllocator; + using base_t = BaseLogFormatter; + using typename base_t::string_t; private: string_t mFormat; + string_t mFormatBuffer; 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; }; -template typename TAllocator = std::allocator, - deleter_type>> TDeleter = AllocatorDeleter>>>> - requires(allocator_type>) -class FormattingLogSink : public LogSink +#define FORMATTER_SET_ARGS(chr_type) chr_type, std::char_traits, TAllocator + +MIJIN_DEFINE_CHAR_VERSIONS_TMPL(LogFormatter, FORMATTER_COMMON_ARGS, FORMATTER_SET_ARGS) +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, \ + template typename TAllocator = MIJIN_DEFAULT_ALLOCATOR, \ + deleter_type>> TDeleter \ + = AllocatorDeleter>>> + +#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 + requires(allocator_type>) +class BaseFormattingLogSink : public BaseLogSink { public: - using allocator_t = TAllocator; - using formatter_t = LogFormatter; + using base_t = BaseLogSink; + + using char_t = TChar; + using traits_t = TTraits; + 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: not_null_t mFormatter; string_t mBuffer; public: - explicit FormattingLogSink(not_null_t formatter, TAllocator allocator = {}) - MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v>) + explicit BaseFormattingLogSink(not_null_t formatter, allocator_t allocator = {}) + MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v) : 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(); mFormatter->format(message, mBuffer); @@ -70,33 +107,42 @@ public: } }; -template TAllocator> -void SimpleLogFormatter::format(const LogMessage& message, string_t& outFormatted) noexcept +#define SINK_SET_ARGS(chr_type) chr_type, std::char_traits, TAllocator, TDeleter + +MIJIN_DEFINE_CHAR_VERSIONS_TMPL(FormattingLogSink, MIJIN_FORMATTING_SINK_COMMON_ARGS, SINK_SET_ARGS) + +#undef SINK_SET_ARGS + +template TAllocator> +void BaseSimpleLogFormatter::format(const LogMessage& message, string_t& outFormatted) noexcept { + using string_view_t = std::basic_string_view; + + mFormatBuffer.clear(); for (auto pos = mFormat.begin(); pos != mFormat.end(); ++pos) { - if (*pos == '{') + if (*pos == MIJIN_SMART_QUOTE(char_t, '{')) { ++pos; - if (*pos == '{') + if (*pos == MIJIN_SMART_QUOTE(char_t, '{')) { // double { - outFormatted += '{'; + outFormatted += MIJIN_SMART_QUOTE(char_t, '{'); continue; } 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()); MIJIN_ASSERT(pos != mFormat.end(), "Invalid format."); - const std::string_view argName(argStart, pos); - std::string argFormat; + const string_view_t argName(argStart, pos); + string_view_t argFormat; if (*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."); - 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 @@ -107,56 +153,102 @@ void SimpleLogFormatter::format(const LogMessage& message, string_t& // if there is no format, just directly print the value if (argFormat.empty()) { - if constexpr (std::is_arithmetic_v) + if constexpr (is_char_v) { - std::format_to(std::back_inserter(outFormatted), "{}", value); + convertStringType(string_view_t(&value, 1), outFormatted); + } + else if constexpr (std::is_arithmetic_v) + { + std::format_to(std::back_inserter(outFormatted), MIJIN_SMART_QUOTE(char_t, "{}"), value); + } + else if constexpr (is_string_v || is_cstring_v) + { + convertStringType(value, outFormatted); } else { + static_assert(always_false_v); outFormatted += value; } return; } + // first copy the format string + braces into the buffer - const auto formatStart = outFormatted.size(); - outFormatted += '{'; - outFormatted += argFormat; - outFormatted += '}'; - const auto formatEnd = outFormatted.size(); - auto format = std::string_view(outFormatted).substr(formatStart, formatEnd - formatStart); + const auto formatStart = mFormatBuffer.size(); + mFormatBuffer += '{'; + mFormatBuffer += argFormat; + mFormatBuffer += '}'; + const auto formatEnd = mFormatBuffer.size(); - // then append the formatted text - std::vformat_to(std::back_inserter(outFormatted), format, std::make_format_args(value)); + auto doFormatTo = [](string_t& string, string_view_t format, const auto& value) + { + if constexpr (std::is_same_v) + { + std::vformat_to(std::back_inserter(string), format, std::make_format_args(value)); + } + else if constexpr (std::is_same_v) + { + std::vformat_to(std::back_inserter(string), format, std::make_wformat_args(value)); + } + else + { + static_assert(always_false_v, "Cannot format this char type."); + } + }; - // and then remove the format from the buffer again - outFormatted.erase(formatStart, formatEnd - formatStart); + auto doFormat = [&](const auto& value) + { + auto format = string_view_t(mFormatBuffer).substr(formatStart, formatEnd - formatStart); + doFormatTo(outFormatted, format, value); + }; + + if constexpr (is_char_v && !std::is_same_v) + { + static_assert(always_false_v, "TODO..."); + } + else if constexpr ((is_string_v || is_cstring_v) && !std::is_same_v, 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 == "text") + if (argName == MIJIN_SMART_QUOTE(char_t, "text")) { formatInline(message.text); } - else if (argName == "channel") + else if (argName == MIJIN_SMART_QUOTE(char_t, "channel")) { formatInline(message.channel->name); } - else if (argName == "file") + else if (argName == MIJIN_SMART_QUOTE(char_t, "file")) { formatInline(message.sourceLocation.file_name()); } - else if (argName == "function") + else if (argName == MIJIN_SMART_QUOTE(char_t, "function")) { formatInline(message.sourceLocation.function_name()); } - else if (argName == "line") + else if (argName == MIJIN_SMART_QUOTE(char_t, "line")) { formatInline(message.sourceLocation.line()); } - else if (argName == "column") + else if (argName == MIJIN_SMART_QUOTE(char_t, "column")) { formatInline(message.sourceLocation.column()); } - else if (argName == "level") + else if (argName == MIJIN_SMART_QUOTE(char_t, "level")) { formatInline(message.level->name); } diff --git a/source/mijin/logging/logger.hpp b/source/mijin/logging/logger.hpp index ea2b7d9..a40ff1d 100644 --- a/source/mijin/logging/logger.hpp +++ b/source/mijin/logging/logger.hpp @@ -14,69 +14,139 @@ 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 +struct BaseLogLevel { - const char* name; + using char_t = TChar; + + const char_t* name; int 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 +struct BaseLogChannel { - const char* name; + using char_t = TChar; + + const char_t* name; }; -struct LogMessage +MIJIN_DEFINE_CHAR_VERSIONS(LogChannel) + +template +struct BaseLogMessage { - const char* text; - const LogChannel* channel; - const LogLevel* level; + using char_t = TChar; + + const char_t* text; + const BaseLogChannel* channel; + const BaseLogLevel* level; std::source_location sourceLocation; }; -class LogSink +MIJIN_DEFINE_CHAR_VERSIONS(LogMessage) + +template +class BaseLogSink { public: - virtual ~LogSink() noexcept = default; + using char_t = TChar; + using message_t = BaseLogMessage; - virtual void handleMessage(const LogMessage& message) MIJIN_NOEXCEPT = 0; + virtual ~BaseLogSink() noexcept = default; + + virtual void handleMessage(const message_t& message) MIJIN_NOEXCEPT = 0; }; -template typename TAllocator = std::allocator> -class Logger +MIJIN_DEFINE_CHAR_VERSIONS(LogSink) + +#define LOGGER_COMMON_ARGS(chr_type) template typename TAllocator = MIJIN_DEFAULT_ALLOCATOR +template, LOGGER_COMMON_ARGS(TChar)> +class BaseLogger { -private: - std::vector> mSinks; public: - explicit Logger(TAllocator allocator = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v>) + using char_t = TChar; + using traits_t = TTraits; + using allocator_t = TAllocator; + + using sink_t = BaseLogSink; + using level_t = BaseLogLevel; + using channel_t = BaseLogChannel; + using message_t = BaseLogMessage; + using string_t = std::basic_string; +private: + std::vector> mSinks; +public: + explicit BaseLogger(TAllocator allocator = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v>) : 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); } - 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); } } - 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({ .text = msg, @@ -87,87 +157,100 @@ public: } template - void log(const LogLevel& level, const LogChannel& channel, std::source_location sourceLocation, - std::format_string fmt, TArgs&& ... args) const - MIJIN_NOEXCEPT_IF(noexcept(std::declval>().allocate(1))) + void log(const level_t& level, const channel_t& channel, std::source_location sourceLocation, + std::basic_format_string...> fmt, TArgs&& ... args) const + MIJIN_NOEXCEPT_IF(noexcept(std::declval().allocate(1))) { - std::basic_string, TAllocator> buffer(TAllocator(mSinks.get_allocator())); + 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()); } }; -#define MIJIN_DECLARE_LOG_CHANNEL(cnlName) \ -namespace mijin_log_channel \ -{ \ - extern const ::mijin::LogChannel cnlName; \ -} +#define LOGGER_SET_ARGS(chr_type) chr_type, std::char_traits, TAllocator -#define MIJIN_DEFINE_LOG_CHANNEL(cnlName) \ -namespace mijin_log_channel \ -{ \ -const ::mijin::LogChannel cnlName { \ - .name = #cnlName \ -}; \ -} +MIJIN_DEFINE_CHAR_VERSIONS_TMPL(Logger, LOGGER_COMMON_ARGS, LOGGER_SET_ARGS) -#define MIJIN_DEFINE_LOG_LEVEL(lvlName, lvlValue) \ -namespace mijin_log_level \ +#undef LOGGER_COMMON_ARGS +#undef LOGGER_SET_ARGS + +#define MIJIN_DECLARE_LOG_CHANNEL_BASE(chr_type, cnlName) \ +namespace MIJIN_NSNAME_LOG_CHANNEL \ +{ \ + extern const ::mijin::BaseLogChannel 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 \ { \ - inline constexpr ::mijin::LogLevel lvlName{ \ - .name = #lvlName, \ - .value = lvlValue \ - }; \ +const ::mijin::BaseLogChannel cnlName { \ + .name = MIJIN_SMART_STRINGIFY(chr_type, cnlName) \ +}; \ } +#define MIJIN_DEFINE_LOG_CHANNEL(cnlName) MIJIN_DEFINE_LOG_CHANNEL_BASE(MIJIN_DEFAULT_CHAR_TYPE, cnlName) -MIJIN_DECLARE_LOG_CHANNEL(GENERAL) -MIJIN_DEFINE_LOG_LEVEL(DEBUG, -1000) -MIJIN_DEFINE_LOG_LEVEL(VERBOSE, -500) -MIJIN_DEFINE_LOG_LEVEL(INFO, 0) -MIJIN_DEFINE_LOG_LEVEL(WARNING, 500) -MIJIN_DEFINE_LOG_LEVEL(ERROR, 1000) +#define MIJIN_DEFINE_LOG_LEVEL_BASE(chr_type, lvlName, lvlValue) \ +namespace MIJIN_NSNAME_LOG_LEVEL \ +{ \ + inline constexpr ::mijin::BaseLogLevel lvlName{ \ + .name = MIJIN_SMART_STRINGIFY(chr_type, lvlName), \ + .value = lvlValue \ + }; \ +} +#define MIJIN_DEFINE_LOG_LEVEL(lvlName, lvlValue) MIJIN_DEFINE_LOG_LEVEL_BASE(MIJIN_DEFAULT_CHAR_TYPE, lvlName, lvlValue) -#if defined(MIJIN_MIN_LOGLEVEL) -inline constexpr int MIN_LOG_LEVEL = static_cast(MIJIN_MIN_LOGLEVEL); +#if defined(MIJIN_MIN_LOGLEVEL_COMPILE) +inline constexpr int MIN_LOG_LEVEL_COMPILE = static_cast(MIJIN_MIN_LOGLEVEL_COMPILE); #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 -inline constexpr int MIN_LOG_LEVEL = mijin_log_level::VERBOSE.value; +inline constexpr int MIN_LOG_LEVEL_COMPILE = MIJIN_LOG_LEVEL_VALUE_VERBOSE; #endif -#define MIJIN_IMPORT_LOG_DEFAULTS \ -namespace mijin_log_channel \ -{ \ - using ::mijin::mijin_log_channel::GENERAL; \ -} \ - \ -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_DEFINE_DEFAULT_LOG_EVELS \ +MIJIN_DEFINE_LOG_LEVEL(DEBUG, MIJIN_LOG_LEVEL_VALUE_DEBUG) \ +MIJIN_DEFINE_LOG_LEVEL(VERBOSE, MIJIN_LOG_LEVEL_VALUE_VERBOSE) \ +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) -#define MIJIN_LOG_ALWAYS(level, channel, ...) mijin__getLogger__().log( \ - mijin_log_level::level, mijin_log_channel::channel, std::source_location::current(), __VA_ARGS__ \ +#define MIJIN_LOG_LEVEL_OBJECT(level) MIJIN_NSNAME_LOG_LEVEL::level +#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, ...) \ -if constexpr (mijin_log_level::level.value < mijin::MIN_LOG_LEVEL) {} \ +#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__) -#define MIJIN_SET_CLASS_LOGGER(loggerExpr) \ -const auto& mijin__getLogger__() const noexcept \ -{ \ - return loggerExpr; \ +#define MIJIN_SET_CLASS_LOGGER(loggerExpr) \ +const auto& MIJIN_FUNCNAME_GET_LOGGER() const noexcept \ +{ \ + return loggerExpr; \ } -#define MIJIN_SET_SCOPE_LOGGER(loggerExpr) \ -auto mijin__getLogger__ = [&]() -> const auto& \ -{ \ - return loggerExpr; \ +#define MIJIN_SET_SCOPE_LOGGER(loggerExpr) \ +auto MIJIN_FUNCNAME_GET_LOGGER = [&]() -> const auto& \ +{ \ + 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 +inline constexpr int MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE() MIJIN_NOEXCEPT +{ + return ::mijin::MIN_LOG_LEVEL_COMPILE; +} + #endif // !defined(MIJIN_LOGGING_LOGGER_HPP_INCLUDED) diff --git a/source/mijin/logging/stdio_sink.hpp b/source/mijin/logging/stdio_sink.hpp index e06ec7c..43739e0 100644 --- a/source/mijin/logging/stdio_sink.hpp +++ b/source/mijin/logging/stdio_sink.hpp @@ -5,32 +5,50 @@ #define MIJIN_LOGGING_STDIO_SINK_HPP_INCLUDED 1 #include "./formatting.hpp" +#include "../util/traits.hpp" namespace mijin { -template typename TAllocator = std::allocator, - deleter_type>> TDeleter = AllocatorDeleter>>>> - requires(allocator_type>) -class StdioSink : public FormattingLogSink +template + requires(allocator_type>) +class StdioSink : public BaseFormattingLogSink { public: - using base_t = FormattingLogSink; + using base_t = BaseFormattingLogSink; + using typename base_t::char_t; + using typename base_t::allocator_t; using typename base_t::formatter_ptr_t; + using typename base_t::message_t; +private: + int mMinStderrLevel; +public: + explicit StdioSink(not_null_t formatter, allocator_t allocator = {}, + int minStderrLevel = MIJIN_LOG_LEVEL_VALUE_WARNING) + MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v) + : base_t(std::move(formatter), std::move(allocator)), mMinStderrLevel(minStderrLevel) {} - explicit StdioSink(not_null_t formatter, TAllocator allocator = {}) - MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v>) - : base_t(std::move(formatter), std::move(allocator)) {} - - void handleMessageFormatted(const LogMessage& message, const char* formatted) MIJIN_NOEXCEPT override + void handleMessageFormatted(const message_t& message, const char_t* 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) { - std::fputs(formatted, stderr); - std::fputc('\n', stderr); + std::fputs(formatted, stream); + std::fputc('\n', stream); + } + else if constexpr (std::is_same_v) + { + 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(formatted), stream); + std::fputc('\n', stream); } else { - std::puts(formatted); + static_assert(always_false_v, "Character type not supported."); } } }; diff --git a/source/mijin/net/url.hpp b/source/mijin/net/url.hpp index 4456a58..8a39fe3 100644 --- a/source/mijin/net/url.hpp +++ b/source/mijin/net/url.hpp @@ -12,7 +12,7 @@ namespace mijin { -template, typename TAllocator = std::allocator> +template, typename TAllocator = MIJIN_DEFAULT_ALLOCATOR> class URLBase { public: @@ -33,8 +33,8 @@ public: constexpr URLBase(const URLBase&) = default; constexpr URLBase(URLBase&&) MIJIN_NOEXCEPT = default; 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(const TChar* base) : URLBase(string_t(base)) {} + constexpr URLBase(string_view_t base, TAllocator allocator = {}) : URLBase(string_t(base.begin(), base.end(), std::move(allocator))) {} + constexpr URLBase(const TChar* base, TAllocator allocator = {}) : URLBase(string_t(base, std::move(allocator))) {} constexpr URLBase& operator=(const URLBase&) = default; constexpr URLBase& operator=(URLBase&&) MIJIN_NOEXCEPT = default; diff --git a/source/mijin/util/string.hpp b/source/mijin/util/string.hpp index 05540d8..d6448e6 100644 --- a/source/mijin/util/string.hpp +++ b/source/mijin/util/string.hpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include #include @@ -17,6 +19,7 @@ #include "./iterators.hpp" #include "../internal/common.hpp" +#include "../util/traits.hpp" namespace mijin { @@ -33,8 +36,56 @@ namespace mijin // public traits // -template -using char_type_t = decltype(std::string_view(std::declval()))::value_type; +template +inline constexpr bool is_string_v = is_template_instance_v>; + +template +concept std_string_type = is_string_v; + +template +inline constexpr bool is_string_view_v = is_template_instance_v>; + +template +concept std_string_view_type = is_string_view_v; + +template +inline constexpr bool is_char_v = is_any_type_v, char, wchar_t, char8_t, char16_t, char32_t>; + +template +concept char_type = is_char_v; + +template +inline constexpr bool is_cstring_v = std::is_pointer_v && is_char_v>; + +template +concept cstring_type = is_cstring_v; + +template +struct str_char_type +{ + using type = void; +}; + +template +struct str_char_type +{ + using type = std::remove_cvref_t; +}; + +template +struct str_char_type +{ + using type = typename std::remove_cvref_t::value_type; +}; + +template +struct str_char_type +{ + using type = typename std::remove_cvref_t::value_type; +}; + +template +using str_char_type_t = str_char_type::type; // // public types @@ -255,7 +306,7 @@ template [[nodiscard]] auto trimPrefix(TString&& string) { - return trimPrefix(string, detail::DEFAULT_TRIM_CHARS>); + return trimPrefix(string, detail::DEFAULT_TRIM_CHARS>); } template @@ -269,7 +320,7 @@ template [[nodiscard]] auto trimSuffix(TString&& string) { - return trimSuffix(string, detail::DEFAULT_TRIM_CHARS>); + return trimSuffix(string, detail::DEFAULT_TRIM_CHARS>); } template @@ -283,7 +334,7 @@ template [[nodiscard]] auto trim(TString&& string) { - return trim(string, detail::DEFAULT_TRIM_CHARS>); + return trim(string, detail::DEFAULT_TRIM_CHARS>); } template @@ -425,7 +476,7 @@ struct Join { const char* delimiter; - explicit Join(const char* delimiter_) MIJIN_NOEXCEPT : delimiter(delimiter_) {} + explicit Join(const char* delimiter_) MIJIN_NOEXCEPT: delimiter(delimiter_) {} }; template @@ -433,6 +484,142 @@ auto operator|(TIterable&& iterable, const Join& joiner) { return join(std::forward(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(*this); + } +}; + +template +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) + { + if constexpr (std::is_same_v) + { + const std::size_t result = std::mbrtowc(outTo, chrFrom, numFrom, &mbstate); + if (result == static_cast(-1)) + { + return {}; + } + return { + .numRead = static_cast(result), + .numWritten = 1 + }; + } + if constexpr (std::is_same_v) + { + + } + } + if constexpr (std::is_same_v) + { + if constexpr (std::is_same_v) + { + 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(-1)) + { + return {}; + } + return { + .numRead = 1, + .numWritten = static_cast(result) + }; + } + if constexpr (std::is_same_v) + { + + } + } + if constexpr (std::is_same_v) + { + + } +} +template +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 +struct [[nodiscard]] ConvertStringTypeResult +{ + TIterator iterator; + bool success; +}; + +template TIterator> +ConvertStringTypeResult convertStringType(std::basic_string_view 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 +bool convertStringType(std::basic_string_view strFrom, std::basic_string& outString) +{ + if constexpr (std::is_same_v) + { + outString += strFrom; + return true; + } + else + { + return convertStringType(strFrom, std::back_inserter(outString)).success; + } +} + +template +bool convertStringType(const TFrom* strFrom, std::basic_string& outString) +{ + return convertStringType(std::basic_string_view(strFrom), outString); } } // namespace mijin