diff --git a/source/mijin/async/boxed_signal.hpp b/source/mijin/async/boxed_signal.hpp new file mode 100644 index 0000000..ef94485 --- /dev/null +++ b/source/mijin/async/boxed_signal.hpp @@ -0,0 +1,50 @@ + +#pragma once + +#if !defined(MIJIN_ASYNC_BOXED_SIGNAL_HPP_INCLUDED) +#define MIJIN_ASYNC_BOXED_SIGNAL_HPP_INCLUDED 1 + +#include "./signal.hpp" +#include "../container/boxed_object.hpp" + +namespace mijin +{ + +// +// public defines +// + +// +// public constants +// + +// +// public types +// + + +template typename TAllocator, typename... TArgs> +class BaseBoxedSignal : private BoxedObject> +{ +private: + using base_t = BoxedObject>; +public: + using base_t::construct; + using base_t::destroy; + using base_t::moveTo; + + MIJIN_BOXED_PROXY_FUNC(connect) + MIJIN_BOXED_PROXY_FUNC(disconnect) + MIJIN_BOXED_PROXY_FUNC(emit) +}; + +template +using BoxedSignal = BaseBoxedSignal; + +// +// public functions +// + +} // namespace mijin + +#endif // !defined(MIJIN_ASYNC_BOXED_SIGNAL_HPP_INCLUDED) diff --git a/source/mijin/async/coroutine.hpp b/source/mijin/async/coroutine.hpp index 06cf809..5527b48 100644 --- a/source/mijin/async/coroutine.hpp +++ b/source/mijin/async/coroutine.hpp @@ -24,6 +24,7 @@ #include "../memory/memutil.hpp" #include "../util/flag.hpp" #include "../util/iterators.hpp" +#include "../util/misc.hpp" #include "../util/traits.hpp" #if MIJIN_COROUTINE_ENABLE_DEBUG_INFO #include "../debug/stacktrace.hpp" @@ -269,7 +270,14 @@ struct TaskAllocatorTraits { return TAllocator(taskLoop->getAllocator()); } - return TAllocator(); + if constexpr (std::is_default_constructible_v>) + { + return TAllocator(); + } + else + { + MIJIN_FATAL("Could not create task allocator."); + } } }; @@ -501,6 +509,17 @@ private: template typename TAllocator2> friend class WrappedTask; }; +template +struct is_task : std::false_type {}; + +template typename TAllocator> +struct is_task> : std::true_type {}; + +template +inline constexpr bool is_task_v = is_task::value; + +template +concept task_type = is_task_v; template typename TAllocator = MIJIN_DEFAULT_ALLOCATOR> class WrappedTaskBase @@ -573,16 +592,24 @@ public: MIJIN_DEFINE_FLAG(IgnoreWaiting); using wrapped_task_t = WrappedTaskBase; - using wrapped_task_base_ptr_t = std::unique_ptr>>; + using wrapped_allocator_t = TAllocator; + using wrapped_deleter_t = AllocatorDeleter; + using wrapped_task_base_ptr_t = std::unique_ptr; struct StoredTask { + using set_future_t = std::function; wrapped_task_base_ptr_t task; - std::function setFuture; + set_future_t setFuture; std::any resultData; + + StoredTask(wrapped_task_base_ptr_t&& task_, set_future_t&& setFuture_, std::any&& resultData_) + : task(std::move(task_)), setFuture(std::move(setFuture_)), resultData(std::move(resultData_)) {} + template + StoredTask(TAllocator allocator_) : task(nullptr, wrapped_deleter_t(wrapped_allocator_t(allocator_))) {} }; using exception_handler_t = std::function; - using allocator_t = TAllocator; + using allocator_t = TAllocator; protected: using task_vector_t = std::vector>; @@ -661,7 +688,8 @@ private: std::thread::id threadId_; public: explicit BaseSimpleTaskLoop(const allocator_t& allocator = {}) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v) - : base_t(std::move(allocator)), tasks_(TAllocator(allocator_)), newTasks_(TAllocator(allocator_)) {} + : base_t(std::move(allocator)), tasks_(TAllocator(allocator_)), newTasks_(TAllocator(allocator_)), + queuedTasks_(constructArray::BUFFER_SIZE>(allocator_)) {} public: // TaskLoop implementation void transferCurrentTask(TaskLoop& otherLoop) MIJIN_NOEXCEPT override; void addStoredTask(StoredTask&& storedTask) MIJIN_NOEXCEPT override; @@ -778,7 +806,7 @@ FuturePtr TaskLoop::addTaskImpl(TaskBase>(TAllocator>(allocator_)); + FuturePtr future = std::allocate_shared>(TAllocator>(allocator_), allocator_); auto setFuture = &setFutureHelper; if (outHandle != nullptr) @@ -787,11 +815,8 @@ FuturePtr TaskLoop::addTaskImpl(TaskBase>>(allocator_), std::move(task)), - .setFuture = setFuture, - .resultData = future - }); + TAllocator>> allocator(allocator_); + addStoredTask(StoredTask(wrapTask(std::move(allocator), std::move(task)), std::move(setFuture), std::move(future))); return future; } diff --git a/source/mijin/async/future.hpp b/source/mijin/async/future.hpp index ec59bbc..c554873 100644 --- a/source/mijin/async/future.hpp +++ b/source/mijin/async/future.hpp @@ -26,7 +26,7 @@ namespace mijin // // public types // -template +template typename TAllocator = MIJIN_DEFAULT_ALLOCATOR> class Future; // TODO: add support for mutexes and waiting for futures @@ -57,16 +57,17 @@ struct FutureStorage }; } // namespace impl -template +template typename TAllocator> class Future { private: - impl::FutureStorage value_; + [[no_unique_address]] impl::FutureStorage value_; bool isSet_ = false; public: Future() = default; Future(const Future&) = delete; Future(Future&&) MIJIN_NOEXCEPT = default; + explicit Future(TAllocator allocator) : sigSet(std::move(allocator)) {} public: Future& operator=(const Future&) = delete; Future& operator=(Future&&) MIJIN_NOEXCEPT = default; @@ -126,11 +127,11 @@ public: // modification } } public: // signals - Signal<> sigSet; + BaseSignal sigSet; }; -template -using FuturePtr = std::shared_ptr>; +template typename TAllocator = MIJIN_DEFAULT_ALLOCATOR> +using FuturePtr = std::shared_ptr>; // // public functions diff --git a/source/mijin/async/message_queue.hpp b/source/mijin/async/message_queue.hpp index 67a3cc0..d6e04cc 100644 --- a/source/mijin/async/message_queue.hpp +++ b/source/mijin/async/message_queue.hpp @@ -79,15 +79,20 @@ class MessageQueue public: using message_t = TMessage; using iterator_t = MessageQueueIterator>; + static constexpr std::size_t BUFFER_SIZE = bufferSize; private: - std::array messages; - mijin::BitArray messageReady; - std::atomic_uint writePos = 0; - std::atomic_uint readPos = 0; + std::array messages_; + mijin::BitArray messageReady_; + std::atomic_uint writePos_ = 0; + std::atomic_uint readPos_ = 0; public: MessageQueue() = default; MessageQueue(const MessageQueue&) = delete; MessageQueue(MessageQueue&&) = delete; + explicit MessageQueue(const std::array& messages) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v) + : messages_(messages) {} + explicit MessageQueue(std::array&& messages) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v) + : messages_(std::move(messages)) {} MessageQueue& operator=(const MessageQueue&) = delete; MessageQueue& operator=(MessageQueue&&) = delete; @@ -118,22 +123,22 @@ struct TaskMessageQueue template bool MessageQueue::tryPushMaybeMove(TMessage& message) { - unsigned oldWritePos = writePos.load(std::memory_order_relaxed); + unsigned oldWritePos = writePos_.load(std::memory_order_relaxed); unsigned newWritePos = 0; do { newWritePos = (oldWritePos + 1) % bufferSize; - if (newWritePos == readPos) { + if (newWritePos == readPos_) { return false; } - } while (!writePos.compare_exchange_weak(oldWritePos, newWritePos, std::memory_order_release, std::memory_order_relaxed)); + } while (!writePos_.compare_exchange_weak(oldWritePos, newWritePos, std::memory_order_release, std::memory_order_relaxed)); - while (messageReady.get(oldWritePos)) { + while (messageReady_.get(oldWritePos)) { std::this_thread::yield(); // someone is still reading, wait... } - messages[oldWritePos] = std::move(message); - messageReady.set(oldWritePos, true); + messages_[oldWritePos] = std::move(message); + messageReady_.set(oldWritePos, true); return true; } @@ -149,22 +154,22 @@ void MessageQueue::push(TMessage message) template std::optional MessageQueue::tryPop() { - unsigned oldReadPos = readPos.load(std::memory_order_relaxed); + unsigned oldReadPos = readPos_.load(std::memory_order_relaxed); unsigned newReadPos = 0; do { - if (oldReadPos == writePos) { + if (oldReadPos == writePos_) { return std::nullopt; } newReadPos = (oldReadPos + 1) % bufferSize; - } while (!readPos.compare_exchange_weak(oldReadPos, newReadPos, std::memory_order_release, std::memory_order_relaxed)); + } while (!readPos_.compare_exchange_weak(oldReadPos, newReadPos, std::memory_order_release, std::memory_order_relaxed)); - while (!messageReady.get(oldReadPos)) { + while (!messageReady_.get(oldReadPos)) { std::this_thread::yield(); // no harm in busy-waiting here, should be fast }; - TMessage message = std::move(messages[oldReadPos]); - messageReady.set(oldReadPos, false); + TMessage message = std::move(messages_[oldReadPos]); + messageReady_.set(oldReadPos, false); return message; } diff --git a/source/mijin/async/signal.hpp b/source/mijin/async/signal.hpp index 382f352..75e6ffa 100644 --- a/source/mijin/async/signal.hpp +++ b/source/mijin/async/signal.hpp @@ -56,12 +56,6 @@ public: explicit BaseSignal(TAllocator allocator = {}) : handlers_(TAllocator(std::move(allocator))) {} BaseSignal(const BaseSignal&) = delete; BaseSignal(BaseSignal&&) MIJIN_NOEXCEPT = default; - - void reinit(TAllocator allocator = {}) - { - MIJIN_ASSERT(handlers_.empty(), "Attempting to re-initialize a signal that already has handlers."); - handlers_ = handler_vector_t(TAllocator(std::move(allocator))); - } public: BaseSignal& operator=(const BaseSignal&) = delete; BaseSignal& operator=(BaseSignal&&) MIJIN_NOEXCEPT = default; diff --git a/source/mijin/container/boxed_object.hpp b/source/mijin/container/boxed_object.hpp index c4a9d26..d4bb191 100644 --- a/source/mijin/container/boxed_object.hpp +++ b/source/mijin/container/boxed_object.hpp @@ -16,8 +16,26 @@ namespace mijin // #if !defined(MIJIN_BOXED_OBJECT_DEBUG) +#if defined(MIJIN_DEBUG) #define MIJIN_BOXED_OBJECT_DEBUG MIJIN_DEBUG +#else +#define MIJIN_BOXED_OBJECT_DEBUG 0 #endif +#endif // !defined(MIJIN_BOXED_OBJECT_DEBUG) + +#define MIJIN_BOXED_PROXY_FUNC(funcname) \ +template \ +decltype(auto) funcname(TFuncArgs&&... args) \ +{ \ + return base_t::get().funcname(std::forward(args)...); \ +} + +#define MIJIN_BOXED_PROXY_FUNC_CONST(funcname) \ +template \ +decltype(auto) funcname(TFuncArgs&&... args) const \ +{ \ + return base_t::get().funcname(std::forward(args)...); \ +} // // public constants diff --git a/source/mijin/debug/stacktrace.cpp b/source/mijin/debug/stacktrace.cpp index 567e1e7..d9a4d00 100644 --- a/source/mijin/debug/stacktrace.cpp +++ b/source/mijin/debug/stacktrace.cpp @@ -100,7 +100,7 @@ Result captureStacktrace(unsigned skipFrames) MIJIN_NOEXCEPT return Stacktrace(std::move(btData.stackframes)); #else // MIJIN_USE_LIBBACKTRACE (void) skipFrames; - return {}; // TODO + return ResultError("not implemented"); #endif // MIJIN_USE_LIBBACKTRACE } diff --git a/source/mijin/logging/formatting.hpp b/source/mijin/logging/formatting.hpp index 09df423..ad3a6f8 100644 --- a/source/mijin/logging/formatting.hpp +++ b/source/mijin/logging/formatting.hpp @@ -4,7 +4,6 @@ #if !defined(MIJIN_LOGGING_FORMATTING_HPP_INCLUDED) #define MIJIN_LOGGING_FORMATTING_HPP_INCLUDED 1 -#include #include #include #include "./logger.hpp" diff --git a/source/mijin/logging/logger.hpp b/source/mijin/logging/logger.hpp index a40ff1d..d2db264 100644 --- a/source/mijin/logging/logger.hpp +++ b/source/mijin/logging/logger.hpp @@ -208,7 +208,7 @@ inline constexpr int MIN_LOG_LEVEL_COMPILE = MIJIN_LOG_LEVEL_VALUE_DEBUG; inline constexpr int MIN_LOG_LEVEL_COMPILE = MIJIN_LOG_LEVEL_VALUE_VERBOSE; #endif -#define MIJIN_DEFINE_DEFAULT_LOG_EVELS \ +#define MIJIN_DEFINE_DEFAULT_LOG_LEVELS \ 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) \ diff --git a/source/mijin/memory/stack_allocator.hpp b/source/mijin/memory/stack_allocator.hpp index 33b8ca7..84e65d8 100644 --- a/source/mijin/memory/stack_allocator.hpp +++ b/source/mijin/memory/stack_allocator.hpp @@ -9,10 +9,24 @@ #include "../util/align.hpp" #include "../util/traits.hpp" +#if !defined(MIJIN_STACK_ALLOCATOR_DEBUG) +#if defined(MIJIN_DEBUG) +#define MIJIN_STACK_ALLOCATOR_DEBUG 1 +#else +#define MIJIN_STACK_ALLOCATOR_DEBUG 0 +#endif +#endif // !defined(MIJIN_STACK_ALLOCATOR_DEBUG) + +#if MIJIN_STACK_ALLOCATOR_DEBUG > 1 +#include +#include +#include "../debug/stacktrace.hpp" +#endif + namespace mijin { template -struct StlStackAllocator +class StlStackAllocator { public: using value_type = TValue; @@ -21,7 +35,7 @@ private: public: explicit StlStackAllocator(TStackAllocator& base) MIJIN_NOEXCEPT : base_(&base) {} template - StlStackAllocator(const StlStackAllocator& other) MIJIN_NOEXCEPT : base_(other.base) {} + StlStackAllocator(const StlStackAllocator& other) MIJIN_NOEXCEPT : base_(other.base_) {} template StlStackAllocator& operator=(const StlStackAllocator& other) MIJIN_NOEXCEPT @@ -35,10 +49,35 @@ public: [[nodiscard]] TValue* allocate(std::size_t count) const { - return static_cast(base_->allocate(alignof(TValue), count * sizeof(TValue))); + void* result = base_->allocate(alignof(TValue), count * sizeof(TValue)); +#if MIJIN_STACK_ALLOCATOR_DEBUG == 1 + ++base_->numAllocations_; +#elif MIJIN_STACK_ALLOCATOR_DEBUG > 1 + base_->activeAllocations_.emplace(result, captureStacktrace(1)); +#endif + return static_cast(result); } - void deallocate(TValue* /* ptr */, std::size_t /* count */) const MIJIN_NOEXCEPT {} + void deallocate([[maybe_unused]] TValue* ptr, std::size_t /* count */) const MIJIN_NOEXCEPT + { +#if MIJIN_STACK_ALLOCATOR_DEBUG == 1 + MIJIN_ASSERT(base_->numAllocations_ > 0, "Unbalanced allocations in stack allocators!"); + --base_->numAllocations_; +#elif MIJIN_STACK_ALLOCATOR_DEBUG > 1 + auto it = base_->activeAllocations_.find(ptr); + if (it != base_->activeAllocations_.end()) + { + base_->activeAllocations_.erase(it); + } + else + { + MIJIN_ERROR("Deallocating invalid pointer from StackAllocator."); + } +#endif + } + + template + friend class StlStackAllocator; }; template typename TBacking = MIJIN_DEFAULT_ALLOCATOR> requires (allocator_tmpl) @@ -59,6 +98,12 @@ private: }; [[no_unique_address]] TBacking backing_; Chunk* firstChunk_ = nullptr; +#if MIJIN_STACK_ALLOCATOR_DEBUG == 1 + std::size_t numAllocations_ = 0; +#elif MIJIN_STACK_ALLOCATOR_DEBUG > 1 + // just for debugging, so we don't care what memory this uses... + std::unordered_map> activeAllocations_; +#endif public: StackAllocator() MIJIN_NOEXCEPT_IF(std::is_nothrow_default_constructible_v) = default; explicit StackAllocator(backing_t backing) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v, backing_t&&>)) @@ -146,6 +191,26 @@ public: void reset() noexcept { +#if MIJIN_STACK_ALLOCATOR_DEBUG == 1 + MIJIN_ASSERT(numAllocations_ == 0, "Missing deallocation in StackAllocator!"); +#elif MIJIN_STACK_ALLOCATOR_DEBUG > 1 + if (!activeAllocations_.empty()) + { + std::println(stderr, "{} active allocations in StackAllocator when resetting!", activeAllocations_.size()); + for (const auto& [ptr, stack] : activeAllocations_) + { + if (stack.isError()) + { + std::println(stderr, "at {}, no stacktrace ({})", ptr, stack.getError().message); + } + else + { + std::println(stderr, "at 0x{}:\n{}", ptr, stack.getValue()); + } + } + MIJIN_TRAP(); + } +#endif for (Chunk* chunk = firstChunk_; chunk != nullptr; chunk = chunk->next) { chunk->allocated = 0; @@ -186,6 +251,9 @@ private: newChunk->next = firstChunk_; firstChunk_ = newChunk; } + + template + friend class StlStackAllocator; }; } // namespace mijin diff --git a/source/mijin/util/annot.hpp b/source/mijin/util/annot.hpp index c6061df..adc406b 100644 --- a/source/mijin/util/annot.hpp +++ b/source/mijin/util/annot.hpp @@ -44,6 +44,11 @@ public: { MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr."); } + + // some compilers apparently need this since they are unable to do proper pattern matching ... + NotNullable(const NotNullable&) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v) = default; + NotNullable(NotNullable&&) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v) = default; + template requires(std::is_constructible_v) constexpr NotNullable(const NotNullable& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v)) : base_(other.base_) @@ -70,6 +75,12 @@ public: { MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr."); } + constexpr NotNullable(NotNullable&& other) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v) + requires(std::is_copy_constructible_v) + : base_(other.base_) + { + MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr."); + } constexpr NotNullable(NotNullable&& other) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v) requires(std::is_move_constructible_v) : base_(std::exchange(other.base_, nullptr)) @@ -78,8 +89,12 @@ public: } constexpr NotNullable(std::nullptr_t) MIJIN_DELETE("Type is not nullable."); - constexpr NotNullable& operator=(const NotNullable& other) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v) - requires(std::is_copy_constructible_v) + // some compilers apparently need this since they are unable to do proper pattern matching ... + NotNullable& operator=(const NotNullable&) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v) = default; + NotNullable& operator=(NotNullable&&) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v) = default; + + constexpr NotNullable& operator=(const NotNullable& other) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_assignable_v) + requires(std::is_copy_assignable_v) { if (this != &other) { @@ -114,13 +129,13 @@ public: return base_ != other.base_; } - template TOther> requires(!std::is_same_v) + template requires(std::equality_comparable_with && !std::is_same_v) bool operator==(const TOther& other) MIJIN_NOEXCEPT_IF(noexcept(std::declval() == std::declval())) { return base_ == other; } - template TOther> requires(!std::is_same_v) + template requires(std::equality_comparable_with && !std::is_same_v) bool operator!=(const TOther& other) MIJIN_NOEXCEPT_IF(noexcept(std::declval() != std::declval())) { return base_ != other; diff --git a/source/mijin/util/misc.hpp b/source/mijin/util/misc.hpp new file mode 100644 index 0000000..e8bb82d --- /dev/null +++ b/source/mijin/util/misc.hpp @@ -0,0 +1,42 @@ + +#pragma once + +#if !defined(MIJIN_UTIL_MISC_HPP_INCLUDED) +#define MIJIN_UTIL_MISC_HPP_INCLUDED 1 + +#include +#include + +namespace mijin +{ + +// +// public functions +// + +template +constexpr decltype(auto) idValue(T&& value) +{ + return std::forward(value); +} + +namespace impl +{ +template +struct ConstructArrayHelper +{ + template + static constexpr std::array construct(const TArgs&... args, std::index_sequence) + { + return {idValue(T(args...))...}; + } +}; +} + +template +constexpr std::array constructArray(const TArgs&... args) +{ + return impl::ConstructArrayHelper::construct(args..., std::make_index_sequence()); +} +} +#endif // !defined(MIJIN_UTIL_MISC_HPP_INCLUDED) diff --git a/source/mijin/util/os.cpp b/source/mijin/util/os.cpp index ec0b0a8..973268e 100644 --- a/source/mijin/util/os.cpp +++ b/source/mijin/util/os.cpp @@ -145,7 +145,7 @@ std::string getExecutablePath() MIJIN_NOEXCEPT void* alignedAlloc(std::size_t alignment, std::size_t size) MIJIN_NOEXCEPT { #if MIJIN_TARGET_OS == MIJIN_OS_WINDOWS - return _aligned_alloc(size, alignment); + return _aligned_malloc(size, alignment); #else return std::aligned_alloc(alignment, size); #endif diff --git a/source/mijin/util/traits.hpp b/source/mijin/util/traits.hpp index 1699fbb..bddd0c0 100644 --- a/source/mijin/util/traits.hpp +++ b/source/mijin/util/traits.hpp @@ -21,6 +21,9 @@ namespace mijin // public types // +struct Type_; // use as typevar +struct Any_; // use as placeholder in templates + template struct always_false { @@ -95,11 +98,88 @@ struct map_template { using type_t = TTemplate>; }; -template -struct is_any_type : std::disjunction...> {}; +template typename TTemplate, typename TType> +struct is_template_instance : std::false_type {}; + +template typename TTemplate, typename... TArgs> +struct is_template_instance> : std::true_type {}; + +template typename TTemplate, typename TType> +constexpr bool is_template_instance_v = is_template_instance::value; + +namespace impl +{ +template typename TTemplate, typename... TArgsTTmpl> +struct tmpl_param_comparator +{ + template + static constexpr bool compare_single = std::is_same_v or std::is_same_v; + + template + struct matches : std::false_type {}; + + // original (which MSVC didn't like for some reason :/) + // template + // struct matches> : std::bool_constant<(compare_single && ...)> {}; + + template typename TOtherTemplate, typename... TArgsInstance> requires(is_template_instance_v>) + struct matches> : std::bool_constant<(compare_single && ...)> {}; + + template + using matches_t = matches; +}; +} + +template +struct match_template_type : std::false_type {}; + +template typename TTemplate, typename TType, typename... TArgs> +struct match_template_type, TType> : impl::tmpl_param_comparator::template matches_t {}; + +template +constexpr bool match_template_type_v = match_template_type::value; + +// similar to std::is_same, but allows placeholders +// - is_type_or_impl, some_type> resolves to some_tmpl +// - is_type_or_impl, some_type> checks if some_type is an instance of some_tmpl with int as 2nd parameter +template +struct type_matches +{ + using type = std::is_same; +}; + +template typename TTmplMatch, typename TConcrete> +struct type_matches, TConcrete> +{ + using type = TTmplMatch; +}; + +template typename TTmplMatch, typename TConcrete, typename... TArgs> +struct type_matches, TConcrete> +{ + using type = match_template_type, TConcrete>; +}; + +template +using type_matches_t = type_matches::type; + +template +inline constexpr bool type_matches_v = type_matches_t::value; + +template +struct is_any_type : std::disjunction...> {}; + +template +static constexpr bool is_any_type_v = is_any_type::value; + +template +struct match_any_type : std::disjunction...> {}; + +template +static constexpr bool match_any_type_v = match_any_type::value; template -static constexpr bool is_any_type_v = is_any_type::value; +concept union_type = match_any_type_v; template struct is_type_member; @@ -139,15 +219,6 @@ struct TypeAtHelper<0, TArg, TArgs...> template using type_at_t = TypeAtHelper::type_t; -template typename TTemplate, typename TType> -struct is_template_instance : std::false_type {}; - -template typename TTemplate, typename... TArgs> -struct is_template_instance> : std::true_type {}; - -template typename TTemplate, typename TType> -constexpr bool is_template_instance_v = is_template_instance::value; - template typename TOper, typename... TArgs> struct detect_or { @@ -192,6 +263,34 @@ decltype(auto) delayEvaluation(TType&& value) return static_cast(value); } +#define MIJIN_STATIC_TESTS 1 +#if MIJIN_STATIC_TESTS +namespace test +{ +template +struct MyTemplate {}; + +static_assert(match_template_type_v, MyTemplate>); +static_assert(match_template_type_v, MyTemplate>); +static_assert(type_matches_v, MyTemplate>); +static_assert(type_matches_v, MyTemplate>); +static_assert(!type_matches_v, MyTemplate>); +static_assert(type_matches_v, MyTemplate>); +static_assert(type_matches_v, void*>); + +static_assert(union_type); +static_assert(!union_type); +static_assert(!union_type); +static_assert(union_type, MyTemplate>); +static_assert(union_type, MyTemplate>); +static_assert(union_type, MyTemplate>); +static_assert(union_type, MyTemplate, MyTemplate>); +static_assert(!union_type); +static_assert(union_type>); +static_assert(!union_type>); +} +#endif + } // namespace mijin #endif // !defined(MIJIN_UTIL_TRAITS_HPP_INCLUDED)