Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Choice::visit #206

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 93 additions & 12 deletions subspace/choice/__private/storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,22 @@ union Storage<I, ::sus::Tuple<Ts...>, 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]<size_t... Is>(std::index_sequence<Is...>) {
Expand Down Expand Up @@ -247,6 +263,22 @@ union Storage<I, Nothing, 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);
} 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<I + 1, Elements...> more_;
};
Expand Down Expand Up @@ -342,6 +374,22 @@ union Storage<I, ::sus::Tuple<T>, 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>();
Expand Down Expand Up @@ -412,6 +460,16 @@ union Storage<I, ::sus::Tuple<Ts...>> {
::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]<size_t... Is>(std::index_sequence<Is...>) {
Expand Down Expand Up @@ -466,6 +524,16 @@ union Storage<I, Nothing> {
::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 <size_t I, class T>
Expand Down Expand Up @@ -523,6 +591,16 @@ union Storage<I, ::sus::Tuple<T>> {
::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>();
Expand All @@ -541,36 +619,39 @@ union Storage<I, ::sus::Tuple<T>> {

template <auto I, class S>
static constexpr const auto& find_choice_storage(const S& storage) {
return find_choice_storage(storage, std::integral_constant<size_t, size_t{I}>());
return find_choice_storage(storage,
std::integral_constant<size_t, size_t{I}>());
}

template <size_t I, class S>
static constexpr const auto& find_choice_storage(const S& storage,
std::integral_constant<size_t, I>) {
return find_choice_storage(storage.more_, std::integral_constant<size_t, I - 1u>());
static constexpr const auto& find_choice_storage(
const S& storage, std::integral_constant<size_t, I>) {
return find_choice_storage(storage.more_,
std::integral_constant<size_t, I - 1u>());
}

template <class S>
static constexpr const auto& find_choice_storage(const S& storage,
std::integral_constant<size_t, 0>) {
static constexpr const auto& find_choice_storage(
const S& storage, std::integral_constant<size_t, 0>) {
return storage;
}

template <auto I, class S>
static constexpr auto& find_choice_storage_mut(S& storage) {
return find_choice_storage_mut(storage, std::integral_constant<size_t, size_t{I}>());
return find_choice_storage_mut(storage,
std::integral_constant<size_t, size_t{I}>());
}

template <size_t I, class S>
static constexpr auto& find_choice_storage_mut(S& storage,
std::integral_constant<size_t, I>) {
static constexpr auto& find_choice_storage_mut(
S& storage, std::integral_constant<size_t, I>) {
return find_choice_storage_mut(storage.more_,
std::integral_constant<size_t, I - 1u>());
std::integral_constant<size_t, I - 1u>());
}

template <class S>
static constexpr auto& find_choice_storage_mut(S& storage,
std::integral_constant<size_t, 0>) {
static constexpr auto& find_choice_storage_mut(
S& storage, std::integral_constant<size_t, 0>) {
return storage;
}

Expand Down
85 changes: 85 additions & 0 deletions subspace/choice/choice.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,91 @@ class Choice<__private::TypeList<Ts...>, 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<sus_choice_types(
/// (Order::First, u32),
/// (Order::Second, i32))
/// >::with<Order::First>(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<sus_choice_types(
/// (Order::First, u32),
/// (Order::Second, i32))
/// >::with<Order::First>(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 <TagsType V, int&...,
__private::ValueIsVoid Arg = StorageTypeOfTag<V>>
void set() & noexcept {
Expand Down
36 changes: 36 additions & 0 deletions subspace/choice/choice_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<Order> called;
};
Visitor visitor;

auto c =
Choice<sus_choice_types((Order::First, u32), (Order::Second, i32),
(Order::Third, void))>::with<Order::First>(4u);
c.visit(visitor);
EXPECT_EQ(visitor.called.take().unwrap(), Order::First);

c.set<Order::Second>(2);
c.visit(visitor);
EXPECT_EQ(visitor.called.take().unwrap(), Order::Second);

c.set<Order::Third>();
c.visit(visitor);
EXPECT_EQ(visitor.called.take().unwrap(), Order::Third);
}

} // namespace