180 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			180 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| #pragma once
 | |
| 
 | |
| #if !defined(MIJIN_UTIL_ANNOT_HPP_INCLUDED)
 | |
| #define MIJIN_UTIL_ANNOT_HPP_INCLUDED 1
 | |
| 
 | |
| #include <utility>
 | |
| #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(<gsl/gsl>)
 | |
|     #define MIJIN_USE_GSL 1
 | |
| #else
 | |
|     #define MIJIN_USE_GSL 0
 | |
| #endif
 | |
| #endif // !defined(MIJIN_USE_GSL)
 | |
| 
 | |
| #include <concepts>
 | |
| #include "./concepts.hpp"
 | |
| 
 | |
| #if MIJIN_USE_GSL
 | |
|     #include <gsl/gsl>
 | |
| #endif
 | |
| 
 | |
| namespace mijin
 | |
| {
 | |
| template<typename T>
 | |
| concept nullable_type = !std::is_same_v<T, std::nullptr_t> && requires(T t) { t == nullptr; };
 | |
| 
 | |
| template<nullable_type T>
 | |
| class NotNullable
 | |
| {
 | |
| private:
 | |
|     T base_;
 | |
| public:
 | |
|     template<typename U> requires(std::is_same_v<T, U> && std::is_copy_constructible_v<T>)
 | |
|     constexpr NotNullable(U base) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v<T>)
 | |
|         : 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<T>) = default;
 | |
|     NotNullable(NotNullable&&) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<T>) = default;
 | |
| 
 | |
|     template<typename TOther> requires(std::is_constructible_v<T, const TOther&>)
 | |
|     constexpr NotNullable(const NotNullable<TOther>& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, const TOther&>))
 | |
|             : base_(other.base_)
 | |
|     {
 | |
|         MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr.");
 | |
|     }
 | |
|     template<typename TOther> requires(std::is_constructible_v<T, TOther&&>)
 | |
|     constexpr NotNullable(NotNullable<TOther>&& other) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, TOther&&>))
 | |
|             : base_(std::exchange(other.base_, nullptr))
 | |
|     {
 | |
|         MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr.");
 | |
|     }
 | |
|     template<typename TArg, typename... TArgs> requires(!std::is_same_v<TArg, std::nullptr_t>
 | |
|                                                       && (!std::is_same_v<TArg, T> && sizeof...(TArgs) == 0)
 | |
|                                                       && std::is_constructible_v<T, TArg&&, TArgs&&...>)
 | |
|     constexpr NotNullable(TArg&& arg, TArgs&&... args) MIJIN_NOEXCEPT_IF((std::is_nothrow_constructible_v<T, TArg&&, TArgs&&...>))
 | |
|         : base_(std::forward<TArg>(arg), std::forward<TArgs>(args)...)
 | |
|     {
 | |
|         MIJIN_ASSERT(base_ != nullptr, "Constructed non-nullable type with nullptr.");
 | |
|     }
 | |
|     constexpr NotNullable(T&& base) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<T>)
 | |
|                                     requires(std::is_move_constructible_v<T>)
 | |
|         : 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<T>)
 | |
|                                                requires(std::is_copy_constructible_v<T>)
 | |
|         : 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<T>)
 | |
|                                                requires(std::is_move_constructible_v<T>)
 | |
|         : 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<T>) = default;
 | |
|     NotNullable& operator=(NotNullable&&) MIJIN_NOEXCEPT_IF(std::is_nothrow_move_constructible_v<T>) = default;
 | |
| 
 | |
|     constexpr NotNullable& operator=(const NotNullable& other) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_assignable_v<T>)
 | |
|                                                                requires(std::is_copy_assignable_v<T>)
 | |
|     {
 | |
|         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<T>)
 | |
|                                                           requires(std::is_move_assignable_v<T>)
 | |
|     {
 | |
|         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<std::equality_comparable_with<T> TOther>
 | |
|     bool operator==(const NotNullable<TOther>& other) MIJIN_NOEXCEPT_IF(noexcept(std::declval<T>() == std::declval<TOther>()))
 | |
|     {
 | |
|         return base_ == other.base_;
 | |
|     }
 | |
| 
 | |
|     template<std::equality_comparable_with<T> TOther>
 | |
|     bool operator!=(const NotNullable<TOther>& other) MIJIN_NOEXCEPT_IF(noexcept(std::declval<T>() != std::declval<TOther>()))
 | |
|     {
 | |
|         return base_ != other.base_;
 | |
|     }
 | |
| 
 | |
|     template<nullable_type TOther> requires(std::equality_comparable_with<T, TOther> && !std::is_same_v<TOther, std::nullptr_t>)
 | |
|     bool operator==(const TOther& other) MIJIN_NOEXCEPT_IF(noexcept(std::declval<T>() == std::declval<TOther>()))
 | |
|     {
 | |
|         return base_ == other;
 | |
|     }
 | |
| 
 | |
|     template<nullable_type TOther> requires(std::equality_comparable_with<T, TOther> && !std::is_same_v<TOther, std::nullptr_t>)
 | |
|     bool operator!=(const TOther& other) MIJIN_NOEXCEPT_IF(noexcept(std::declval<T>() != std::declval<TOther>()))
 | |
|     {
 | |
|         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<nullable_type TOther>
 | |
|     friend class NotNullable;
 | |
| };
 | |
| 
 | |
| #if MIJIN_USE_GSL
 | |
| template<mijin::pointer_type T>
 | |
| using owner_t = gsl::owner<T>;
 | |
| #else
 | |
| template<mijin::pointer_type T>
 | |
| using owner_t = T;
 | |
| #endif
 | |
| 
 | |
| template<typename T>
 | |
| using not_null_t = NotNullable<T>;
 | |
| }
 | |
| 
 | |
| #endif // !defined(MIJIN_UTIL_ANNOT_HPP_INCLUDED)
 |