Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 44c11c9f authored by Dominik Laskowski's avatar Dominik Laskowski Committed by Android (Google) Code Review
Browse files

Merge "FTL: Add Optional<T>::or_else" into udc-dev

parents 54942141 07a71cdb
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -54,5 +54,15 @@ struct and_then_result {
template <typename F, typename T>
using and_then_result_t = typename and_then_result<F, T>::type;

template <typename F, typename T>
struct or_else_result {
  using type = remove_cvref_t<std::invoke_result_t<F>>;
  static_assert(std::is_same_v<type, std::optional<T>> || std::is_same_v<type, Optional<T>>,
                "or_else function must return an optional T");
};

template <typename F, typename T>
using or_else_result_t = typename or_else_result<F, T>::type;

}  // namespace details
}  // namespace android::ftl
+13 −1
Original line number Diff line number Diff line
@@ -96,13 +96,25 @@ struct Optional final : std::optional<T> {
    return R();
  }

  // Returns this Optional<T> if not nullopt, or else the Optional<T> returned by the function F.
  template <typename F>
  constexpr auto or_else(F&& f) const& -> details::or_else_result_t<F, T> {
    if (has_value()) return *this;
    return std::forward<F>(f)();
  }

  template <typename F>
  constexpr auto or_else(F&& f) && -> details::or_else_result_t<F, T> {
    if (has_value()) return std::move(*this);
    return std::forward<F>(f)();
  }

  // Delete new for this class. Its base doesn't have a virtual destructor, and
  // if it got deleted via base class pointer, it would cause undefined
  // behavior. There's not a good reason to allocate this object on the heap
  // anyway.
  static void* operator new(size_t) = delete;
  static void* operator new[](size_t) = delete;

};

template <typename T, typename U>
+40 −0
Original line number Diff line number Diff line
@@ -164,6 +164,46 @@ TEST(Optional, AndThen) {
                     }));
}

TEST(Optional, OrElse) {
  // Non-empty.
  {
    const Optional opt = false;
    EXPECT_EQ(false, opt.or_else([] { return Optional(true); }));
    EXPECT_EQ('x', Optional('x').or_else([] { return std::make_optional('y'); }));
  }

  // Empty.
  {
    const Optional<int> opt;
    EXPECT_EQ(123, opt.or_else([]() -> Optional<int> { return 123; }));
    EXPECT_EQ("abc"s, Optional<std::string>().or_else([] { return Optional("abc"s); }));
  }
  {
    bool empty = false;
    EXPECT_EQ(Optional<float>(), Optional<float>().or_else([&empty]() -> Optional<float> {
      empty = true;
      return std::nullopt;
    }));
    EXPECT_TRUE(empty);
  }

  // Chaining.
  using StringVector = StaticVector<std::string, 3>;
  EXPECT_EQ(999, Optional(StaticVector{"1"s, "0"s, "0"s})
                     .and_then([](StringVector&& v) -> Optional<StringVector> {
                       if (v.push_back("0"s)) return v;
                       return {};
                     })
                     .or_else([] {
                       return Optional(StaticVector{"9"s, "9"s, "9"s});
                     })
                     .transform([](const StringVector& v) {
                       return std::accumulate(v.begin(), v.end(), std::string());
                     })
                     .and_then(parse_int)
                     .or_else([] { return Optional(-1); }));
}

// Comparison.
namespace {