diff options
author | 2022-08-01 08:23:34 -0700 | |
---|---|---|
committer | 2022-08-19 07:50:56 -0700 | |
commit | a7e22553cfee75447d9e3a4337f5ccc0baba5d16 (patch) | |
tree | 800688434b4535cba49bbd21bd1a0fe16df2c2d9 | |
parent | f2ddca6b914611f40f998fccc7a40921a001c094 (diff) |
FTL: Add Optional<T>::transform
Bug: 185536303
Test: ftl_test
Change-Id: If4cb9894c615499af04bb9793d9f900485e18fe2
-rw-r--r-- | include/ftl/optional.h | 71 | ||||
-rw-r--r-- | libs/ftl/Android.bp | 1 | ||||
-rw-r--r-- | libs/ftl/optional_test.cpp | 77 |
3 files changed, 149 insertions, 0 deletions
diff --git a/include/ftl/optional.h b/include/ftl/optional.h new file mode 100644 index 0000000000..daf4502bd2 --- /dev/null +++ b/include/ftl/optional.h @@ -0,0 +1,71 @@ +/* + * 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 diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index c010a2e58a..8f89e7d151 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -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", diff --git a/libs/ftl/optional_test.cpp b/libs/ftl/optional_test.cpp new file mode 100644 index 0000000000..6a8c8f9d3d --- /dev/null +++ b/libs/ftl/optional_test.cpp @@ -0,0 +1,77 @@ +/* + * 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 |