Compare commits

...

7 Commits

Author SHA1 Message Date
Patrick Wuttke
4d19752964 Added quoted() string helper. 2025-08-30 00:31:27 +02:00
Patrick Wuttke
0e988a4d9e Added member_pointer_of traits. 2025-08-30 00:31:05 +02:00
Patrick Wuttke
a95885880f Added formatter for exceptions. 2025-08-30 00:30:47 +02:00
Patrick Wuttke
d76e64c062 Fixed converting auf DynamicPointers, added wrapDynamic helper to simplify creating non-owning DynamicPointers. 2025-08-30 00:30:09 +02:00
Patrick Wuttke
e704c082b7 Some fixes for logging, added MIJIN_LOG_IF and the DebugOutputLogSink. 2025-08-30 00:29:16 +02:00
Patrick Wuttke
b44d6feb97 Added MIJIN_RTTI macro for detecting if RTTI is available. 2025-08-30 00:28:19 +02:00
Patrick Wuttke
e91184ec82 Fixed current loop not being reset on exceptions. 2025-08-30 00:25:16 +02:00
10 changed files with 234 additions and 11 deletions

View File

@ -25,6 +25,7 @@
#include "../util/flag.hpp"
#include "../util/iterators.hpp"
#include "../util/misc.hpp"
#include "../util/scope_guard.hpp"
#include "../util/traits.hpp"
#if MIJIN_COROUTINE_ENABLE_DEBUG_INFO
#include "../debug/stacktrace.hpp"
@ -997,6 +998,9 @@ inline auto BaseSimpleTaskLoop<TAllocator>::tick() -> CanContinue
// set current taskloop
MIJIN_ASSERT(TaskLoop<TAllocator>::currentLoopStorage() == nullptr, "Trying to tick a loop from a coroutine, this is not supported.");
TaskLoop<TAllocator>::currentLoopStorage() = this;
MIJIN_SCOPE_EXIT {
TaskLoop<TAllocator>::currentLoopStorage() = nullptr;
};
threadId_ = std::this_thread::get_id();
// move over all tasks from newTasks
@ -1046,8 +1050,6 @@ inline auto BaseSimpleTaskLoop<TAllocator>::tick() -> CanContinue
canContinue = CanContinue::YES;
}
}
// reset current loop
TaskLoop<TAllocator>::currentLoopStorage() = nullptr;
// remove any tasks that have been transferred to another queue
it = std::remove_if(tasks_.begin(), tasks_.end(), [](const StoredTask& task) {

View File

@ -61,6 +61,26 @@ namespace mijin
#endif
#endif
#if !defined(MIJIN_RTTI)
#if MIJIN_COMPILER == MIJIN_COMPILER_GCC
#if defined(__GXX_RTTI)
#define MIJIN_RTTI 1
#else
#define MIJIN_RTTI 0
#endif
#elif MIJIN_COMPILER == MIJIN_COMPILER_CLANG
#define MIJIN_RTTI (__has_feature(cxx_rtti))
#elif MIJIN_COMPILER == MIJIN_COMPILER_MSVC
#if defined(_CPPRTTI)
#define MIJIN_RTTI 1
#else
#define MIJIN_RTTI 0
#endif
#else
#define MIJIN_RTTI 0
#endif
#endif
//
// public constants
//

View File

@ -0,0 +1,67 @@
#pragma once
#if !defined(MIJIN_LOGGING_DEBUG_OUTPUT_SINK_HPP_INCLUDED)
#define MIJIN_LOGGING_DEBUG_OUTPUT_SINK_HPP_INCLUDED 1
#include "./formatting.hpp"
#include "../detect.hpp"
#include "../util/traits.hpp"
#if MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
#pragma comment(lib, "Kernel32.lib")
extern "C" void OutputDebugStringA(const char* lpOutputString);
extern "C" void OutputDebugStringW(const wchar_t* lpOutputString);
namespace mijin
{
template<MIJIN_FORMATTING_SINK_TMPL_ARGS_INIT>
requires(allocator_type<TAllocator<TChar>>)
class BaseDebugOutputSink : public BaseFormattingLogSink<MIJIN_FORMATTING_SINK_TMP_ARG_NAMES>
{
public:
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::message_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 char_t* formatted) MIJIN_NOEXCEPT override
{
if constexpr (std::is_same_v<char_t, char>)
{
OutputDebugStringA(formatted);
OutputDebugStringA("\n");
}
else if constexpr (std::is_same_v<char_t, wchar_t>)
{
OutputDebugStringW(formatted);
OutputDebugStringW(L"\n");
}
else if constexpr (sizeof(char_t) == sizeof(char))
{
// char8_t etc.
OutputDebugStringA(std::bit_cast<const char*>(formatted));
OutputDebugStringA("\n");
}
else
{
static_assert(always_false_v<char_t>, "Character type not supported.");
}
}
};
#define SINK_SET_ARGS(chr_type) chr_type, std::char_traits<chr_type>, TAllocator, TDeleter
MIJIN_DEFINE_CHAR_VERSIONS_TMPL(DebugOutputSink, MIJIN_FORMATTING_SINK_COMMON_ARGS, SINK_SET_ARGS)
#undef SINK_SET_ARGS
} // namespace mijin
#endif // MIJIN_TARGET_OS == MIJIN_OS_WINDOWS
#endif // !defined(MIJIN_LOGGING_DEBUG_OUTPUT_SINK_HPP_INCLUDED)

View File

@ -92,10 +92,10 @@ public:
using string_t = formatter_t::string_t;
using typename base_t::message_t;
private:
not_null_t<formatter_ptr_t> mFormatter;
formatter_ptr_t mFormatter;
string_t mBuffer;
public:
explicit BaseFormattingLogSink(not_null_t<formatter_ptr_t> formatter, 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))
{}

