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)
 |