Class Subspace :: sus :: choice_type :: Choice

class Choice final
{ ... };

A tagged union, or sum type.

A Choice is always set to one of its Tags values, and each tag has zero or more types attached to it as data values.

Choice can be thought of as a combination of an enum and a std::variant, and typically the Tags are specified to be values in an enum.

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 be used except to be re-initialized.

Use the sus_choice_types() macro to specify the types in a Choice type.

To access the values in Choice, the current tag must be specified as a template parameter, and it will be checked for correctness. When it does not match, the Choice method will panic.

  • as<Tag>() gives const access to all the values attached to the tag.
  • as_mut<Tag>() gives mutable access to all the values attached to the tag. It is only callable on mutable Choice.
  • into_inner<Tag>() 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<Tag>() 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<Tag>() 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 with the First tag or a u32 with the Second tag.

enum class Order { First, Second };
using EitherOr = Choice<sus_choice_types(
    (Order::First, u64),
    (Order::Second, u32)
)>;

A Choice tag may be associated with no values by making its type void or may be associated with more than one type in which case all access will be done with a Tuple.

enum class Order { First, Second };
using EitherOr = Choice<sus_choice_types(
    (Order::First, void),
    (Order::Second, std::string, i32)
)>;
auto e1 = EitherOr::with<Order::First>();
auto e2 = EitherOr::with<Order::Second>("text", 123);

The Choice type can be used in a switch, with each case being one of its possible tag values. Within each tag case block, the values can be pulled out of the Choice with as in a type-safe and memory-safe way.

enum class Order { First, Second };
using EitherOr = Choice<sus_choice_types(
    (Order::First, u64),
    (Order::Second, std::string, i32)
)>;
auto e = EitherOr::with<Order::Second>("hello worl", 0xd);
switch (e) {
  case Order::First: {
    const auto& i = e.as<Order::First>();
    // We don't get here.
    fmt::println("First has u64 {}", i);
    break;
  }
  case Order::Second: {
    const auto& [s, i] = e.as<Order::Second>();
    // Prints "Second has hello world".
    fmt::println("Second has {}{:x}", s, i);
    break;
  }
}

Choice will re-export the tag value type as a nested Tag subtype. This allows access to the Choice's values though MyChoiceType::Tag::Name.

enum class Order { First, Second };
using EitherOr = Choice<sus_choice_types(
    (Order::First, u64),
    (Order::Second, u32)
)>;
auto x = EitherOr::with<EitherOr::Tag::First>(987u);

Static Methods

Choice(Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>&& o)
requires
(... && ::sus::mem::Move<Ts>)
(std::is_trivially_move_constructible_v<TagsType> && ... &&
              std::is_trivially_move_constructible_v<Ts>)
Choice(Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>&& o)
requires
(... && ::sus::mem::Move<Ts>)
!(std::is_trivially_move_constructible_v<TagsType> && ... &&
               std::is_trivially_move_constructible_v<Ts>)
Choice(Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>&& o)
requires
!(... && ::sus::mem::Move<Ts>)
deleted

Move constructor.

Choice satisfies Move when the types it holds all satisfy Move.

Choice(const Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>& o)
requires
(... && ::sus::mem::Copy<Ts>)
(std::is_trivially_copy_constructible_v<TagsType> && ... &&
              std::is_trivially_copy_constructible_v<Ts>)
Choice(const Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>& o)
requires
(... && ::sus::mem::Copy<Ts>)
!(std::is_trivially_copy_constructible_v<TagsType> && ... &&
               std::is_trivially_copy_constructible_v<Ts>)
Choice(const Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>& o)
requires
!(... && ::sus::mem::Copy<Ts>)
deleted

Copy constructor.

Choice satisfies Copy when the types it holds all satisfy Copy.