View File

@ -254,10 +254,14 @@ 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(level, channel, ...) \
#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_LOG_IF(cond, level, channel, ...) \
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_SET_CLASS_LOGGER(loggerExpr) \
const auto& MIJIN_FUNCNAME_GET_LOGGER() const noexcept \
{ \
@ -268,6 +272,11 @@ auto MIJIN_FUNCNAME_GET_LOGGER = [&]() -> const auto& \
{ \
return loggerExpr; \
};
#define MIJIN_SET_NS_LOGGER(loggerExpr) \
inline const mijin::Logger& MIJIN_FUNCNAME_GET_LOGGER() \
{ \
return loggerExpr; \
}
#define MIJIN_SET_CLASS_MIN_LOG_LEVEL_COMPILE(level) \
int MIJIN_FUNCNAME_MIN_LOG_LEVEL_COMPILE() MIJIN_NOEXCEPT \
{ \

View File

@ -22,7 +22,7 @@ public:
private:
int mMinStderrLevel = MIJIN_LOG_LEVEL_VALUE_WARNING;
public:
explicit BaseStdioSink(not_null_t<formatter_ptr_t> formatter, 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)) {}

View File

@ -35,10 +35,11 @@ public:
{
MIJIN_ASSERT((std::bit_cast<std::uintptr_t>(ptr) & 1) == 0, "Invalid address, DynamicPointer requires addresses to be divisible by two.");
}
template<typename TOther, typename TOtherDeleter> requires (std::is_constructible_v<TDeleter, TOtherDeleter&&>)
template<typename TOther, typename TOtherDeleter> requires (std::is_assignable_v<T*&, TOther*> && std::is_constructible_v<TDeleter, TOtherDeleter&&>)
constexpr DynamicPointer(DynamicPointer<TOther, TOtherDeleter>&& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_convertible_v<TOtherDeleter, TDeleter>))
: mData(std::exchange(other.mData, 0)), mDeleter(std::move(other.mDeleter)) {
MIJIN_ASSERT(other.mData == 0, "");
: DynamicPointer(other.get(), other.isOwning() ? Owning::YES : Owning::NO, TDeleter(std::move(other.mDeleter)))
{
other.mData = 0;
}
constexpr ~DynamicPointer() noexcept
{
@ -64,6 +65,15 @@ public:
return *this;
}
// template<typename TOther, typename TOtherDeleter> requires (std::is_base_of_v<TOther, T>)
// constexpr operator DynamicPointer<TOther, TOtherDeleter>() && // MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<TDeleter>>)
// {
// const Owning owning = isOwning() ? Owning::YES : Owning::NO;
// T* const ptr = release();
//
// return DynamicPointer<TOther, TOtherDeleter>(static_cast<TOther*>(ptr), owning, TOtherDeleter(std::move(mDeleter)));
// }
template<typename TOther, typename TOtherDeleter> requires(std::equality_comparable_with<T, TOther>)
auto operator<=>(const DynamicPointer<TOther, TOtherDeleter>& other) MIJIN_NOEXCEPT
{
@ -155,9 +165,9 @@ bool operator!=(std::nullptr_t, const DynamicPointer<T, TDeleter>& pointer) MIJI
}
template<typename T, typename... TArgs>
DynamicPointer<T, std::default_delete<T>> makeDynamic(TArgs&&... args) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, TArgs...>))
DynamicPointer<T> makeDynamic(TArgs&&... args) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, TArgs...>))
{
return DynamicPointer<T, std::default_delete<T>>(new T(std::forward<TArgs>(args)...), Owning::YES);
return DynamicPointer<T>(new T(std::forward<TArgs>(args)...), Owning::YES);
}
template<typename T, allocator_type_for<T> TAllocator, typename... TArgs>
@ -171,6 +181,12 @@ DynamicPointer<T, AllocatorDeleter<TAllocator>> makeDynamicWithAllocator(TAlloca
}
return DynamicPointer<T, AllocatorDeleter<TAllocator>>(obj, Owning::YES, AllocatorDeleter<TAllocator>(std::move(allocator)));
}
template<typename T>
DynamicPointer<T> wrapDynamic(T* raw)
{
return DynamicPointer<T>(raw, Owning::NO);
}
} // namespace mijin
#endif // !defined(MIJIN_MEMORY_DYNAMIC_POINTER_HPP_INCLUDED)

