diff --git a/subspace/choice/__private/storage.h b/subspace/choice/__private/storage.h index 7517d5757..04e01f206 100644 --- a/subspace/choice/__private/storage.h +++ b/subspace/choice/__private/storage.h @@ -161,6 +161,22 @@ union Storage, Elements...> { return more_.partial_ord(index, other.more_); } } + inline constexpr void visit(size_t index, auto tag, + auto&& overload_set) const& { + if (index == I) { + overload_set(tag, get_ref()); + } else { + more_.visit(index, tag, ::sus::move(overload_set)); + } + } + inline constexpr void visit_mut(size_t index, auto tag, + auto&& overload_set) & { + if (index == I) { + overload_set(tag, get_mut()); + } else { + more_.visit_mut(index, tag, ::sus::move(overload_set)); + } + } constexpr auto get_ref() const& { return [this](std::index_sequence) { @@ -247,6 +263,22 @@ union Storage { return more_.partial_ord(index, other.more_); } } + inline constexpr void visit(size_t index, auto tag, + auto&& overload_set) const& { + if (index == I) { + overload_set(tag); + } else { + more_.visit(index, tag, ::sus::move(overload_set)); + } + } + inline constexpr void visit_mut(size_t index, auto tag, + auto&& overload_set) & { + if (index == I) { + overload_set(tag); + } else { + more_.visit_mut(index, tag, ::sus::move(overload_set)); + } + } [[sus_no_unique_address]] Storage more_; }; @@ -342,6 +374,22 @@ union Storage, Elements...> { return more_.partial_ord(index, other.more_); } } + inline constexpr void visit(size_t index, auto tag, + auto&& overload_set) const& { + if (index == I) { + overload_set(tag, get_ref()); + } else { + more_.visit(index, tag, ::sus::move(overload_set)); + } + } + inline constexpr void visit_mut(size_t index, auto tag, + auto&& overload_set) & { + if (index == I) { + overload_set(tag, get_mut()); + } else { + more_.visit_mut(index, tag, ::sus::move(overload_set)); + } + } inline constexpr decltype(auto) get_ref() const& { return tuple_.template get_ref<0>(); @@ -412,6 +460,16 @@ union Storage> { ::sus::check(index == I); return std::partial_order(tuple_, other.tuple_); } + inline constexpr void visit(size_t index, auto tag, + auto&& overload_set) const& { + ::sus::check(index == I); + overload_set(tag, get_ref()); + } + inline constexpr void visit_mut(size_t index, auto tag, + auto&& overload_set) & { + ::sus::check(index == I); + overload_set(tag, get_mut()); + } constexpr auto get_ref() const& { return [this](std::index_sequence) { @@ -466,6 +524,16 @@ union Storage { ::sus::check(index == I); return std::partial_ordering::equivalent; } + inline constexpr void visit(size_t index, auto tag, + auto&& overload_set) const& { + ::sus::check(index == I); + overload_set(tag); + } + inline constexpr void visit_mut(size_t index, auto tag, + auto&& overload_set) & { + ::sus::check(index == I); + overload_set(tag); + } }; template @@ -523,6 +591,16 @@ union Storage> { ::sus::check(index == I); return std::partial_order(tuple_, other.tuple_); } + inline constexpr void visit(size_t index, auto tag, + auto&& overload_set) const& { + ::sus::check(index == I); + overload_set(tag, get_ref()); + } + inline constexpr void visit_mut(size_t index, auto tag, + auto&& overload_set) & { + ::sus::check(index == I); + overload_set(tag, get_mut()); + } inline constexpr decltype(auto) get_ref() const& { return tuple_.template get_ref<0>(); @@ -541,36 +619,39 @@ union Storage> { template static constexpr const auto& find_choice_storage(const S& storage) { - return find_choice_storage(storage, std::integral_constant()); + return find_choice_storage(storage, + std::integral_constant()); } template -static constexpr const auto& find_choice_storage(const S& storage, - std::integral_constant) { - return find_choice_storage(storage.more_, std::integral_constant()); +static constexpr const auto& find_choice_storage( + const S& storage, std::integral_constant) { + return find_choice_storage(storage.more_, + std::integral_constant()); } template -static constexpr const auto& find_choice_storage(const S& storage, - std::integral_constant) { +static constexpr const auto& find_choice_storage( + const S& storage, std::integral_constant) { return storage; } template static constexpr auto& find_choice_storage_mut(S& storage) { - return find_choice_storage_mut(storage, std::integral_constant()); + return find_choice_storage_mut(storage, + std::integral_constant()); } template -static constexpr auto& find_choice_storage_mut(S& storage, - std::integral_constant) { +static constexpr auto& find_choice_storage_mut( + S& storage, std::integral_constant) { return find_choice_storage_mut(storage.more_, - std::integral_constant()); + std::integral_constant()); } template -static constexpr auto& find_choice_storage_mut(S& storage, - std::integral_constant) { +static constexpr auto& find_choice_storage_mut( + S& storage, std::integral_constant) { return storage; } diff --git a/subspace/choice/choice.h b/subspace/choice/choice.h index cb49bb94c..4cc29d5d2 100644 --- a/subspace/choice/choice.h +++ b/subspace/choice/choice.h @@ -348,6 +348,91 @@ class Choice<__private::TypeList, Tags...> final { } } + /// Calls overload_set with a const reference to the currently stored value in + /// the Choice. + /// + /// Typically the `overload_set` would be object which has `operator()` + /// overloaded for each possible value type in the Choice. If there is no + /// `void` type attached to any tag, then a templated function can be used as + /// well. + /// + /// Because the same type may be stored for multiple tag values, the tag + /// value is also passed, as the first parameter, to the `overload_set`. + /// + /// If the type attached to a tag is void, the overload for that tag would + /// only receive the tag as a parameter: `fn(Tag t)`. Otherwise the + /// overload will receive the tag and the value as parameters: + /// `fn(Tag t, const TagValue& v)`. + /// + /// # Example + /// + /// ```cpp + /// struct Visitor { + /// void operator()(Order which, const u32& v) { + /// printf("First\n"); + /// } + /// void operator()(Order which, const i32& v) { + /// printf("Second\n"); + /// } + /// void operator()(Order which /* void */) { + /// printf("Third\n"); + /// } + /// }; + /// Visitor visitor; + /// + /// auto c = Choice::with(4u); + /// c.visit(visitor); // Prints "First". + /// ``` + void visit(auto&& overload_set) const& { + storage_.visit(size_t{index_}, which(), ::sus::move(overload_set)); + } + void visit(auto&& overload_set) && = delete; + + /// Calls overload_set with a mutable reference to the currently stored value + /// in the Choice. + /// + /// Typically the `overload_set` would be object which has `operator()` + /// overloaded for each possible value type in the Choice. If there is no + /// `void` type attached to any tag, then a templated function can be used as + /// well. + /// + /// Because the same type may be stored for multiple tag values, the tag + /// value is also passed, as the first parameter, to the `overload_set`. + /// + /// If the type attached to a tag is void, the overload for that tag would + /// only receive the tag as a parameter: `fn(Tag t)`. Otherwise the + /// overload will receive the tag and the value as parameters: + /// `fn(Tag t, TagValue& v)`. + /// + /// # Example + /// + /// ```cpp + /// struct Visitor { + /// void operator()(Order which, u32& v) { + /// printf("First\n"); + /// } + /// void operator()(Order which, i32& v) { + /// printf("Second\n"); + /// } + /// void operator()(Order which /* void */) { + /// printf("Third\n"); + /// } + /// }; + /// Visitor visitor; + /// + /// auto c = Choice::with(4u); + /// c.visit_mut(visitor); // Prints "First". + /// ``` + void visit_mut(auto&& overload_set) & { + storage_.visit_mut(size_t{index_}, which(), ::sus::move(overload_set)); + } + template > void set() & noexcept { diff --git a/subspace/choice/choice_unittest.cc b/subspace/choice/choice_unittest.cc index a528e294e..41e4496bd 100644 --- a/subspace/choice/choice_unittest.cc +++ b/subspace/choice/choice_unittest.cc @@ -535,4 +535,40 @@ TEST(Choice, VoidValues) { EXPECT_LT(u4, u6); } +TEST(Choice, Visit) { + struct Visitor { + void operator()(Order which, const u32& v) { + EXPECT_EQ(which, Order::First); + EXPECT_EQ(v, 4u); + called.insert(which); + } + void operator()(Order which, const i32& v) { + EXPECT_EQ(which, Order::Second); + EXPECT_EQ(v, 2); + called.insert(which); + } + void operator()(Order which) { + EXPECT_EQ(which, Order::Third); + called.insert(which); + } + + sus::Option called; + }; + Visitor visitor; + + auto c = + Choice::with(4u); + c.visit(visitor); + EXPECT_EQ(visitor.called.take().unwrap(), Order::First); + + c.set(2); + c.visit(visitor); + EXPECT_EQ(visitor.called.take().unwrap(), Order::Second); + + c.set(); + c.visit(visitor); + EXPECT_EQ(visitor.called.take().unwrap(), Order::Third); +} + } // namespace