diff options
Diffstat (limited to 'asl/option.hpp')
-rw-r--r-- | asl/option.hpp | 1016 |
1 files changed, 508 insertions, 508 deletions
diff --git a/asl/option.hpp b/asl/option.hpp index f7a3eab..d12d4ce 100644 --- a/asl/option.hpp +++ b/asl/option.hpp @@ -1,509 +1,509 @@ -#pragma once
-
-#include "asl/assert.hpp"
-#include "asl/meta.hpp"
-#include "asl/maybe_uninit.hpp"
-#include "asl/functional.hpp"
-#include "asl/annotations.hpp"
-#include "asl/hash.hpp"
-
-namespace asl
-{
-
-struct nullopt_t {};
-static constexpr nullopt_t nullopt{};
-
-// @Todo(option) Reference
-// @Todo(option) Function
-// @Todo(option) Arrays
-
-template<is_object T> class option;
-
-namespace option_internal
-{
-
-template<typename T, typename U>
-concept not_constructible_from_option =
- !constructible_from<T, option<U>&> &&
- !constructible_from<T, const option<U>&> &&
- !constructible_from<T, option<U>&&> &&
- !constructible_from<T, const option<U>&&>;
-
-template<typename T, typename U>
-concept not_assignable_from_option =
- !assignable_from<T&, option<U>&> &&
- !assignable_from<T&, const option<U>&> &&
- !assignable_from<T&, option<U>&&> &&
- !assignable_from<T&, const option<U>&&>;
-
-template<typename T, typename U>
-concept not_constructible_assignable_from_option =
- not_constructible_from_option<T, U> &&
- not_assignable_from_option<T, U>;
-
-} // namespace option_internal
-
-template<typename T>
-concept is_option = requires
-{
- typename T::type;
- requires same_as<un_cvref_t<T>, option<typename T::type>>;
-};
-
-template<is_object T>
-class option
-{
- static constexpr bool kHasNiche = has_niche<T>;
-
- using HasValueMarker = select_t<kHasNiche, empty, bool>;
-
- maybe_uninit<T> m_payload{};
- ASL_NO_UNIQUE_ADDRESS HasValueMarker m_has_value{};
-
- template<is_object U>
- friend class option;
-
- template<typename... Args>
- constexpr void construct(Args&&... args)
- {
- ASL_ASSERT(!has_value());
-
- if constexpr (!kHasNiche)
- {
- m_payload.construct_unsafe(ASL_FWD(args)...);
- m_has_value = true;
- }
- else
- {
- if constexpr (move_assignable<T>)
- {
- m_payload.assign_unsafe(ASL_MOVE(T{ASL_FWD(args)...}));
- }
- else
- {
- m_payload.destroy_unsafe();
- m_payload.construct_unsafe(ASL_FWD(args)...);
- }
- }
- }
-
- template<typename U>
- constexpr void assign(U&& arg)
- {
- ASL_ASSERT(has_value());
- m_payload.assign_unsafe(ASL_FWD(arg));
- }
-
-public:
- using type = T;
-
- constexpr option() : option{nullopt} {}
-
- // NOLINTNEXTLINE(*-explicit-conversions)
- constexpr option(nullopt_t) requires (!kHasNiche) {}
-
- // NOLINTNEXTLINE(*-explicit-conversions)
- constexpr option(nullopt_t) requires kHasNiche : m_payload{in_place, niche_t{}} {}
-
- template<typename U = T>
- constexpr explicit (!convertible_from<T, U&&>)
- option(U&& value)
- requires (
- kHasNiche &&
- constructible_from<T, U&&> &&
- !same_as<un_cvref_t<U>, option>
- )
- : m_payload{in_place, ASL_FWD(value)}
- {}
-
- template<typename U = T>
- constexpr explicit (!convertible_from<T, U&&>)
- option(U&& value)
- requires (
- !kHasNiche &&
- constructible_from<T, U&&> &&
- !is_option<U>
- )
- : m_payload{in_place, ASL_FWD(value)}
- , m_has_value{true}
- {}
-
- constexpr option(const option& other) requires trivially_copy_constructible<T> = default;
- constexpr option(const option& other) requires (!copy_constructible<T>) = delete;
-
- constexpr option(const option& other)
- requires copy_constructible<T> && (!trivially_copy_constructible<T>)
- : option{nullopt}
- {
- if (other.has_value())
- {
- construct(other.m_payload.as_init_unsafe());
- }
- }
-
- constexpr option(option&& other) requires trivially_move_constructible<T> = default;
- constexpr option(option&& other) requires (!move_constructible<T>) = delete;
-
- constexpr option(option&& other)
- requires move_constructible<T> && (!trivially_move_constructible<T>)
- : option{nullopt}
- {
- if (other.has_value())
- {
- construct(ASL_MOVE(other.m_payload.as_init_unsafe()));
- }
- }
-
- template<typename U>
- constexpr explicit (!convertible_from<T, const U&>)
- option(const option<U>& other)
- requires (
- constructible_from<T, const U&> &&
- option_internal::not_constructible_from_option<T, U>
- )
- : option{nullopt}
- {
- if (other.has_value())
- {
- construct(other.m_payload.as_init_unsafe());
- }
- }
-
- template<typename U>
- constexpr explicit (!convertible_from<T, U&&>)
- option(option<U>&& other)
- requires (
- constructible_from<T, U&&> &&
- option_internal::not_constructible_from_option<T, U>
- )
- : option{nullopt}
- {
- if (other.has_value())
- {
- construct(ASL_MOVE(other.m_payload.as_init_unsafe()));
- }
- }
-
- constexpr option& operator=(nullopt_t) &
- {
- reset();
- return *this;
- }
-
- template<typename U = T>
- constexpr option& operator=(U&& value) &
- requires (
- assignable_from<T&, U&&> &&
- constructible_from<T, U&&> &&
- !is_option<U>
- )
- {
- if (has_value())
- {
- assign(ASL_FWD(value));
- }
- else
- {
- construct(ASL_FWD(value));
- }
-
- return *this;
- }
-
- constexpr option& operator=(const option& other) &
- requires (!copy_assignable<T>) = delete;
-
- constexpr option& operator=(const option& other) &
- requires trivially_copy_assignable<T> = default;
-
- constexpr option& operator=(const option& other) &
- requires copy_assignable<T> && (!trivially_copy_constructible<T>)
- {
- if (&other == this) { return *this; }
-
- if (other.has_value())
- {
- if (has_value())
- {
- assign(other.m_payload.as_init_unsafe());
- }
- else
- {
- construct(other.m_payload.as_init_unsafe());
- }
- }
- else if (has_value())
- {
- reset();
- }
-
- return *this;
- }
-
- constexpr option& operator=(option&& other) &
- requires (!move_assignable<T>) = delete;
-
- constexpr option& operator=(option&& other) &
- requires trivially_move_assignable<T> = default;
-
- constexpr option& operator=(option&& other) &
- requires move_assignable<T> && (!trivially_move_constructible<T>)
- {
- if (&other == this) { return *this; }
-
- if (other.has_value())
- {
- if (has_value())
- {
- assign(ASL_MOVE(other.m_payload.as_init_unsafe()));
- }
- else
- {
- construct(ASL_MOVE(other.m_payload.as_init_unsafe()));
- }
- }
- else if (has_value())
- {
- reset();
- }
-
- return *this;
- }
-
- template<typename U = T>
- constexpr option& operator=(const option<U>& other) &
- requires (
- constructible_from<T, const U&> &&
- assignable_from<T&, const U&> &&
+#pragma once + +#include "asl/assert.hpp" +#include "asl/meta.hpp" +#include "asl/maybe_uninit.hpp" +#include "asl/functional.hpp" +#include "asl/annotations.hpp" +#include "asl/hash.hpp" + +namespace asl +{ + +struct nullopt_t {}; +static constexpr nullopt_t nullopt{}; + +// @Todo(option) Reference +// @Todo(option) Function +// @Todo(option) Arrays + +template<is_object T> class option; + +namespace option_internal +{ + +template<typename T, typename U> +concept not_constructible_from_option = + !constructible_from<T, option<U>&> && + !constructible_from<T, const option<U>&> && + !constructible_from<T, option<U>&&> && + !constructible_from<T, const option<U>&&>; + +template<typename T, typename U> +concept not_assignable_from_option = + !assignable_from<T&, option<U>&> && + !assignable_from<T&, const option<U>&> && + !assignable_from<T&, option<U>&&> && + !assignable_from<T&, const option<U>&&>; + +template<typename T, typename U> +concept not_constructible_assignable_from_option = + not_constructible_from_option<T, U> && + not_assignable_from_option<T, U>; + +} // namespace option_internal + +template<typename T> +concept is_option = requires +{ + typename T::type; + requires same_as<un_cvref_t<T>, option<typename T::type>>; +}; + +template<is_object T> +class option +{ + static constexpr bool kHasNiche = has_niche<T>; + + using HasValueMarker = select_t<kHasNiche, empty, bool>; + + maybe_uninit<T> m_payload{}; + ASL_NO_UNIQUE_ADDRESS HasValueMarker m_has_value{}; + + template<is_object U> + friend class option; + + template<typename... Args> + constexpr void construct(Args&&... args) + { + ASL_ASSERT(!has_value()); + + if constexpr (!kHasNiche) + { + m_payload.construct_unsafe(ASL_FWD(args)...); + m_has_value = true; + } + else + { + if constexpr (move_assignable<T>) + { + m_payload.assign_unsafe(ASL_MOVE(T{ASL_FWD(args)...})); + } + else + { + m_payload.destroy_unsafe(); + m_payload.construct_unsafe(ASL_FWD(args)...); + } + } + } + + template<typename U> + constexpr void assign(U&& arg) + { + ASL_ASSERT(has_value()); + m_payload.assign_unsafe(ASL_FWD(arg)); + } + +public: + using type = T; + + constexpr option() : option{nullopt} {} + + // NOLINTNEXTLINE(*-explicit-conversions) + constexpr option(nullopt_t) requires (!kHasNiche) {} + + // NOLINTNEXTLINE(*-explicit-conversions) + constexpr option(nullopt_t) requires kHasNiche : m_payload{in_place, niche_t{}} {} + + template<typename U = T> + constexpr explicit (!convertible_from<T, U&&>) + option(U&& value) + requires ( + kHasNiche && + constructible_from<T, U&&> && + !same_as<un_cvref_t<U>, option> + ) + : m_payload{in_place, ASL_FWD(value)} + {} + + template<typename U = T> + constexpr explicit (!convertible_from<T, U&&>) + option(U&& value) + requires ( + !kHasNiche && + constructible_from<T, U&&> && + !is_option<U> + ) + : m_payload{in_place, ASL_FWD(value)} + , m_has_value{true} + {} + + constexpr option(const option& other) requires trivially_copy_constructible<T> = default; + constexpr option(const option& other) requires (!copy_constructible<T>) = delete; + + constexpr option(const option& other) + requires copy_constructible<T> && (!trivially_copy_constructible<T>) + : option{nullopt} + { + if (other.has_value()) + { + construct(other.m_payload.as_init_unsafe()); + } + } + + constexpr option(option&& other) requires trivially_move_constructible<T> = default; + constexpr option(option&& other) requires (!move_constructible<T>) = delete; + + constexpr option(option&& other) + requires move_constructible<T> && (!trivially_move_constructible<T>) + : option{nullopt} + { + if (other.has_value()) + { + construct(ASL_MOVE(other.m_payload.as_init_unsafe())); + } + } + + template<typename U> + constexpr explicit (!convertible_from<T, const U&>) + option(const option<U>& other) + requires ( + constructible_from<T, const U&> && + option_internal::not_constructible_from_option<T, U> + ) + : option{nullopt} + { + if (other.has_value()) + { + construct(other.m_payload.as_init_unsafe()); + } + } + + template<typename U> + constexpr explicit (!convertible_from<T, U&&>) + option(option<U>&& other) + requires ( + constructible_from<T, U&&> && + option_internal::not_constructible_from_option<T, U> + ) + : option{nullopt} + { + if (other.has_value()) + { + construct(ASL_MOVE(other.m_payload.as_init_unsafe())); + } + } + + constexpr option& operator=(nullopt_t) & + { + reset(); + return *this; + } + + template<typename U = T> + constexpr option& operator=(U&& value) & + requires ( + assignable_from<T&, U&&> && + constructible_from<T, U&&> && + !is_option<U> + ) + { + if (has_value()) + { + assign(ASL_FWD(value)); + } + else + { + construct(ASL_FWD(value)); + } + + return *this; + } + + constexpr option& operator=(const option& other) & + requires (!copy_assignable<T>) = delete; + + constexpr option& operator=(const option& other) & + requires trivially_copy_assignable<T> = default; + + constexpr option& operator=(const option& other) & + requires copy_assignable<T> && (!trivially_copy_constructible<T>) + { + if (&other == this) { return *this; } + + if (other.has_value()) + { + if (has_value()) + { + assign(other.m_payload.as_init_unsafe()); + } + else + { + construct(other.m_payload.as_init_unsafe()); + } + } + else if (has_value()) + { + reset(); + } + + return *this; + } + + constexpr option& operator=(option&& other) & + requires (!move_assignable<T>) = delete; + + constexpr option& operator=(option&& other) & + requires trivially_move_assignable<T> = default; + + constexpr option& operator=(option&& other) & + requires move_assignable<T> && (!trivially_move_constructible<T>) + { + if (&other == this) { return *this; } + + if (other.has_value()) + { + if (has_value()) + { + assign(ASL_MOVE(other.m_payload.as_init_unsafe())); + } + else + { + construct(ASL_MOVE(other.m_payload.as_init_unsafe())); + } + } + else if (has_value()) + { + reset(); + } + + return *this; + } + + template<typename U = T> + constexpr option& operator=(const option<U>& other) & + requires ( + constructible_from<T, const U&> && + assignable_from<T&, const U&> && option_internal::not_constructible_assignable_from_option<T, U> - )
- {
- if (other.has_value())
- {
- if (has_value())
- {
- assign(other.m_payload.as_init_unsafe());
- }
- else
- {
- construct(other.m_payload.as_init_unsafe());
- }
- }
- else if (has_value())
- {
- reset();
- }
-
- return *this;
- }
-
- template<typename U = T>
- constexpr option& operator=(option<U>&& other) &
- requires (
- constructible_from<T, U&&> &&
- assignable_from<T&, U&&> &&
- option_internal::not_constructible_assignable_from_option<T, U>
- )
- {
- if (other.has_value())
- {
- if (has_value())
- {
- assign(ASL_MOVE(other.m_payload.as_init_unsafe()));
- }
- else
- {
- construct(ASL_MOVE(other.m_payload.as_init_unsafe()));
- }
- }
- else if (has_value())
- {
- reset();
- }
-
- return *this;
- }
-
- constexpr ~option() requires trivially_destructible<T> = default;
- constexpr ~option() requires (!trivially_destructible<T>)
- {
- reset();
- }
-
- constexpr void reset()
- {
- if (!has_value()) { return; }
-
- if constexpr (kHasNiche)
- {
- if constexpr (move_assignable<T>)
- {
- m_payload.assign_unsafe(ASL_MOVE(T{niche_t{}}));
- }
- else
- {
- m_payload.destroy_unsafe();
- m_payload.construct_unsafe(niche_t{});
- }
- }
- else
- {
- m_has_value = false;
- m_payload.destroy_unsafe();
- }
- }
-
- constexpr bool has_value() const
- {
- if constexpr (kHasNiche)
- {
- return m_payload.as_init_unsafe() != niche_t{};
- }
- else
- {
- return m_has_value;
- }
- }
-
- // @Todo(C++23) Deducing this
- constexpr T&& value() &&
- {
- ASL_ASSERT_RELEASE(has_value());
- return ASL_MOVE(m_payload).as_init_unsafe();
- }
-
- constexpr T& value() &
- {
- ASL_ASSERT_RELEASE(has_value());
- return m_payload.as_init_unsafe();
- }
-
- constexpr const T& value() const&
- {
- ASL_ASSERT_RELEASE(has_value());
- return m_payload.as_init_unsafe();
- }
-
- template<typename U>
- constexpr T value_or(U&& other_value) const&
- requires copy_constructible<T> && convertible_from<T, U&&>
- {
- return has_value() ? value() : static_cast<T>(ASL_FWD(other_value));
- }
-
- template<typename U>
- constexpr T value_or(U&& other_value) &&
- requires move_constructible<T> && convertible_from<T, U&&>
- {
- return has_value() ? ASL_MOVE(value()) : static_cast<T>(ASL_FWD(other_value));
- }
-
- template<typename... Args>
- constexpr T& emplace(Args&&... args) &
- requires constructible_from<T, Args&&...>
- {
- if (has_value()) { reset(); }
- construct(ASL_FWD(args)...);
- return value();
- }
-
- template<typename F>
- constexpr auto and_then(F&& f) &
- requires is_option<result_of_t<F(T&)>>
- {
- if (has_value())
- {
- return invoke(ASL_FWD(f), value());
- }
- return un_cvref_t<result_of_t<F(T&)>>{};
- }
-
- template<typename F>
- constexpr auto and_then(F&& f) const&
- requires is_option<result_of_t<F(const T&)>>
- {
- if (has_value())
- {
- return invoke(ASL_FWD(f), value());
- }
- return un_cvref_t<result_of_t<F(const T&)>>{};
- }
-
- template<typename F>
- constexpr auto and_then(F&& f) &&
- requires is_option<result_of_t<F(T)>>
- {
- if (has_value())
- {
- return invoke(ASL_FWD(f), ASL_MOVE(value()));
- }
- return un_cvref_t<result_of_t<F(T)>>{};
- }
-
- template<typename F>
- constexpr auto transform(F&& f) &
- {
- using U = un_cvref_t<result_of_t<F(T&)>>;
- if (has_value())
- {
- return option<U>{ invoke(ASL_FWD(f), value()) };
- }
- return option<U>{};
- }
-
- template<typename F>
- constexpr auto transform(F&& f) const&
- {
- using U = un_cvref_t<result_of_t<F(const T&)>>;
- if (has_value())
- {
- return option<U>{ invoke(ASL_FWD(f), value()) };
- }
- return option<U>{};
- }
-
- template<typename F>
- constexpr auto transform(F&& f) &&
- {
- using U = un_cvref_t<result_of_t<F(T)>>;
- if (has_value())
- {
- return option<U>{ invoke(ASL_FWD(f), ASL_MOVE(value())) };
- }
- return option<U>{};
- }
-
- template<typename F>
- constexpr option or_else(F&& f) const&
- requires same_as<un_cvref_t<result_of_t<F()>>, option>
- {
- return has_value() ? *this : invoke(ASL_FWD(f));
- }
-
- template<typename F>
- constexpr option or_else(F&& f) &&
- requires same_as<un_cvref_t<result_of_t<F()>>, option>
- {
- return has_value() ? ASL_MOVE(*this) : invoke(ASL_FWD(f));
- }
-
- template<typename H>
- requires (!uniquely_represented<option>) && hashable<T>
- friend H AslHashValue(H h, const option& opt)
- {
- if (!opt.has_value())
- {
- return H::combine(ASL_MOVE(h), 0);
- }
- return H::combine(ASL_MOVE(h), 1, opt.value());
- }
-};
-
-template<typename T>
-requires has_niche<T> && uniquely_represented<T>
-struct is_uniquely_represented<option<T>> : true_type {};
-
-template<typename T>
-option(T) -> option<T>;
-
-} // namespace asl
+ ) + { + if (other.has_value()) + { + if (has_value()) + { + assign(other.m_payload.as_init_unsafe()); + } + else + { + construct(other.m_payload.as_init_unsafe()); + } + } + else if (has_value()) + { + reset(); + } + + return *this; + } + + template<typename U = T> + constexpr option& operator=(option<U>&& other) & + requires ( + constructible_from<T, U&&> && + assignable_from<T&, U&&> && + option_internal::not_constructible_assignable_from_option<T, U> + ) + { + if (other.has_value()) + { + if (has_value()) + { + assign(ASL_MOVE(other.m_payload.as_init_unsafe())); + } + else + { + construct(ASL_MOVE(other.m_payload.as_init_unsafe())); + } + } + else if (has_value()) + { + reset(); + } + + return *this; + } + + constexpr ~option() requires trivially_destructible<T> = default; + constexpr ~option() requires (!trivially_destructible<T>) + { + reset(); + } + + constexpr void reset() + { + if (!has_value()) { return; } + + if constexpr (kHasNiche) + { + if constexpr (move_assignable<T>) + { + m_payload.assign_unsafe(ASL_MOVE(T{niche_t{}})); + } + else + { + m_payload.destroy_unsafe(); + m_payload.construct_unsafe(niche_t{}); + } + } + else + { + m_has_value = false; + m_payload.destroy_unsafe(); + } + } + + constexpr bool has_value() const + { + if constexpr (kHasNiche) + { + return m_payload.as_init_unsafe() != niche_t{}; + } + else + { + return m_has_value; + } + } + + // @Todo(C++23) Deducing this + constexpr T&& value() && + { + ASL_ASSERT_RELEASE(has_value()); + return ASL_MOVE(m_payload).as_init_unsafe(); + } + + constexpr T& value() & + { + ASL_ASSERT_RELEASE(has_value()); + return m_payload.as_init_unsafe(); + } + + constexpr const T& value() const& + { + ASL_ASSERT_RELEASE(has_value()); + return m_payload.as_init_unsafe(); + } + + template<typename U> + constexpr T value_or(U&& other_value) const& + requires copy_constructible<T> && convertible_from<T, U&&> + { + return has_value() ? value() : static_cast<T>(ASL_FWD(other_value)); + } + + template<typename U> + constexpr T value_or(U&& other_value) && + requires move_constructible<T> && convertible_from<T, U&&> + { + return has_value() ? ASL_MOVE(value()) : static_cast<T>(ASL_FWD(other_value)); + } + + template<typename... Args> + constexpr T& emplace(Args&&... args) & + requires constructible_from<T, Args&&...> + { + if (has_value()) { reset(); } + construct(ASL_FWD(args)...); + return value(); + } + + template<typename F> + constexpr auto and_then(F&& f) & + requires is_option<result_of_t<F(T&)>> + { + if (has_value()) + { + return invoke(ASL_FWD(f), value()); + } + return un_cvref_t<result_of_t<F(T&)>>{}; + } + + template<typename F> + constexpr auto and_then(F&& f) const& + requires is_option<result_of_t<F(const T&)>> + { + if (has_value()) + { + return invoke(ASL_FWD(f), value()); + } + return un_cvref_t<result_of_t<F(const T&)>>{}; + } + + template<typename F> + constexpr auto and_then(F&& f) && + requires is_option<result_of_t<F(T)>> + { + if (has_value()) + { + return invoke(ASL_FWD(f), ASL_MOVE(value())); + } + return un_cvref_t<result_of_t<F(T)>>{}; + } + + template<typename F> + constexpr auto transform(F&& f) & + { + using U = un_cvref_t<result_of_t<F(T&)>>; + if (has_value()) + { + return option<U>{ invoke(ASL_FWD(f), value()) }; + } + return option<U>{}; + } + + template<typename F> + constexpr auto transform(F&& f) const& + { + using U = un_cvref_t<result_of_t<F(const T&)>>; + if (has_value()) + { + return option<U>{ invoke(ASL_FWD(f), value()) }; + } + return option<U>{}; + } + + template<typename F> + constexpr auto transform(F&& f) && + { + using U = un_cvref_t<result_of_t<F(T)>>; + if (has_value()) + { + return option<U>{ invoke(ASL_FWD(f), ASL_MOVE(value())) }; + } + return option<U>{}; + } + + template<typename F> + constexpr option or_else(F&& f) const& + requires same_as<un_cvref_t<result_of_t<F()>>, option> + { + return has_value() ? *this : invoke(ASL_FWD(f)); + } + + template<typename F> + constexpr option or_else(F&& f) && + requires same_as<un_cvref_t<result_of_t<F()>>, option> + { + return has_value() ? ASL_MOVE(*this) : invoke(ASL_FWD(f)); + } + + template<typename H> + requires (!uniquely_represented<option>) && hashable<T> + friend H AslHashValue(H h, const option& opt) + { + if (!opt.has_value()) + { + return H::combine(ASL_MOVE(h), 0); + } + return H::combine(ASL_MOVE(h), 1, opt.value()); + } +}; + +template<typename T> +requires has_niche<T> && uniquely_represented<T> +struct is_uniquely_represented<option<T>> : true_type {}; + +template<typename T> +option(T) -> option<T>; + +} // namespace asl |