mijin2/source/mijin/util/annot.hpp

147 lines
5.8 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> requires(!std::is_same_v<T, std::nullptr_t>) && requires(T t) { t == nullptr; }
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 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_; }
};
#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)