You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

454 lines
15 KiB
C++

// -*- c++ -*-
#pragma once
#include <stdexcept>
#include <utility>
namespace std
{
struct nullopt_t { explicit constexpr nullopt_t(int) {} };
inline constexpr nullopt_t nullopt{0};
namespace LibshitOptionalDetail
{
// base+destructor
template <typename T, bool = std::is_trivially_destructible_v<T>>
struct Cnt0
{
union
{
char dummy;
T data;
};
bool valid = false;
constexpr Cnt0() noexcept {}
template <typename... Args>
constexpr Cnt0(std::in_place_t, Args&&... args)
: data(std::forward<Args>(args)...), valid(true) {}
void Reset() { data.~T(); valid = false; }
template <typename X, typename U>
void GenCtor(U&& o)
{
if (!o.valid) return;
new (this->buf) T(std::forward<X>(o.data));
this->valid = true;
}
template <typename X, typename U>
void GenOpEq(U&& o)
{
if (valid && !o.valid) Reset();
if (!valid && o.valid)
{
new (&data) T(std::forward<X>(o.data));
this->valid = true;
}
if (valid && o.valid) data = std::forward<X>(o.data);
}
};
template <typename T> struct Cnt0<T, false> : Cnt0<T, true>
{ ~Cnt0() noexcept { if (this->valid) this->Reset(); } };
// copy ctor
template <typename T, bool = std::is_copy_constructible_v<T>,
bool = std::is_trivially_copy_constructible_v<T>>
struct Cnt1;
template <typename T> struct Cnt1<T, true, true> : Cnt0<T>
{ using Cnt0<T>::Cnt0; };
template <typename T> struct Cnt1<T, false, false> : Cnt0<T>
{
using Cnt0<T>::Cnt0;
Cnt1(const Cnt1&) = delete;
Cnt1(Cnt1&&) = default;
Cnt1& operator=(const Cnt1&) = default;
Cnt1& operator=(Cnt1&&) = default;
};
template <typename T> struct Cnt1<T, true, false> : Cnt0<T>
{
using Cnt0<T>::Cnt0;
Cnt1(const Cnt1& o) { this->template GenCtor<const T&>(o); }
Cnt1(Cnt1&&) = default;
Cnt1& operator=(const Cnt1&) = default;
Cnt1& operator=(Cnt1&&) = default;
};
// move ctor
template <typename T, bool = std::is_move_constructible_v<T>,
bool = std::is_trivially_move_constructible_v<T>>
struct Cnt2;
template <typename T> struct Cnt2<T, true, true> : Cnt1<T>
{ using Cnt1<T>::Cnt1; };
template <typename T> struct Cnt2<T, false, false> : Cnt1<T>
{
using Cnt1<T>::Cnt1;
Cnt2(const Cnt2&) = default;
Cnt2(Cnt2&&) = delete;
Cnt2& operator=(const Cnt2&) = default;
Cnt2& operator=(Cnt2&&) = default;
};
template <typename T> struct Cnt2<T, true, false> : Cnt1<T>
{
using Cnt1<T>::Cnt1;
Cnt2(const Cnt2&) = default;
Cnt2(Cnt2&& o) noexcept(std::is_nothrow_move_constructible_v<T>)
{ this->template GenCtor<T>(std::move(o)); }
Cnt2& operator=(const Cnt2&) = default;
Cnt2& operator=(Cnt2&&) = default;
};
// copy op=
template <
typename T,
bool = std::is_copy_constructible_v<T> && std::is_copy_assignable_v<T>,
bool = std::is_trivially_copy_constructible_v<T> &&
std::is_trivially_copy_assignable_v<T> &&
std::is_trivially_destructible_v<T>>
struct Cnt3;
template <typename T> struct Cnt3<T, true, true> : Cnt2<T>
{ using Cnt2<T>::Cnt2; };
template <typename T> struct Cnt3<T, false, false> : Cnt2<T>
{
using Cnt2<T>::Cnt2;
Cnt3(const Cnt3&) = default;
Cnt3(Cnt3&&) = default;
Cnt3& operator=(const Cnt3&) = delete;
Cnt3& operator=(Cnt3&&) = default;
};
template <typename T> struct Cnt3<T, true, false> : Cnt2<T>
{
using Cnt2<T>::Cnt2;
Cnt3(const Cnt3&) = default;
Cnt3(Cnt3&&) = default;
Cnt3& operator=(const Cnt3& o)
{ this->template GenOpEq<const T&>(o); return *this; }
Cnt3& operator=(Cnt3&&) = default;
};
// move op=
template <
typename T,
bool = std::is_move_constructible_v<T> && std::is_move_assignable_v<T>,
bool = std::is_trivially_move_constructible_v<T> &&
std::is_trivially_move_assignable_v<T> &&
std::is_trivially_destructible_v<T>>
struct Cnt4;
template <typename T> struct Cnt4<T, true, true> : Cnt3<T>
{ using Cnt3<T>::Cnt3; };
template <typename T> struct Cnt4<T, false, false> : Cnt3<T>
{
using Cnt3<T>::Cnt3;
Cnt4(const Cnt4&) = default;
Cnt4(Cnt4&&) = default;
Cnt4& operator=(const Cnt4&) = default;
Cnt4& operator=(Cnt4&&) = delete;
};
template <typename T> struct Cnt4<T, true, false> : Cnt3<T>
{
using Cnt3<T>::Cnt3;
Cnt4(const Cnt4&) = default;
Cnt4(Cnt4&&) = default;
Cnt4& operator=(const Cnt4& o) = default;
Cnt4& operator=(Cnt4&& o)
noexcept(std::is_nothrow_move_assignable_v<T> &&
std::is_nothrow_move_constructible_v<T>)
{ this->template GenOpEq<T>(std::move(o)); return *this; }
};
}
struct bad_optional_access : std::exception
{
const char* what() const noexcept override
{ return "bad_exception_access"; }
};
template <typename T>
class optional : private LibshitOptionalDetail::Cnt4<T>
{
template <typename U> friend class optional;
using Base = LibshitOptionalDetail::Cnt4<T>;
template <typename U>
inline static constexpr const bool ConvCtor =
!std::is_constructible_v<T, std::optional<U>&> &&
!std::is_constructible_v<T, const std::optional<U>&> &&
!std::is_constructible_v<T, std::optional<U>&&> &&
!std::is_constructible_v<T, const std::optional<U>&&> &&
!std::is_convertible_v<std::optional<U>&, T> &&
!std::is_convertible_v<const std::optional<U>&, T> &&
!std::is_convertible_v<std::optional<U>&&, T> &&
!std::is_convertible_v<const std::optional<U>&&, T>;
template <typename U>
inline static constexpr const bool ConvOpEq = ConvCtor<U> &&
!std::is_assignable_v<T&, std::optional<U>&> &&
!std::is_assignable_v<T&, const std::optional<U>&> &&
!std::is_assignable_v<T&, std::optional<U>&&> &&
!std::is_assignable_v<T&, const std::optional<U>&&>;
template <typename U>
inline static constexpr const bool ValCtor =
std::is_constructible_v<T, U&&> &&
!std::is_same_v<std::in_place_t, std::decay_t<U>> &&
!std::is_same_v<std::optional<T>, std::decay_t<U>>;
public:
using value_type = T;
constexpr optional() noexcept {}
constexpr optional(nullopt_t) noexcept {}
constexpr optional(const optional&) = default;
constexpr optional(optional&&) = default;
template <typename U, std::enable_if_t<
std::is_constructible_v<T, const U&> && ConvCtor<U> &&
std::is_convertible_v<const U&, T>>* = nullptr>
optional(const optional<U>& o)
{ this->template GenCtor<const U&>(o); }
template <typename U, std::enable_if_t<
std::is_constructible_v<T, const U&> && ConvCtor<U> &&
!std::is_convertible_v<const U&, T>>* = nullptr>
explicit optional(const optional<U>& o)
{ this->template GenCtor<const U&>(o); }
template <typename U, std::enable_if_t<
std::is_constructible_v<T, U&&> && ConvCtor<U> &&
std::is_convertible_v<U&&, T>>* = nullptr>
optional(optional<U>&& o)
{ this->template GenCtor<U>(std::move(o)); }
template <typename U, std::enable_if_t<
std::is_constructible_v<T, U&&> && ConvCtor<U> &&
!std::is_convertible_v<U&&, T>>* = nullptr>
explicit optional(optional<U>&& o)
{ this->template GenCtor<U>(std::move(o)); }
template <typename... Args,
std::enable_if_t<std::is_constructible_v<T, Args...>>* = nullptr>
constexpr explicit optional(std::in_place_t, Args&&... args)
: Base(in_place, std::forward<Args>(args)...) {}
template <typename U, typename... Args, std::enable_if_t<
std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* = nullptr>
constexpr explicit optional(
std::in_place_t, std::initializer_list<U> list, Args&&... args)
: Base(in_place, list, std::forward<Args>(args)...) {}
template <typename U = value_type, std::enable_if_t<
ValCtor<U> && std::is_convertible_v<U&&, T>>* = nullptr>
constexpr optional(U&& value)
: Base(in_place, std::forward<U>(value)) {}
template <typename U = value_type, std::enable_if_t<
ValCtor<U> && !std::is_convertible_v<U&&, T>>* = nullptr>
explicit constexpr optional(U&& value)
: Base(in_place, std::forward<U>(value)) {}
// ---
optional& operator=(const optional&) = default;
optional& operator=(optional&&) = default;
optional& operator=(std::nullopt_t) noexcept
{ reset(); return *this; }
template <typename U = T, std::enable_if_t<
!std::is_same_v<std::decay_t<U>, std::optional<T>> &&
std::is_constructible_v<T, U> && std::is_assignable_v<T&, U> && (
!std::is_scalar_v<T> || !std::is_same_v<std::decay_t<U>, T>)>* = nullptr>
optional& operator=(U&& value)
{
if (this->valid) this->data = std::forward<U>(value);
else { new (&this->data) T(std::forward<U>(value)); this->valid = true; }
return *this;
}
template <typename U, std::enable_if_t<
ConvOpEq<U> && std::is_constructible_v<T, const U&> &&
std::is_assignable_v<T&, const U&>>* = nullptr>
optional& operator=(const optional<U>& o)
{ this->template GenOpEq<const U&>(o); return *this; }
template <typename U, std::enable_if_t<
ConvOpEq<U> && std::is_constructible_v<T, U> &&
std::is_assignable_v<T&, U>>* = nullptr>
optional& operator=(optional<U>&& o)
{ this->template GenOpEq<U>(std::move(o)); return *this; }
// ---
constexpr T* operator->() { return &this->data; }
constexpr const T* operator->() const { return &this->data; }
constexpr T& operator*() & { return this->data; }
constexpr const T& operator*() const & { return this->data; }
constexpr T&& operator*() && { return std::move(this->data); }
constexpr const T&& operator*() const && { return std::move(this->data); }
constexpr explicit operator bool() const noexcept { return this->valid; }
constexpr bool has_value() const noexcept { return this->valid; }
constexpr T& value() &
{ if (this->valid) return this->data; else throw bad_optional_access{}; }
constexpr const T& value() const &
{ if (this->valid) return this->data; else throw bad_optional_access{}; }
constexpr T&& value() &&
{
if (this->valid) return std::move(this->data);
else throw bad_optional_access{};
}
constexpr const T&& value() const &&
{
if (this->valid) return std::move(this->data);
else throw bad_optional_access{};
}
template <typename U> constexpr T value_or(U&& def) const&
{
if (this->valid) return this->data;
else return static_cast<T>(std::forward<U>(def));
}
template <typename U> constexpr T value_or(U&& def) &&
{
if (this->valid) return std::move(this->data);
else return static_cast<T>(std::forward<U>(def));
}
// ---
void swap(optional& o) noexcept(std::is_nothrow_move_constructible_v<T> &&
std::is_nothrow_swappable_v<T>)
{
if (this->valid && !o.valid)
{
o.template GenCtor<T>(std::move(this->data));
this->Reset();
}
if (!this->valid && o.valid)
{
this->template GenCtor<T>(std::move(o.data));
o.Reset();
}
if (this->valid && o.valid)
{
using std::swap;
swap(this->data, o.data);
}
}
void reset() noexcept
{
// both gcc and clang can't optimize if (value) Reset() into value = false
// with trivially destructible types, so be a bit more explicit
if constexpr (std::is_trivially_destructible_v<T>) this->valid = false;
else if (this->valid) this->Reset();
}
// ---
template <typename... Args>
T& emplace(Args&&... args)
{
reset();
new (&this->data) T(std::forward<Args>(args)...);
this->valid = true;
return this->data;
}
template <typename U, typename... Args, std::enable_if_t<
std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* = nullptr>
T& emplace(std::initializer_list<U> list, Args&&... args)
{
reset();
new (&this->data) T(list, std::forward<Args>(args)...);
this->valid = true;
return this->data;
}
};
template <typename T> optional(T) -> optional<T>;
// ---
template <typename T>
constexpr optional<std::decay_t<T>> make_optional(T&& value)
{ return optional<std::decay_t<T>>(std::forward<T>(value)); }
template <typename T, typename... Args>
constexpr optional<T> make_optional(Args&&... args)
{ return optional<T>(std::in_place, std::forward<Args>(args)...); }
template <typename T, typename U, typename... Args>
constexpr optional<T> make_optional(std::initializer_list<U> list, Args&&... args)
{ return optional<T>(std::in_place, list, std::forward<Args>(args)...); }
// ---
template <typename T, std::enable_if_t<
std::is_move_constructible_v<T> && std::is_swappable_v<T>>* = nullptr>
void swap(optional<T>& lhs, optional<T>& rhs)
noexcept(noexcept(lhs.swap(rhs)))
{ lhs.swap(rhs); }
// todo: enabled/disabled not supported
template <typename T>
struct hash<optional<T>>
{
size_t operator()(const optional<T>& o) const
{ return o ? hash<std::remove_const_t<T>>()(*o) : 0xb19b00b2; }
};
// ---
#define LIBSHIT_GEN(op) \
template <typename T> \
constexpr bool operator op(const optional<T>& a, const optional<T>& b) \
{ \
if (!a || !b) return bool(a) op bool(b); \
return *a op *b; \
} \
template <typename T> \
constexpr bool operator op(const optional<T>& o, nullopt_t) \
{ return bool(o) op false; } \
template <typename T> \
constexpr bool operator op(nullopt_t, const optional<T>& o) \
{ return false op bool(o); } \
template <typename T, typename U> \
constexpr bool operator op(const optional<T>& a, const U& b) \
{ \
if (!a) return bool(a) op true; \
return *a op b; \
} \
template <typename T, typename U> \
constexpr bool operator op(const T& a, const optional<U>& b) \
{ \
if (!b) return true op bool(b); \
return a op *b; \
}
LIBSHIT_GEN(==) LIBSHIT_GEN(!=) LIBSHIT_GEN(<) LIBSHIT_GEN(<=)
LIBSHIT_GEN(>) LIBSHIT_GEN(>=)
#undef LIBSHIT_GEN
}