template <TagsType V>
static auto with() -> Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>
requires
__private::StorageCount<StorageTypeOfTag<V>> == 0u
template <TagsType V, class U>
static auto with(U&& value) -> Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>
requires
__private::StorageCount<StorageTypeOfTag<V>> == 1u
sus::construct::SafelyConstructibleFromReference<StorageTypeOfTag<V>, U &&>
std::convertible_to<U &&, StorageTypeOfTag<V>>
template <TagsType V, class... Us>
static auto with(Us&&... values) -> Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>
requires
__private::StorageCount<StorageTypeOfTag<V>> > 1u
sizeof...(Us) ==
                 __private::StorageCount<StorageTypeOfTag<V>>
sus::choice_type::__private::StorageIsSafelyConstructibleFromReference<StorageTypeOfTag<V>, Us &&...>
std::constructible_from<StorageTypeOfTag<V>, Us &&...>

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.

Methods

template <TagsType V>
auto as() const& -> decltype(auto)
requires
__private::StorageCount<StorageTypeOfTag<V>> > 0u
template <TagsType V>
auto as() && -> const sus::choice_type::Choice::StorageTypeOfTag<V>&
requires
!std::is_reference_v<StorageTypeOfTag<V>>
deleted

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.

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.

Panics

The function will panic if the active member does not match the tag value passed as the template parameter.

template <TagsType V>
auto as_mut() & -> decltype(auto)
requires
__private::StorageCount<StorageTypeOfTag<V>> > 0u

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.

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.

Panics

The function will panic if the active member does not match the tag value passed as the template parameter.

auto clone() const& -> Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>
requires
(... && ::sus::mem::Clone<Ts>)
!(... && ::sus::mem::Copy<Ts>)

Clone support. Choice satisfies Clone when the types it holds all satisfy Clone.

template <TagsType V>
auto get() const& -> Option<sus::choice_type::Choice::AccessTypeOfTagConst<V>>
requires
__private::StorageCount<StorageTypeOfTag<V>> > 0u
template <TagsType V>
auto get() && -> Option<sus::choice_type::Choice::AccessTypeOfTagConst<V>>
requires
!std::is_reference_v<StorageTypeOfTag<V>>
deleted

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 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.

template <TagsType V>
auto get_mut() & -> Option<sus::choice_type::Choice::AccessTypeOfTagMut<V>>
requires
__private::StorageCount<StorageTypeOfTag<V>> > 0u

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 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.

template <TagsType V>
auto get_unchecked(UnsafeFnMarker) const& -> decltype(auto)
requires
__private::StorageCount<StorageTypeOfTag<V>> > 0u
template <TagsType V>
auto get_unchecked(UnsafeFnMarker) && -> const sus::choice_type::Choice::StorageTypeOfTag<V>&
requires
!std::is_reference_v<StorageTypeOfTag<V>>
deleted

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.

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.

Safety

If the active member does not match the tag value passed as the template parameter, Undefined Behaviour results.

template <TagsType V>
auto get_unchecked_mut(UnsafeFnMarker) & -> decltype(auto)
requires
__private::StorageCount<StorageTypeOfTag<V>> > 0u

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.

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.

Safety

If the active member does not match the tag value passed as the template parameter, Undefined Behaviour results.

template <TagsType V>
auto into_inner() && -> decltype(auto)
requires
__private::StorageCount<StorageTypeOfTag<V>> > 0u

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.

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.

Panics

The function will panic if the active member does not match the tag value passed as the template parameter.

template <TagsType V>
auto set() & -> void
requires
__private::StorageCount<StorageTypeOfTag<V>> == 0u
template <TagsType V, class U>
auto set(U&& value) & -> void
requires
__private::StorageCount<StorageTypeOfTag<V>> == 1u
sus::construct::SafelyConstructibleFromReference<StorageTypeOfTag<V>, U &&>
std::convertible_to<U &&, StorageTypeOfTag<V>>
template <TagsType V, class... Us>
auto set(Us&&... values) & -> void
requires
__private::StorageCount<StorageTypeOfTag<V>> > 1u
sizeof...(Us) ==
                 __private::StorageCount<StorageTypeOfTag<V>>
