/*
- * Copyright 2017 Facebook, Inc.
+ * Copyright 2016-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
/**
* Like folly::Optional, but can store a value *or* an error.
*
#include <glog/logging.h>
+#include <folly/CPortability.h>
+#include <folly/CppAttributes.h>
#include <folly/Likely.h>
#include <folly/Optional.h>
#include <folly/Portability.h>
#include <folly/Traits.h>
#include <folly/Unit.h>
#include <folly/Utility.h>
+#include <folly/lang/ColdClass.h>
#define FOLLY_EXPECTED_ID(X) FB_CONCATENATE(FB_CONCATENATE(Folly, X), __LINE__)
/**
* An exception type thrown by Expected on catastrophic logic errors.
*/
-class BadExpectedAccess : public std::logic_error {
+class FOLLY_EXPORT BadExpectedAccess : public std::logic_error {
public:
BadExpectedAccess() : std::logic_error("bad Expected access") {}
};
+namespace expected_detail {
+
+[[noreturn]] void throwBadExpectedAccess();
+
+} // namespace expected_detail
+
/**
* Unexpected - a helper type used to disambiguate the construction of
* Expected objects in the error state.
*/
template <class Error>
-class Unexpected final {
+class Unexpected final : ColdClass {
template <class E>
friend class Unexpected;
template <class V, class E>
* when the user tries to access the nested value but the Expected object is
* actually storing an error code.
*/
- class BadExpectedAccess : public folly::BadExpectedAccess {
+ class FOLLY_EXPORT BadExpectedAccess : public folly::BadExpectedAccess {
public:
explicit BadExpectedAccess(Error err)
: folly::BadExpectedAccess{}, error_(std::move(err)) {}
void swap(Expected& that) noexcept(
expected_detail::StrictAllOf<IsNothrowSwappable, Value, Error>::value) {
if (this->uninitializedByException() || that.uninitializedByException()) {
- throw BadExpectedAccess();
+ expected_detail::throwBadExpectedAccess();
}
using std::swap;
if (*this) {
* Accessors
*/
constexpr bool hasValue() const noexcept {
- return expected_detail::Which::eValue == this->which_;
+ return LIKELY(expected_detail::Which::eValue == this->which_);
}
constexpr bool hasError() const noexcept {
- return expected_detail::Which::eError == this->which_;
+ return UNLIKELY(expected_detail::Which::eError == this->which_);
}
using Base::uninitializedByException;
std::declval<const Base&>(),
std::declval<Fns>()...)) {
if (this->uninitializedByException()) {
- throw BadExpectedAccess();
+ expected_detail::throwBadExpectedAccess();
}
return expected_detail::ExpectedHelper::then_(
base(), static_cast<Fns&&>(fns)...);
std::declval<Base&>(),
std::declval<Fns>()...)) {
if (this->uninitializedByException()) {
- throw BadExpectedAccess();
+ expected_detail::throwBadExpectedAccess();
}
return expected_detail::ExpectedHelper::then_(
base(), static_cast<Fns&&>(fns)...);
std::declval<Base&&>(),
std::declval<Fns>()...)) {
if (this->uninitializedByException()) {
- throw BadExpectedAccess();
+ expected_detail::throwBadExpectedAccess();
}
return expected_detail::ExpectedHelper::then_(
std::move(base()), static_cast<Fns&&>(fns)...);
std::declval<Yes>()(std::declval<const Value&>())) {
using Ret = decltype(std::declval<Yes>()(std::declval<const Value&>()));
if (this->uninitializedByException()) {
- throw BadExpectedAccess();
+ expected_detail::throwBadExpectedAccess();
}
return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
std::declval<Yes>()(std::declval<Value&>())) {
using Ret = decltype(std::declval<Yes>()(std::declval<Value&>()));
if (this->uninitializedByException()) {
- throw BadExpectedAccess();
+ expected_detail::throwBadExpectedAccess();
}
return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
std::declval<Yes>()(std::declval<Value&&>())) {
using Ret = decltype(std::declval<Yes>()(std::declval<Value&&>()));
if (this->uninitializedByException()) {
- throw BadExpectedAccess();
+ expected_detail::throwBadExpectedAccess();
}
return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
std::move(base()), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
if (LIKELY(hasError())) {
throw typename Unexpected<Error>::BadExpectedAccess(this->error_);
}
- throw BadExpectedAccess();
+ expected_detail::throwBadExpectedAccess();
}
}
void requireError() const {
if (UNLIKELY(!hasError())) {
- throw BadExpectedAccess();
+ expected_detail::throwBadExpectedAccess();
}
}
operator==(
const Expected<Value, Error>& lhs,
const Expected<Value, Error>& rhs) {
- if (UNLIKELY(lhs.which_ != rhs.which_)) {
- return UNLIKELY(lhs.uninitializedByException()) ? false
- : throw BadExpectedAccess();
- }
if (UNLIKELY(lhs.uninitializedByException())) {
- throw BadExpectedAccess();
+ expected_detail::throwBadExpectedAccess();
+ }
+ if (UNLIKELY(lhs.which_ != rhs.which_)) {
+ return false;
}
if (UNLIKELY(lhs.hasError())) {
return true; // All error states are considered equal
const Expected<Value, Error>& rhs) {
if (UNLIKELY(
lhs.uninitializedByException() || rhs.uninitializedByException())) {
- throw BadExpectedAccess();
+ expected_detail::throwBadExpectedAccess();
}
if (UNLIKELY(lhs.hasError())) {
return !rhs.hasError();
}
} // namespace folly
-// This makes folly::Optional<Value> useable as a coroutine return type..
-FOLLY_NAMESPACE_STD_BEGIN
+// This makes folly::Expected<Value> useable as a coroutine return type...
+namespace std {
namespace experimental {
template <typename Value, typename Error, typename... Args>
struct coroutine_traits<folly::Expected<Value, Error>, Args...> {
using promise_type = folly::expected_detail::Promise<Value, Error>;
};
} // namespace experimental
-FOLLY_NAMESPACE_STD_END
+} // namespace std
#endif // FOLLY_HAS_COROUTINES