#pragma once #if !defined(MIJIN_UTIL_ANNOT_HPP_INCLUDED) #define MIJIN_UTIL_ANNOT_HPP_INCLUDED 1 #include #include "../internal/common.hpp" #include "../debug/assert.hpp" #if !defined(__has_include) #define __has_include(x) (false) #endif #if !defined(MIJIN_USE_GSL) #if __has_include() #define MIJIN_USE_GSL 1 #else #define MIJIN_USE_GSL 0 #endif #endif // !defined(MIJIN_USE_GSL) #include #include "./concepts.hpp" #if MIJIN_USE_GSL #include #endif namespace mijin { template concept nullable_type = !std::is_same_v && requires(T t) { t == nullptr; }; template class NotNullable { public: using wrapped_t = T; using element_type = std::remove_reference_t())>; using pointer = element_type*; private: T base_; public: template requires(std::is_same_v && std::is_copy_constructible_v) constexpr NotNullable(U base) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v) : base_(base) { 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_) { MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr."); } template requires(std::is_base_of_v::element_type, element_type> && std::is_constructible_v) explicit constexpr NotNullable(const NotNullable& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v)) : base_(static_cast(&*other)) { MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr."); } template requires(std::is_constructible_v) constexpr NotNullable(NotNullable&& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v)) : base_(std::exchange(other.base_, nullptr)) { MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr."); } template requires(!std::is_same_v && (!std::is_same_v && sizeof...(TArgs) == 0) && std::is_constructible_v) constexpr NotNullable(TArg&& arg, TArgs&&... args) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v)) : base_(std::forward(arg), std::forward(args)...) { MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr."); } constexpr NotNullable(T&& base) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v) requires(std::is_move_constructible_v) : base_(std::move(base)) { 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)) { MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr."); } constexpr NotNullable(std::nullptr_t) MIJIN_DELETE("Type is not nullable."); // 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) { this->base_ = other.base_; } MIJIN_ASSERT(base_ != nullptr, "Assigned nullptr to non-nullable type."); // might still happen if the other type was moved from return *this; } constexpr NotNullable& operator=(NotNullable&& other) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_assignable_v) requires(std::is_move_assignable_v) { if (this != &other) { this->base_ = std::exchange(other.base_, nullptr); } MIJIN_ASSERT(base_ != nullptr, "Assigned nullptr to non-nullable type."); // might still happen if the other type was moved from return *this; } constexpr NotNullable& operator=(std::nullptr_t) MIJIN_DELETE("Type is not nullable."); template TOther> bool operator==(const NotNullable& other) MIJIN_NOEXCEPT_IF(noexcept(std::declval() == std::declval())) { return base_ == other.base_; } template TOther> bool operator!=(const NotNullable& other) MIJIN_NOEXCEPT_IF(noexcept(std::declval() != std::declval())) { return base_ != other.base_; } 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 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; } bool operator==(std::nullptr_t) MIJIN_DELETE("Type is not nullable."); bool operator!=(std::nullptr_t) MIJIN_DELETE("Type is not nullable."); constexpr operator const T&() const MIJIN_NOEXCEPT { return get(); } constexpr operator std::nullptr_t() const MIJIN_DELETE("Type is not nullable."); constexpr const T& operator->() const MIJIN_NOEXCEPT { return get(); } constexpr decltype(auto) operator*() const MIJIN_NOEXCEPT_IF(noexcept(*get())) { return *get(); } NotNullable& operator++() MIJIN_DELETE("Operator disabled for non-nullable types."); NotNullable& operator--() MIJIN_DELETE("Operator disabled for non-nullable types."); NotNullable operator++(int) MIJIN_DELETE("Operator disabled for non-nullable types."); NotNullable operator--(int) MIJIN_DELETE("Operator disabled for non-nullable types."); NotNullable& operator+=(std::ptrdiff_t) MIJIN_DELETE("Operator disabled for non-nullable types."); NotNullable& operator-=(std::ptrdiff_t) MIJIN_DELETE("Operator disabled for non-nullable types."); void operator[](std::ptrdiff_t) const MIJIN_DELETE("Operator disabled for non-nullable types."); [[nodiscard]] constexpr const T& get() const MIJIN_NOEXCEPT { return base_; } template friend class NotNullable; }; #if MIJIN_USE_GSL template using owner_t = gsl::owner; #else template using owner_t = T; #endif template using not_null_t = NotNullable; } #endif // !defined(MIJIN_UTIL_ANNOT_HPP_INCLUDED)