From 731087fc09bb87e19dc1308587a7dc2d76780cf2 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Sat, 23 Sep 2023 02:07:41 -0400 Subject: [PATCH] Add more Choice docs --- sus/choice/__private/storage.h | 16 +-- sus/choice/choice.h | 254 +++++++++++++++++++++++---------- 2 files changed, 187 insertions(+), 83 deletions(-) diff --git a/sus/choice/__private/storage.h b/sus/choice/__private/storage.h index 17dd1d710..dcbf30be6 100644 --- a/sus/choice/__private/storage.h +++ b/sus/choice/__private/storage.h @@ -173,7 +173,7 @@ union Storage, Elements...> { return more_.eq(index, other.more_); } } - inline constexpr std::strong_ordering ord(size_t index, + inline constexpr std::strong_ordering strong_ord(size_t index, const Storage& other) const& { if (index == I) { return std::strong_order(tuple_, other.tuple_); @@ -285,11 +285,11 @@ union Storage { return more_.eq(index, other.more_); } } - inline constexpr auto ord(size_t index, const Storage& other) const& { + inline constexpr auto strong_ord(size_t index, const Storage& other) const& { if (index == I) { return std::strong_ordering::equivalent; } else { - return more_.ord(index, other.more_); + return more_.strong_ord(index, other.more_); } } inline constexpr auto weak_ord(size_t index, const Storage& other) const& { @@ -409,11 +409,11 @@ union Storage, Elements...> { return more_.eq(index, other.more_); } } - inline constexpr auto ord(size_t index, const Storage& other) const& { + inline constexpr auto strong_ord(size_t index, const Storage& other) const& { if (index == I) { return std::strong_order(tuple_, other.tuple_); } else { - return more_.ord(index, other.more_); + return more_.strong_ord(index, other.more_); } } inline constexpr auto weak_ord(size_t index, const Storage& other) const& { @@ -506,7 +506,7 @@ union Storage> { ::sus::check(index == I); return tuple_ == other.tuple_; } - inline constexpr auto ord(size_t index, + inline constexpr auto strong_ord(size_t index, const Storage& other) const& noexcept { ::sus::check(index == I); return std::strong_order(tuple_, other.tuple_); @@ -565,7 +565,7 @@ union Storage { ::sus::check(index == I); return true; } - inline constexpr auto ord(size_t index, const Storage&) const& { + inline constexpr auto strong_ord(size_t index, const Storage&) const& { ::sus::check(index == I); return std::strong_ordering::equivalent; } @@ -642,7 +642,7 @@ union Storage> { ::sus::check(index == I); return tuple_ == other.tuple_; } - inline constexpr auto ord(size_t index, const Storage& other) const& { + inline constexpr auto strong_ord(size_t index, const Storage& other) const& { ::sus::check(index == I); return std::strong_order(tuple_, other.tuple_); } diff --git a/sus/choice/choice.h b/sus/choice/choice.h index 033f17c00..10f450427 100644 --- a/sus/choice/choice.h +++ b/sus/choice/choice.h @@ -71,7 +71,7 @@ concept ChoiceValueIsVoid = !requires(const Choice& c) { /// [`std::variant`](https://en.cppreference.com/w/cpp/utility/variant), and /// typically the `Tags` are specified to be values in an `enum`. /// -/// A `Choice` always has an active tag, as one must be specified at +/// A `Choice` always has an active member, as the tag must be specified at /// construction, and the asociated values for the tag are always set as they /// must be set when the tag is specified. This means a `Choice` is always in a /// fully specified state, or it is moved-from. Once it is moved from it may not @@ -90,6 +90,12 @@ concept ChoiceValueIsVoid = !requires(const Choice& c) { /// * [`into_inner()`]($sus::choice_type::Choice::into_inner) moves all /// values attached to the tag out of the `Choice` and marks the `Choice` as /// moved-from. It is only callable on an rvalue `Choice.` +/// * [`get()`]($sus::choice_type::Choice::get) returns a const reference +/// to the values attached to the tag if its currently active, and returns +/// `None` if the tag is not active. +/// * [`get_mut()`]($sus::choice_type::Choice::get) returns a mutable +/// reference to the values attached to the tag if its currently active, and +/// returns `None` if the tag is not active. /// /// # Examples /// This `Choice` holds either a [`u64`]($sus::num::u64) with @@ -242,6 +248,11 @@ class Choice<__private::TypeList, Tags...> final { template using TypeForTag = __private::PublicTypeForStorageType>; + /// Constructs a `Choice` with the `V` tag indicating its active member, and + /// with the parameters used to set the associated values. + /// + /// If the associated type of the tag is `void` then `with()` takes no + /// parameters. template > requires(std::constructible_from) @@ -252,7 +263,6 @@ class Choice<__private::TypeList, Tags...> final { .construct(::sus::forward(values)); return u; } - template > constexpr static Choice with() noexcept { @@ -271,15 +281,17 @@ class Choice<__private::TypeList, Tags...> final { storage_.destroy(index_); } + /// Move constructor. + /// + /// `Choice` satisfies [`Move`]($sus::mem::Move) when the types it holds all + /// satisfy [`Move`]($sus::mem::Move). + /// #[doc.overloads=move] constexpr Choice(Choice&& o) noexcept requires((... && ::sus::mem::Move) && (std::is_trivially_move_constructible_v && ... && std::is_trivially_move_constructible_v)) = default; - Choice(Choice&& o) - requires(!(... && ::sus::mem::Move)) - = delete; - + /// #[doc.overloads=move] constexpr Choice(Choice&& o) noexcept requires((... && ::sus::mem::Move) && !(std::is_trivially_move_constructible_v && ... && @@ -291,16 +303,22 @@ class Choice<__private::TypeList, Tags...> final { check(index_ != kUseAfterMove); storage_.move_construct(index_, ::sus::move(o.storage_)); } + /// #[doc.overloads=move] + Choice(Choice&& o) + requires(!(... && ::sus::mem::Move)) + = delete; + /// Move assignment. + /// + /// `Choice` satisfies [`Move`]($sus::mem::Move) when the types it holds all + /// satisfy [`Move`]($sus::mem::Move). + /// #[doc.overloads=move] Choice& operator=(Choice&& o) noexcept requires((... && ::sus::mem::Move) && (std::is_trivially_move_assignable_v && ... && std::is_trivially_move_assignable_v)) = default; - Choice& operator=(Choice&& o) - requires(!(... && ::sus::mem::Move)) - = delete; - + /// #[doc.overloads=move] constexpr Choice& operator=(Choice&& o) noexcept requires((... && ::sus::mem::Move) && !(std::is_trivially_move_assignable_v && ... && @@ -317,16 +335,22 @@ class Choice<__private::TypeList, Tags...> final { o.index_ = kUseAfterMove; return *this; } + /// #[doc.overloads=move] + Choice& operator=(Choice&& o) + requires(!(... && ::sus::mem::Move)) + = delete; + /// Copy constructor. + /// + /// `Choice` satisfies [`Copy`]($sus::mem::Copy) when the types it holds all + /// satisfy [`Copy`]($sus::mem::Copy). + /// #[doc.overloads=copy] constexpr Choice(const Choice& o) requires((... && ::sus::mem::Copy) && (std::is_trivially_copy_constructible_v && ... && std::is_trivially_copy_constructible_v)) = default; - Choice(const Choice& o) - requires(!(... && ::sus::mem::Copy)) - = delete; - + /// #[doc.overloads=copy] constexpr Choice(const Choice& o) noexcept requires((... && ::sus::mem::Copy) && !(std::is_trivially_copy_constructible_v && ... && @@ -335,16 +359,22 @@ class Choice<__private::TypeList, Tags...> final { check(o.index_ != kUseAfterMove); storage_.copy_construct(index_, o.storage_); } + /// #[doc.overloads=copy] + Choice(const Choice& o) + requires(!(... && ::sus::mem::Copy)) + = delete; + /// Copy assignment. + /// + /// `Choice` satisfies [`Copy`]($sus::mem::Copy) when the types it holds all + /// satisfy [`Copy`]($sus::mem::Copy). + /// #[doc.overloads=copy] constexpr Choice& operator=(const Choice& o) requires((... && ::sus::mem::Copy) && (std::is_trivially_copy_assignable_v && ... && std::is_trivially_copy_assignable_v)) = default; - Choice& operator=(const Choice& o) - requires(!(... && ::sus::mem::Copy)) - = delete; - + /// #[doc.overloads=copy] constexpr Choice& operator=(const Choice& o) noexcept requires((... && ::sus::mem::Copy) && !(std::is_trivially_copy_assignable_v && ... && @@ -360,8 +390,14 @@ class Choice<__private::TypeList, Tags...> final { } return *this; } + /// #[doc.overloads=copy] + Choice& operator=(const Choice& o) + requires(!(... && ::sus::mem::Copy)) + = delete; - // sus::mem::Clone> trait. + /// [`Clone`]($sus::mem::Clone) support. + /// `Choice` satisfies [`Clone`]($sus::mem::Clone) when the types it holds all + /// satisfy [`Clone`]($sus::mem::Clone). constexpr Choice clone() const& noexcept requires((... && ::sus::mem::Clone) && !(... && ::sus::mem::Copy)) { @@ -372,21 +408,23 @@ class Choice<__private::TypeList, Tags...> final { } /// Support for using Choice in a `switch()` statment. + /// + /// See the type's top level documentation for an example. constexpr inline operator TagsType() const& noexcept { return which(); } /// Returns which is the active member of the Choice. /// - /// Typically to access the data in the Choice, a switch statement would be + /// Typically to access the data in the Choice, a `switch` statement would be /// used, so as to call the getter or setter methods with the right value - /// specified as a template parameter. When used in a switch statement, the - /// which() method can be omitted. + /// specified as a template parameter. When used in a `switch` statement, the + /// `which` method can be omitted. /// /// # Example /// ``` - /// switch (my_union) { - /// case Value1: return my_union.as().stuff; - /// case Value2: return my_union.as().andmore; - /// case Value3: return my_union.as().stufftoo; + /// switch (my_choice) { + /// case Value1: return my_choice.as().stuff; + /// case Value2: return my_choice.as().andmore; + /// case Value3: return my_choice.as().stufftoo; /// } /// ``` /// @@ -435,6 +473,8 @@ class Choice<__private::TypeList, Tags...> final { /// The function has a template parameter specifying the tag of the active /// member in the choice. /// + /// If the active member's associated type is `void` then this method is + /// deleted and can't be called. /// If the active member has a single value, a reference to it is returned /// directly, otherwise a Tuple of references is returned to all values in the /// active member. @@ -459,6 +499,8 @@ class Choice<__private::TypeList, Tags...> final { /// The function has a template parameter specifying the tag of the active /// member in the choice. /// + /// If the active member's associated type is `void` then this method is + /// deleted and can't be called. /// If the active member has a single value, a reference to it is returned /// directly, otherwise a Tuple of references is returned to all values in the /// active member. @@ -473,14 +515,16 @@ class Choice<__private::TypeList, Tags...> final { return __private::find_choice_storage_mut>(storage_).as_mut(); } - /// Unwraps the Choice to move out the current value(s) inside the Choice. + /// Unwraps the `Choice` to move out the current value(s) inside the `Choice`. /// /// The function has a template parameter specifying the tag of the active - /// member in the choice. + /// member in the `Choice`. /// + /// If the active member's associated type is `void` then this method is + /// deleted and can't be called. /// If the active member has a single value, an rvalue reference to it is - /// returned directly, otherwise a Tuple of rvalue references is returned to - /// all values in the active member. + /// returned directly, otherwise a [`Tuple`]($sus::tuple_type::Tuple) of + /// rvalue references is returned to all values in the active member. /// /// # Panics /// The function will panic if the active member does not match the tag value @@ -493,14 +537,16 @@ class Choice<__private::TypeList, Tags...> final { return ::sus::move(s).into_inner(); } - /// Returns a const reference to the value(s) inside the Choice. + /// Returns a const reference to the value(s) inside the `Choice`. /// - /// If the template parameter does not match the active member in the Choice, - /// the function returns `None`. + /// If the template parameter does not match the active member in the + /// `Choice`, the function returns `None`. /// + /// If the active member's associated type is `void` then this method is + /// deleted and can't be called. /// If the active member has a single value, a reference to it is returned - /// directly, otherwise a Tuple of references is returned to all values in the - /// active member. + /// directly, otherwise a [`Tuple`]($sus::tuple_type::Tuple) of references is + /// returned to all values in the active member. template requires(__private::ValueIsNotVoid>) constexpr inline Option> get() const& noexcept { @@ -513,14 +559,16 @@ class Choice<__private::TypeList, Tags...> final { requires(!std::is_reference_v>) constexpr inline Option> get() && noexcept = delete; - /// Returns a mutable reference to the value(s) inside the Choice. + /// Returns a mutable reference to the value(s) inside the `Choice`. /// - /// If the template parameter does not match the active member in the Choice, - /// the function returns `None`. + /// If the template parameter does not match the active member in the + /// `Choice`, the function returns `None`. /// + /// If the active member's associated type is `void` then this method is + /// deleted and can't be called. /// If the active member has a single value, a reference to it is returned - /// directly, otherwise a Tuple of references is returned to all values in the - /// active member. + /// directly, otherwise a [`Tuple`]($sus::tuple_type::Tuple) of references is + /// returned to all values in the active member. template requires(__private::ValueIsNotVoid>) constexpr inline Option> get_mut() & noexcept { @@ -529,6 +577,11 @@ class Choice<__private::TypeList, Tags...> final { __private::find_choice_storage_mut>(storage_).as_mut()); } + /// Changes the `Choice` to make the tag `V` active, and sets the associated + /// values of `V` from the paramters. + /// + /// If the associated type of the tag is `void` then `set()` takes no + /// parameters. template > requires(std::convertible_to && __private::ValueIsNotVoid>) @@ -544,15 +597,25 @@ class Choice<__private::TypeList, Tags...> final { ::sus::move(values)); } } + template > + constexpr void set() & noexcept { + if (index_ != index) { + if (index_ != kUseAfterMove) storage_.destroy(index_); + index_ = index; + } + } - /// Returns a const reference to the value(s) inside the Choice. + /// Returns a const reference to the value(s) inside the `Choice`. /// /// The function has a template parameter specifying the tag of the active - /// member in the choice. + /// member in the `Choice`. /// + /// If the active member's associated type is `void` then this method is + /// deleted and can't be called. /// If the active member has a single value, a reference to it is returned - /// directly, otherwise a Tuple of references is returned to all values in the - /// active member. + /// directly, otherwise a [`Tuple`]($sus::tuple_type::Tuple) of references is + /// returned to all values in the active member. /// /// # Safety /// If the active member does not match the tag value passed as the template @@ -570,14 +633,16 @@ class Choice<__private::TypeList, Tags...> final { constexpr inline const StorageTypeOfTag& get_unchecked( ::sus::marker::UnsafeFnMarker) && noexcept = delete; - /// Returns a mutable reference to the value(s) inside the Choice. + /// Returns a mutable reference to the value(s) inside the `Choice`. /// /// The function has a template parameter specifying the tag of the active - /// member in the choice. + /// member in the `Choice`. /// + /// If the active member's associated type is `void` then this method is + /// deleted and can't be called. /// If the active member has a single value, a reference to it is returned - /// directly, otherwise a Tuple of references is returned to all values in the - /// active member. + /// directly, otherwise a [`Tuple`]($sus::tuple_type::Tuple) of references is + /// returned to all values in the active member. /// /// # Safety /// If the active member does not match the tag value passed as the template @@ -589,23 +654,22 @@ class Choice<__private::TypeList, Tags...> final { return __private::find_choice_storage_mut>(storage_).as_mut(); } - template > - constexpr void set() & noexcept { - if (index_ != index) { - if (index_ != kUseAfterMove) storage_.destroy(index_); - index_ = index; - } - } - - /// Compares two [`Choice`]($sus::choice_type::Choice)s for equality if the - /// types inside satisfy `Eq`. + /// Compares two `Choice`s for equality if the + /// types inside satisfy [`Eq`]($sus::ops::Eq). /// /// Satisfies the [`Eq`]($sus::ops::Eq) concept for `Choice`. + friend constexpr bool operator==(const Choice& l, const Choice& r) noexcept + requires(__private::ChoiceIsEq, + TagsType, __private::TypeList>) + { + check(l.index_ != kUseAfterMove && r.index_ != kUseAfterMove); + return l.index_ == r.index_ && l.storage_.eq(l.index_, r.storage_); + } + template requires(__private::ChoiceIsEq, decltype(V), __private::TypeList>) - friend inline constexpr bool operator==( + friend constexpr bool operator==( const Choice& l, const Choice<__private::TypeList, V, Vs...>& r) noexcept { check(l.index_ != kUseAfterMove && r.index_ != kUseAfterMove); @@ -615,18 +679,35 @@ class Choice<__private::TypeList, Tags...> final { template requires(!__private::ChoiceIsEq, decltype(V), __private::TypeList>) - friend inline constexpr bool operator==( + friend constexpr bool operator==( const Choice& l, const Choice<__private::TypeList, V, Vs...>& r) noexcept = delete; - /// sus::ops::StrongOrd, Choice> trait. + /// Compares two `Choice`s for ordering. /// - /// #[doc.overloads=ord] + /// `Choice` satisfies the strongest of [`StrongOrd`]( + /// $sus::ops::StrongOrd), [`Ord`]($sus::ops::Ord), and [`PartialOrd`]( + /// $sus::ops::PartialOrd) that all the values inside the `Choice` types + /// satisfy. + friend constexpr std::strong_ordering operator<=>(const Choice& l, + const Choice& r) noexcept + requires(__private::ChoiceIsStrongOrd, + TagsType, __private::TypeList>) + { + check(l.index_ != kUseAfterMove && r.index_ != kUseAfterMove); + const auto value_order = std::strong_order(l.which(), r.which()); + if (value_order != std::strong_ordering::equivalent) { + return value_order; + } else { + return l.storage_.strong_ord(l.index_, r.storage_); + } + } + template requires( __private::ChoiceIsStrongOrd, decltype(V), __private::TypeList>) - friend inline constexpr std::strong_ordering operator<=>( + friend constexpr std::strong_ordering operator<=>( const Choice& l, const Choice<__private::TypeList, V, Vs...>& r) noexcept { check(l.index_ != kUseAfterMove && r.index_ != kUseAfterMove); @@ -634,17 +715,28 @@ class Choice<__private::TypeList, Tags...> final { if (value_order != std::strong_ordering::equivalent) { return value_order; } else { - return l.storage_.ord(l.index_, r.storage_); + return l.storage_.strong_ord(l.index_, r.storage_); + } + } + + friend constexpr std::weak_ordering operator<=>(const Choice& l, + const Choice& r) noexcept + requires(__private::ChoiceIsOrd, + TagsType, __private::TypeList>) + { + check(l.index_ != kUseAfterMove && r.index_ != kUseAfterMove); + const auto value_order = std::weak_order(l.which(), r.which()); + if (value_order != std::weak_ordering::equivalent) { + return value_order; + } else { + return l.storage_.weak_ord(l.index_, r.storage_); } } - /// sus::ops::Ord, Choice> trait. - /// - /// #[doc.overloads=weakord] template requires(__private::ChoiceIsOrd, decltype(V), __private::TypeList>) - friend inline constexpr std::weak_ordering operator<=>( + friend constexpr std::weak_ordering operator<=>( const Choice& l, const Choice<__private::TypeList, V, Vs...>& r) noexcept { check(l.index_ != kUseAfterMove && r.index_ != kUseAfterMove); @@ -656,14 +748,26 @@ class Choice<__private::TypeList, Tags...> final { } } - /// sus::ops::PartialOrd, Choice> trait. - /// - /// #[doc.overloads=partialord] + friend constexpr std::partial_ordering operator<=>(const Choice& l, + const Choice& r) noexcept + requires( + __private::ChoiceIsPartialOrd, + TagsType, __private::TypeList>) + { + check(l.index_ != kUseAfterMove && r.index_ != kUseAfterMove); + const auto value_order = std::partial_order(l.which(), r.which()); + if (value_order != std::partial_ordering::equivalent) { + return value_order; + } else { + return l.storage_.partial_ord(l.index_, r.storage_); + } + } + template requires( __private::ChoiceIsPartialOrd, decltype(V), __private::TypeList>) - friend inline constexpr std::partial_ordering operator<=>( + friend constexpr std::partial_ordering operator<=>( const Choice& l, const Choice<__private::TypeList, V, Vs...>& r) noexcept { check(l.index_ != kUseAfterMove && r.index_ != kUseAfterMove); @@ -679,13 +783,13 @@ class Choice<__private::TypeList, Tags...> final { requires(!__private::ChoiceIsAnyOrd, decltype(RhsTag), __private::TypeList>) - friend inline constexpr auto operator<=>( + friend constexpr auto operator<=>( const Choice<__private::TypeList, Tags...>& l, const Choice<__private::TypeList, RhsTag, RhsTags...>& r) = delete; private: - constexpr inline Choice(IndexType i) noexcept : index_(i) {} + constexpr explicit Choice(IndexType i) noexcept : index_(i) {} // TODO: We don't use `[[sus_no_unique_address]]` here as the compiler // overwrites the `index_` when we move-construct into the Storage union.