namespace folly {
+template <class Value>
+class Optional;
+
namespace detail {
struct NoneHelper {};
// If exceptions are disabled, std::terminate() will be called instead of
// throwing OptionalEmptyException when the condition fails.
[[noreturn]] void throw_optional_empty_exception();
-}
+
+template <class Value>
+struct OptionalPromiseReturn;
+} // namespace detail
typedef int detail::NoneHelper::*None;
storage_.construct(std::forward<Args>(args)...);
}
+ // Used only when an Optional is used with coroutines on MSVC
+ /* implicit */ Optional(const detail::OptionalPromiseReturn<Value>& p)
+ : Optional{} {
+ p.promise_->value_ = this;
+ }
+
void assign(const None&) {
clear();
}
}
};
FOLLY_NAMESPACE_STD_END
+
+// Enable the use of folly::Optional with `co_await`
+// Inspired by https://github.com/toby-allsopp/coroutine_monad
+#if FOLLY_HAS_COROUTINES
+#include <experimental/coroutine>
+
+namespace folly {
+namespace detail {
+template <typename Value>
+struct OptionalPromise;
+
+template <typename Value>
+struct OptionalPromiseReturn {
+ Optional<Value> storage_;
+ OptionalPromise<Value>* promise_;
+ /* implicit */ OptionalPromiseReturn(OptionalPromise<Value>& promise) noexcept
+ : promise_(&promise) {
+ promise.value_ = &storage_;
+ }
+ OptionalPromiseReturn(OptionalPromiseReturn&& that) noexcept
+ : OptionalPromiseReturn{*that.promise_} {}
+ ~OptionalPromiseReturn() {}
+ /* implicit */ operator Optional<Value>() & {
+ return std::move(storage_);
+ }
+};
+
+template <typename Value>
+struct OptionalPromise {
+ Optional<Value>* value_ = nullptr;
+ OptionalPromise() = default;
+ OptionalPromise(OptionalPromise const&) = delete;
+ // This should work regardless of whether the compiler generates:
+ // folly::Optional<Value> retobj{ p.get_return_object(); } // MSVC
+ // or:
+ // auto retobj = p.get_return_object(); // clang
+ OptionalPromiseReturn<Value> get_return_object() noexcept {
+ return *this;
+ }
+ std::experimental::suspend_never initial_suspend() const noexcept {
+ return {};
+ }
+ std::experimental::suspend_never final_suspend() const {
+ return {};
+ }
+ template <typename U>
+ void return_value(U&& u) {
+ *value_ = static_cast<U&&>(u);
+ }
+ void unhandled_exception() {
+ // Technically, throwing from unhandled_exception is underspecified:
+ // https://github.com/GorNishanov/CoroutineWording/issues/17
+ throw;
+ }
+};
+
+template <typename Value>
+struct OptionalAwaitable {
+ Optional<Value> o_;
+ bool await_ready() const noexcept {
+ return o_.hasValue();
+ }
+ Value await_resume() {
+ return o_.value();
+ }
+ template <typename CoroHandle>
+ void await_suspend(CoroHandle h) const {
+ // make sure the coroutine returns an empty Optional:
+ h.promise().value_->clear();
+ // Abort the rest of the coroutine:
+ h.destroy();
+ }
+};
+} // namespace detail
+
+template <typename Value>
+detail::OptionalAwaitable<Value>
+/* implicit */ operator co_await(Optional<Value> o) {
+ return {std::move(o)};
+}
+} // namespace folly
+
+// This makes std::optional<Value> useable as a coroutine return type..
+FOLLY_NAMESPACE_STD_BEGIN
+namespace experimental {
+template <typename Value, typename... Args>
+struct coroutine_traits<folly::Optional<Value>, Args...> {
+ using promise_type = folly::detail::OptionalPromise<Value>;
+};
+} // experimental
+FOLLY_NAMESPACE_STD_END
+#endif // FOLLY_HAS_COROUTINES
--- /dev/null
+/*
+ * Copyright 2017 Facebook, Inc.
+ *
+ * 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 <folly/Optional.h>
+#include <folly/Portability.h>
+#include <folly/portability/GTest.h>
+
+#if FOLLY_HAS_COROUTINES
+using folly::Optional;
+
+Optional<int> f1() {
+ return 7;
+}
+Optional<double> f2(int x) {
+ return 2.0 * x;
+}
+Optional<int> f3(int x, double y) {
+ return (int)(x + y);
+}
+
+TEST(Optional, CoroutineSuccess) {
+ auto r0 = []() -> Optional<int> {
+ auto x = co_await f1();
+ EXPECT_EQ(7, x);
+ auto y = co_await f2(x);
+ EXPECT_EQ(2.0 * 7, y);
+ auto z = co_await f3(x, y);
+ EXPECT_EQ((int)(2.0 * 7 + 7), z);
+ co_return z;
+ }();
+ EXPECT_TRUE(r0.hasValue());
+ EXPECT_EQ(21, *r0);
+}
+
+Optional<int> f4(int, double) {
+ return folly::none;
+}
+
+TEST(Optional, CoroutineFailure) {
+ auto r1 = []() -> Optional<int> {
+ auto x = co_await f1();
+ auto y = co_await f2(x);
+ auto z = co_await f4(x, y);
+ EXPECT_FALSE(true);
+ co_return z;
+ }();
+ EXPECT_TRUE(!r1.hasValue());
+}
+
+Optional<int> throws() {
+ throw 42;
+}
+
+TEST(Optional, CoroutineException) {
+ try {
+ auto r2 = []() -> Optional<int> {
+ auto x = co_await throws();
+ EXPECT_FALSE(true);
+ co_return x;
+ }();
+ EXPECT_FALSE(true);
+ } catch (/* nolint */ int i) {
+ EXPECT_EQ(42, i);
+ } catch (...) {
+ EXPECT_FALSE(true);
+ }
+}
+#endif