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++
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
|
|
}
|