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

Commit a7e22553 authored by Dominik Laskowski's avatar Dominik Laskowski
Browse files

FTL: Add Optional<T>::transform

Bug: 185536303
Test: ftl_test
Change-Id: If4cb9894c615499af04bb9793d9f900485e18fe2
parent f2ddca6b
Loading
Loading
Loading
Loading

include/ftl/optional.h

0 → 100644
+71 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <functional>
#include <optional>
#include <type_traits>
#include <utility>

namespace android::ftl {

// Superset of std::optional<T> with monadic operations, as proposed in https://wg21.link/P0798R8.
//
// TODO: Remove in C++23.
//
template <typename T>
struct Optional final : std::optional<T> {
  using std::optional<T>::optional;

  using std::optional<T>::has_value;
  using std::optional<T>::value;

  // Returns Optional<U> where F is a function that maps T to U.
  template <typename F>
  constexpr auto transform(F&& f) const& {
    using U = std::remove_cv_t<std::invoke_result_t<F, decltype(value())>>;
    if (has_value()) return Optional<U>(std::invoke(std::forward<F>(f), value()));
    return Optional<U>();
  }

  template <typename F>
  constexpr auto transform(F&& f) & {
    using U = std::remove_cv_t<std::invoke_result_t<F, decltype(value())>>;
    if (has_value()) return Optional<U>(std::invoke(std::forward<F>(f), value()));
    return Optional<U>();
  }

  template <typename F>
  constexpr auto transform(F&& f) const&& {
    using U = std::invoke_result_t<F, decltype(std::move(value()))>;
    if (has_value()) return Optional<U>(std::invoke(std::forward<F>(f), std::move(value())));
    return Optional<U>();
  }

  template <typename F>
  constexpr auto transform(F&& f) && {
    using U = std::invoke_result_t<F, decltype(std::move(value()))>;
    if (has_value()) return Optional<U>(std::invoke(std::forward<F>(f), std::move(value())));
    return Optional<U>();
  }
};

// Deduction guide.
template <typename T>
Optional(T) -> Optional<T>;

}  // namespace android::ftl
+1 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ cc_test {
        "fake_guard_test.cpp",
        "flags_test.cpp",
        "future_test.cpp",
        "optional_test.cpp",
        "small_map_test.cpp",
        "small_vector_test.cpp",
        "static_vector_test.cpp",
+77 −0
Original line number Diff line number Diff line
/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <ftl/optional.h>
#include <ftl/static_vector.h>
#include <ftl/string.h>
#include <gtest/gtest.h>

#include <functional>
#include <numeric>
#include <utility>

using namespace std::placeholders;
using namespace std::string_literals;

namespace android::test {

using ftl::Optional;
using ftl::StaticVector;

TEST(Optional, Transform) {
  // Empty.
  EXPECT_EQ(std::nullopt, Optional<int>().transform([](int) { return 0; }));

  // By value.
  EXPECT_EQ(0, Optional(0).transform([](int x) { return x; }));
  EXPECT_EQ(100, Optional(99).transform([](int x) { return x + 1; }));
  EXPECT_EQ("0b100"s, Optional(4).transform(std::bind(ftl::to_string<int>, _1, ftl::Radix::kBin)));

  // By reference.
  {
    Optional opt = 'x';
    EXPECT_EQ('z', opt.transform([](char& c) {
      c = 'y';
      return 'z';
    }));

    EXPECT_EQ('y', opt);
  }

  // By rvalue reference.
  {
    std::string out;
    EXPECT_EQ("xyz"s, Optional("abc"s).transform([&out](std::string&& str) {
      out = std::move(str);
      return "xyz"s;
    }));

    EXPECT_EQ(out, "abc"s);
  }

  // Chaining.
  EXPECT_EQ(14u, Optional(StaticVector{"upside"s, "down"s})
                     .transform([](StaticVector<std::string, 3>&& v) {
                       v.push_back("cake"s);
                       return v;
                     })
                     .transform([](const StaticVector<std::string, 3>& v) {
                       return std::accumulate(v.begin(), v.end(), std::string());
                     })
                     .transform([](const std::string& s) { return s.length(); }));
}

}  // namespace android::test