165 lines
6.5 KiB
C++
165 lines
6.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.");
|
|
}
|
|
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_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.");
|
|
|
|
constexpr NotNullable& operator=(const NotNullable& other) MIJIN_NOEXCEPT_IF(std::is_nothrow_copy_constructible_v<T>)
|
|
requires(std::is_copy_constructible_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<std::equality_comparable_with<T> TOther> requires(!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<std::equality_comparable_with<T> TOther> requires(!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)
|