#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 { 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."); } 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_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_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."); constexpr NotNullable& operator=(const NotNullable& other) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v) requires(std::is_copy_constructible_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 TOther> requires(!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) 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)