View File

@ -4,8 +4,11 @@
#ifndef MIJIN_UTIL_EXCEPTION_HPP_INCLUDED
#define MIJIN_UTIL_EXCEPTION_HPP_INCLUDED 1
#include <format>
#include <stdexcept>
#include <typeinfo>
#include "../detect.hpp"
#include "../debug/stacktrace.hpp"
namespace mijin
@ -73,5 +76,76 @@ void walkExceptionCause(const std::exception_ptr& cause, TFunc func)
}
}
}
template<typename TFunc>
void walkException(const std::exception& exc, TFunc func)
{
func(exc);
}
template<typename TFunc>
void walkException(const mijin::Exception& exc, TFunc func)
{
func(exc);
walkExceptionCause(exc.getCause(), func);
}
} // namespace mijin
template<typename TChar>
struct std::formatter<mijin::Exception, TChar>
{
using char_t = TChar;
template<class TContext>
constexpr TContext::iterator parse(TContext& ctx)
{
auto it = ctx.begin();
auto end = ctx.end();
if (it != end && *it != MIJIN_SMART_QUOTE(char_t, '}'))
{
throw std::format_error("invalid format");
}
return it;
}
template<typename TContext>
TContext::iterator format(const mijin::Exception& exception, TContext& ctx) const
{
using namespace std::literals;
auto it = ctx.out();
bool first = true;
mijin::walkException(exception, [&]<typename T>(const T& exc)
{
if constexpr (!std::is_same_v<T, std::nullptr_t>)
{
if (!first) {
it = std::ranges::copy(MIJIN_SMART_QUOTE(char_t, "\nCaused by:\n"sv), it).out;
}
first = false;
#if MIJIN_RTTI
it = std::ranges::copy(std::basic_string_view(typeid(exc).name()), it).out;
it = std::ranges::copy(MIJIN_SMART_QUOTE(char_t, ": "sv), it).out;
#endif
it = std::ranges::copy(std::basic_string_view(exc.what()), it).out;
if constexpr (std::is_same_v<T, mijin::Exception>)
{
if (const mijin::Result<mijin::Stacktrace>& trace = exc.getStacktrace(); trace.isSuccess())
{
*it = MIJIN_SMART_QUOTE(char_t, '\n');
++it;
it = std::format_to(it, MIJIN_SMART_QUOTE(char_t, "{}"), trace.getValue());
}
}
}
else
{
it = std::ranges::copy(MIJIN_SMART_QUOTE(char_t, "<unknown exception>"sv), it).out;
}
});
return it;
}
};
#endif // MIJIN_UTIL_EXCEPTION_HPP_INCLUDED

View File

@ -1020,6 +1020,29 @@ bool convertStringType(const TFrom* strFrom, std::basic_string<TTo, TToTraits, T
{
return convertStringType(std::basic_string_view<TFrom>(strFrom), outString);
}
template<typename TChar, typename TTraits, typename TAlloc = MIJIN_DEFAULT_ALLOCATOR<TChar>>
std::basic_string<TChar, TTraits, TAlloc> quoted(std::basic_string_view<TChar, TTraits> input)
{
std::basic_string<TChar, TTraits> result;
result.reserve(input.size() + 2);
result.push_back(TChar('"'));
for (const TChar chr : input)
{
if (chr == TChar('"') || chr == TChar('\\')) {
result.push_back(TChar('\\'));
}
result.push_back(chr);
}
result.push_back(TChar('"'));
return result;
}
template<typename TChar, typename TTraits, typename TAlloc>
std::basic_string<TChar, TTraits, TAlloc> quoted(const std::basic_string<TChar, TTraits, TAlloc>& input)
{
return quoted<TChar, TTraits, TAlloc>(std::basic_string_view(input));
}
} // namespace mijin
#endif // !defined(MIJIN_UTIL_STRING_HPP_INCLUDED)

View File

@ -192,6 +192,18 @@ struct is_type_member<TElement, TCollection<Ts...>>
template<typename TElement, typename TCollection>
constexpr bool is_type_member_v = is_type_member<TElement, TCollection>::value;
template<typename T, typename TObject>
struct is_member_object_pointer_of : std::false_type {};
template<typename TMember, typename TObject>
struct is_member_object_pointer_of<TMember (TObject::*), TObject> : std::true_type {};
template<typename T, typename TObject>
inline constexpr bool is_member_object_pointer_of_v = is_member_object_pointer_of<T, TObject>::value;
template<typename T, typename TObject>
concept member_object_pointer_of = is_member_object_pointer_of_v<T, TObject>;
template<typename TFrom, typename TTo>
using copy_const_t = std::conditional_t<std::is_const_v<TFrom>, std::add_const_t<TTo>, std::remove_const_t<TTo>>;