sus::choice_type::__private::StorageIsSafelyConstructibleFromReference<StorageTypeOfTag<V>, Us &&...>
std::constructible_from<StorageTypeOfTag<V>, Us &&...>

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.

auto which() const& -> sus::choice_type::Choice::TagsType

Returns which is the active member of the Choice.

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.

Example

switch (my_choice) {
  case Value1: return my_choice.as<Value1>().stuff;
  case Value2: return my_choice.as<Value2>().andmore;
  case Value3: return my_choice.as<Value3>().stufftoo;
}

Inspiration


                      ████████
                  ████▓▓░░▓▓██
                ██▓▓▓▓▓▓▓▓██
              ██▓▓▓▓░░▓▓██
            ██░░▓▓▓▓▓▓██
          ██▓▓▓▓▓▓▓▓▓▓██
          ██▓▓▓▓░░▓▓▓▓██
  ████████▓▓▓▓▓▓▓▓▓▓▓▓▓▓████████
██▓▓░░▓▓▓▓▓▓░░▓▓▓▓▓▓▓▓▓▓░░▓▓▓▓▓▓██
██████████████████▓▓██████████████
        ██      ██      ██
        ██  ██  ██      ██
        ██  ██  ████      ██
        ██        ██      ██
        ██▒▒      ██      ██
        ██▒▒        ██      ██
          ████████████████████
                  ██  ██
                ██  ██▓▓██
                ▓▓  ██▓▓▓▓██
              ██  ████░░▓▓▓▓██            ▓▓▓▓▓▓▓▓▓▓▓▓
            ██  ██  ██▓▓▓▓░░▓▓██        ▓▓░░░░░░░░░░░░
            ██  ██    ████▓▓▓▓▓▓██      ▓▓▓▓▓▓▓▓▓▓▓▓▓▓
▓▓▓▓      ██  ██    ██▓▓▓▓▓▓░░▓▓██    ▓▓▓▓░░░░░░░░░░░░
▓▓▓▓▓▓▓▓▓▓██████▓▓▓▓██▓▓░░▓▓▓▓██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
                    ██████████        ▓▓▓▓░░░░░░░░░░░░
                      ██  ██            ▓▓▓▓▓▓▓▓▓▓▓▓▓▓
                        ██  ██          ▓▓░░░░░░░░░░░░
                          ██  ██          ▓▓▓▓▓▓▓▓▓▓▒▒
                            ████

Conversions

Support for using Choice in a switch() statment.

See the type's top level documentation for an example.

Operators

auto operator=(Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>&& o) -> Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>&
requires
(... && ::sus::mem::Move<Ts>)
(std::is_trivially_move_assignable_v<TagsType> && ... &&
              std::is_trivially_move_assignable_v<Ts>)
auto operator=(Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>&& o) -> Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>&
requires
(... && ::sus::mem::Move<Ts>)
!(std::is_trivially_move_assignable_v<TagsType> && ... &&
               std::is_trivially_move_assignable_v<Ts>)
auto operator=(Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>&& o) -> Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>&
requires
!(... && ::sus::mem::Move<Ts>)
deleted

Move assignment.

Choice satisfies Move when the types it holds all satisfy Move.

auto operator=(const Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>& o) -> Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>&
requires
(... && ::sus::mem::Copy<Ts>)
(std::is_trivially_copy_assignable_v<TagsType> && ... &&
              std::is_trivially_copy_assignable_v<Ts>)
auto operator=(const Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>& o) -> Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>&
requires
(... && ::sus::mem::Copy<Ts>)
!(std::is_trivially_copy_assignable_v<TagsType> && ... &&
               std::is_trivially_copy_assignable_v<Ts>)
auto operator=(const Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>& o) -> Choice<sus::choice_type::__private::TypeList<Ts...>, Tags...>&
requires
!(... && ::sus::mem::Copy<Ts>)
deleted

Copy assignment.

Choice satisfies Copy when the types it holds all satisfy Copy.