Skip to content

Commit 7b8c55b

Browse files
committed
Use FnOnce throughout the Option API
1 parent 303daa0 commit 7b8c55b

File tree

2 files changed

+93
-44
lines changed

2 files changed

+93
-44
lines changed

subspace/option/option.h

Lines changed: 68 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "subspace/assertions/unreachable.h"
2828
#include "subspace/construct/default.h"
2929
#include "subspace/fn/fn_concepts.h"
30+
#include "subspace/fn/run_fn.h"
3031
#include "subspace/iter/from_iterator.h"
3132
#include "subspace/iter/into_iterator.h"
3233
#include "subspace/macros/always_inline.h"
@@ -81,13 +82,18 @@ using sus::iter::Once;
8182
using sus::option::__private::Storage;
8283
using sus::option::__private::StoragePointer;
8384

84-
/// A type which either holds #Some value of type `T`, or #None.
85+
/// A type which either holds `Some` value of type `T`, or `None`.
86+
///
87+
/// To immediately pull the inner value out of an Option, use `unwrap()`. If the
88+
/// `Option` is an lvalue, use `operator*` and `operator->` to access the inner
89+
/// value. However if doing this many times, consider doing `unwrap()` a single
90+
/// time up front.
8591
///
8692
/// `Option<const T>` for non-reference-type `T` is disallowed, as the Option
8793
/// owns the `T` in that case and it ensures the `Option` and the `T` are both
8894
/// accessed with the same const-ness.
8995
///
90-
/// If a type `T` satisties `sus::mem::NeverValueField`, then Option<T> will
96+
/// If a type `T` satisties `sus::mem::NeverValueField`, then `Option<T>` will
9197
/// have the same size as T.
9298
template <class T>
9399
class Option final {
@@ -101,7 +107,7 @@ class Option final {
101107
public:
102108
/// Default-construct an Option that is holding no value.
103109
///
104-
/// The Option's contained type `T` must be #Default.
110+
/// The Option's contained type `T` must satisfy `sus::construct::Default`.
105111
inline constexpr Option() noexcept = default;
106112

107113
/// Construct an Option that is holding the given value.
@@ -123,7 +129,7 @@ class Option final {
123129
}
124130
}
125131

126-
static inline constexpr Option some(T t) noexcept
132+
static inline constexpr Option some(T& t) noexcept
127133
requires(std::is_reference_v<T>)
128134
{
129135
return Option(move_to_storage(t));
@@ -442,13 +448,11 @@ class Option final {
442448

443449
/// Returns the contained value inside the Option, if there is one.
444450
/// Otherwise, returns the result of the given function.
445-
template <class Functor>
446-
requires(std::is_same_v<std::invoke_result_t<Functor>, T>)
447-
constexpr T unwrap_or_else(Functor f) && noexcept {
451+
constexpr T unwrap_or_else(::sus::fn::FnOnce<T()> auto&& f) && noexcept {
448452
if (t_.state() == Some) {
449453
return t_.take_and_set_none();
450454
} else {
451-
return f();
455+
return ::sus::run_once(::sus::move(f));
452456
}
453457
}
454458

@@ -517,8 +521,9 @@ class Option final {
517521
/// This method differs from <unwrap_or_else>() in that it does not consume
518522
/// the Option, and instead it can not be called on rvalues.
519523
T& get_or_insert_with(::sus::fn::FnOnce<T()> auto&& f) & noexcept {
520-
if (t_.state() == None)
521-
t_.construct_from_none(move_to_storage(::sus::move(f)()));
524+
if (t_.state() == None) {
525+
t_.construct_from_none(move_to_storage(::sus::run_once(::sus::move(f))));
526+
}
522527
return t_.val_mut();
523528
}
524529

@@ -542,11 +547,11 @@ class Option final {
542547
///
543548
/// Returns an `Option<R>` in state #None if the current Option is in state
544549
/// #None.
545-
constexpr auto map(
546-
::sus::fn::FnOnce<::sus::fn::NonVoid(T&&)> auto&& m) && noexcept {
547-
using R = std::invoke_result_t<decltype(m), T&&>;
550+
template <::sus::fn::FnOnce<::sus::fn::NonVoid(T&&)> MapFn, int&...,
551+
class R = std::invoke_result_t<MapFn&&, T&&>>
552+
constexpr auto map(MapFn&& m) && noexcept {
548553
if (t_.state() == Some) {
549-
return Option<R>(::sus::move(m)(t_.take_and_set_none()));
554+
return Option<R>(::sus::run_once(::sus::move(m), t_.take_and_set_none()));
550555
} else {
551556
return Option<R>::none();
552557
}
@@ -562,23 +567,24 @@ class Option final {
562567
class R = std::invoke_result_t<MapFn&&, T&&>>
563568
constexpr R map_or(R default_result, MapFn&& m) && noexcept {
564569
if (t_.state() == Some) {
565-
return ::sus::move(m)(t_.take_and_set_none());
570+
return ::sus::run_once(::sus::move(m), t_.take_and_set_none());
566571
} else {
567572
return default_result;
568573
}
569574
}
570575

571576
/// Computes a default function result (if none), or applies a different
572577
/// function to the contained value (if any).
573-
template <class DefaultFn, class MapFn, int&...,
578+
template <::sus::fn::FnOnce<::sus::fn::NonVoid()> DefaultFn,
579+
::sus::fn::FnOnce<::sus::fn::NonVoid(T&&)> MapFn, int&...,
574580
class D = std::invoke_result_t<DefaultFn>,
575581
class R = std::invoke_result_t<MapFn, T&&>>
576-
requires(!std::is_void_v<R> && std::is_same_v<D, R>)
577-
constexpr R map_or_else(DefaultFn default_fn, MapFn m) && noexcept {
582+
requires(std::is_same_v<D, R>)
583+
constexpr R map_or_else(DefaultFn&& default_fn, MapFn&& m) && noexcept {
578584
if (t_.state() == Some) {
579-
return m(t_.take_and_set_none());
585+
return ::sus::run_once(::sus::move(m), t_.take_and_set_none());
580586
} else {
581-
return default_fn();
587+
return ::sus::run_once(::sus::move(default_fn));
582588
}
583589
}
584590

@@ -588,11 +594,10 @@ class Option final {
588594
/// #None.
589595
///
590596
/// The predicate function must take `const T&` and return `bool`.
591-
template <class Predicate>
592-
requires(std::is_same_v<std::invoke_result_t<Predicate, const T&>, bool>)
593-
constexpr Option<T> filter(Predicate p) && noexcept {
597+
constexpr Option<T> filter(
598+
::sus::fn::FnOnce<bool(const T&)> auto&& p) && noexcept {
594599
if (t_.state() == Some) {
595-
if (p(t_.val())) {
600+
if (::sus::run_once(::sus::move(p), t_.val())) {
596601
return Option(t_.take_and_set_none());
597602
} else {
598603
// The state has to become None, and we must destroy the inner T.
@@ -617,19 +622,22 @@ class Option final {
617622
}
618623

619624
/// Consumes this Option and returns an Option with #None if this Option holds
620-
/// #None, otherwise calls `f` with the contained value and returns an Option
621-
/// with the result.
625+
/// #None, otherwise calls `f` with the contained value and returns the
626+
/// result.
627+
///
628+
/// The function `f` receives the Option's inner `T` and can return any
629+
/// `Option<U>`.
622630
///
623631
/// Some languages call this operation flatmap.
624-
template <
625-
class AndFn, int&..., class R = std::invoke_result_t<AndFn, T&&>,
626-
class InnerR = ::sus::option::__private::IsOptionType<R>::inner_type>
632+
template <::sus::fn::FnOnce<::sus::fn::NonVoid(T&&)> AndFn, int&...,
633+
class R = std::invoke_result_t<AndFn, T&&>,
634+
class U = ::sus::option::__private::IsOptionType<R>::inner_type>
627635
requires(::sus::option::__private::IsOptionType<R>::value)
628-
constexpr Option<InnerR> and_then(AndFn f) && noexcept {
636+
constexpr Option<U> and_then(AndFn&& f) && noexcept {
629637
if (t_.state() == Some)
630-
return f(t_.take_and_set_none());
638+
return ::sus::run_once(::sus::move(f), t_.take_and_set_none());
631639
else
632-
return Option<InnerR>::none();
640+
return Option<U>::none();
633641
}
634642

635643
/// Consumes and returns an Option with the same value if this Option contains
@@ -643,13 +651,12 @@ class Option final {
643651

644652
/// Consumes and returns an Option with the same value if this Option contains
645653
/// a value, otherwise returns the Option returned by `f`.
646-
template <class ElseFn, int&..., class R = std::invoke_result_t<ElseFn>>
647-
requires(std::is_same_v<R, Option<T>>)
648-
constexpr Option<T> or_else(ElseFn f) && noexcept {
654+
constexpr Option<T> or_else(
655+
::sus::fn::FnOnce<Option<T>()> auto&& f) && noexcept {
649656
if (t_.state() == Some)
650657
return Option(t_.take_and_set_none());
651658
else
652-
return ::sus::move(f)();
659+
return ::sus::run_once(::sus::move(f));
653660
}
654661

655662
/// Consumes this Option and returns an Option, holding the value from either
@@ -694,15 +701,16 @@ class Option final {
694701
/// `Ok(v)` and `None` to `Err(f())`.
695702
//
696703
// TODO: No refs in Result: https://github.com/chromium/subspace/issues/133
697-
template <class ElseFn, int&..., class E = std::invoke_result_t<ElseFn>,
704+
template <::sus::fn::FnOnce<::sus::fn::NonVoid()> ElseFn, int&...,
705+
class E = std::invoke_result_t<ElseFn>,
698706
class Result = ::sus::result::Result<T, E>>
699-
constexpr Result ok_or_else(ElseFn f) && noexcept
707+
constexpr Result ok_or_else(ElseFn&& f) && noexcept
700708
requires(!std::is_reference_v<T> && !std::is_reference_v<E>)
701709
{
702710
if (t_.state() == Some)
703711
return Result::with(t_.take_and_set_none());
704712
else
705-
return Result::with_err(::sus::move(f)());
713+
return Result::with_err(::sus::run_once(::sus::move(f)));
706714
}
707715

708716
/// Transposes an #Option of a #Result into a #Result of an #Option.
@@ -836,10 +844,13 @@ class Option final {
836844
// Calling as_ref() on an rvalue is not returning a reference to the inner
837845
// value if the inner value is already a reference, so we allow calling it on
838846
// an rvalue Option in that case.
839-
constexpr Option<const std::remove_reference_t<T>&> as_ref() const&& noexcept
847+
constexpr Option<const std::remove_reference_t<T>&> as_ref() && noexcept
840848
requires(std::is_reference_v<T>)
841849
{
842-
return as_ref();
850+
if (t_.state() == None)
851+
return Option<const std::remove_reference_t<T>&>::none();
852+
else
853+
return Option<const std::remove_reference_t<T>&>(t_.take_and_set_none());
843854
}
844855

845856
/// Returns an Option<T&> from this Option<T>, that either holds #None or a
@@ -853,18 +864,31 @@ class Option final {
853864
// Calling as_mut() on an rvalue is not returning a reference to the inner
854865
// value if the inner value is already a reference, so we allow calling it on
855866
// an rvalue Option in that case.
856-
constexpr Option<const std::remove_reference_t<T>&> as_mut() && noexcept
867+
constexpr Option<T&> as_mut() && noexcept
857868
requires(std::is_reference_v<T>)
858869
{
859-
return as_mut();
870+
if (t_.state() == None)
871+
return Option<T&>::none();
872+
else
873+
return Option<T&>(t_.take_and_set_none());
860874
}
861875

862876
constexpr Once<const std::remove_reference_t<T>&> iter() const& noexcept {
863877
return Once<const std::remove_reference_t<T>&>::with(as_ref());
864878
}
865-
constexpr Once<const T&> iter() const&& = delete;
879+
constexpr Once<const std::remove_reference_t<T>&> iter() && noexcept
880+
requires(std::is_reference_v<T>)
881+
{
882+
return Once<const std::remove_reference_t<T>&>::with(
883+
::sus::move(*this).as_ref());
884+
}
866885

867886
constexpr Once<T&> iter_mut() & noexcept { return Once<T&>::with(as_mut()); }
887+
constexpr Once<T&> iter_mut() && noexcept
888+
requires(std::is_reference_v<T>)
889+
{
890+
return Once<T&>::with(::sus::move(*this).as_mut());
891+
}
868892

869893
constexpr Once<T> into_iter() && noexcept { return Once<T>::with(take()); }
870894

subspace/option/option_unittest.cc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,6 +1301,12 @@ TEST(Option, AsRef) {
13011301
static constexpr int ci = 2;
13021302
constexpr auto icx = Option<const int&>::some(ci);
13031303
static_assert(&icx.as_ref().unwrap() == &ci);
1304+
1305+
// as_ref() on an rvalue allowed if the Option is holding a reference.
1306+
static_assert(std::is_same_v<decltype(Option<NoCopyMove&>::some(i).as_ref()),
1307+
Option<const NoCopyMove&>>);
1308+
EXPECT_EQ(&Option<NoCopyMove&>::some(i).as_ref().unwrap(), &i);
1309+
EXPECT_TRUE(Option<NoCopyMove&>::none().as_ref().is_none());
13041310
}
13051311

13061312
TEST(Option, AsMut) {
@@ -1324,6 +1330,12 @@ TEST(Option, AsMut) {
13241330
auto o = Option<int>::some(3);
13251331
return o.as_mut().unwrap();
13261332
}() == 3);
1333+
1334+
// as_mut() on an rvalue allowed if the Option is holding a reference.
1335+
static_assert(std::is_same_v<decltype(Option<NoCopyMove&>::some(i).as_mut()),
1336+
Option<NoCopyMove&>>);
1337+
EXPECT_EQ(&Option<NoCopyMove&>::some(i).as_mut().unwrap(), &i);
1338+
EXPECT_TRUE(Option<NoCopyMove&>::none().as_mut().is_none());
13271339
}
13281340

13291341
TEST(Option, TrivialMove) {
@@ -1489,6 +1501,14 @@ TEST(Option, Iter) {
14891501
++count;
14901502
}
14911503
EXPECT_EQ(count, 1);
1504+
1505+
// Iterating on an rvalue is okay if it's holding a reference.
1506+
for (const NoCopyMove& i : Option<NoCopyMove&>::some(mref(n))) {
1507+
EXPECT_EQ(&i, &n);
1508+
}
1509+
for (const NoCopyMove& i : Option<NoCopyMove&>::some(mref(n)).iter()) {
1510+
EXPECT_EQ(&i, &n);
1511+
}
14921512
}
14931513

14941514
TEST(Option, IterMut) {
@@ -1521,6 +1541,11 @@ TEST(Option, IterMut) {
15211541
++count;
15221542
}
15231543
EXPECT_EQ(count, 1);
1544+
1545+
// Iterating on an rvalue is okay if it's holding a reference.
1546+
for (NoCopyMove& i : Option<NoCopyMove&>::some(mref(n)).iter_mut()) {
1547+
EXPECT_EQ(&i, &n);
1548+
}
15241549
}
15251550

15261551
struct MoveOnly {

0 commit comments

Comments
 (0)