Add folly::Expected, an alternative to exceptions for non-throwing APIs that can...
authorEric Niebler <eniebler@fb.com>
Mon, 15 Aug 2016 17:20:25 +0000 (10:20 -0700)
committerFacebook Github Bot <facebook-github-bot-bot@fb.com>
Mon, 15 Aug 2016 17:23:27 +0000 (10:23 -0700)
Summary:
Expected is like an Optional with extra state for reporting //why// the Expected is empty. Something like it is currently under review for inclusion in the C++ standard [1], and Andrei Alexandrescu has spoken about it [2]. It corresponds to the Either Monad in Haskell, where it is used as a return type of an API that has more than one failure mode.

By adding folly::Expected, we get a way to implement non-throwing APIs with a consistent and composable interface.

[^1]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4015.pdf
[^2]: https://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C

Reviewed By: mhx

Differential Revision: D3522501

fbshipit-source-id: 48b8ea2dfbd0769f26ec84d2d52fd41db75dc05a

folly/Expected.h [new file with mode: 0644]
folly/Makefile.am
folly/Optional.h
folly/Traits.h
folly/test/ExpectedTest.cpp [new file with mode: 0644]
folly/test/Makefile.am
folly/test/TraitsTest.cpp

diff --git a/folly/Expected.h b/folly/Expected.h
new file mode 100644 (file)
index 0000000..cde4a04
--- /dev/null
@@ -0,0 +1,1342 @@
+/*
+ * Copyright 2016 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.
+ */
+
+/**
+ * Like folly::Optional, but can store a value *or* and error.
+ *
+ * @author Eric Niebler (eniebler@fb.com)
+ */
+
+#pragma once
+
+#include <cstddef>
+#include <initializer_list>
+#include <new>
+#include <stdexcept>
+#include <type_traits>
+#include <utility>
+
+#include <folly/Likely.h>
+#include <folly/Portability.h>
+#include <folly/Preprocessor.h>
+#include <folly/Traits.h> // for construct_in_place_t
+#include <folly/Unit.h>
+
+#define FOLLY_EXPECTED_ID(X) FB_CONCATENATE(FB_CONCATENATE(Folly, X), __LINE__)
+
+#define FOLLY_REQUIRES_IMPL(...)                                            \
+  bool FOLLY_EXPECTED_ID(Requires) = false,                                 \
+       typename std::enable_if<                                             \
+           (FOLLY_EXPECTED_ID(Requires) || static_cast<bool>(__VA_ARGS__)), \
+           int>::type = 0
+
+#define FOLLY_REQUIRES_TRAILING(...) , FOLLY_REQUIRES_IMPL(__VA_ARGS__)
+
+#define FOLLY_REQUIRES(...) template <FOLLY_REQUIRES_IMPL(__VA_ARGS__)>
+
+/**
+ * gcc-4.7 warns about use of uninitialized memory around the use of storage_
+ * even though this is explicitly initialized at each point.
+ */
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuninitialized"
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif // __GNUC__
+
+namespace folly {
+
+/**
+ * Forward declarations
+ */
+template <class Error>
+class Unexpected;
+
+template <class Error>
+constexpr Unexpected<typename std::decay<Error>::type> makeUnexpected(Error&&);
+
+template <class Value, class Error>
+class Expected;
+
+template <class Error, class Value>
+constexpr Expected<typename std::decay<Value>::type, Error> makeExpected(
+    Value&&);
+
+/**
+ * Alias for an Expected type's assiciated value_type
+ */
+template <class Expected>
+using ExpectedValueType =
+    typename std::remove_reference<Expected>::type::value_type;
+
+/**
+ * Alias for an Expected type's assiciated error_type
+ */
+template <class Expected>
+using ExpectedErrorType =
+    typename std::remove_reference<Expected>::type::error_type;
+
+// Details...
+namespace expected_detail {
+template <template <class...> class Trait, class... Ts>
+using StrictAllOf = StrictConjunction<Trait<Ts>...>;
+
+template <class T>
+using IsCopyable = StrictConjunction<
+    std::is_copy_constructible<T>,
+    std::is_copy_assignable<T>>;
+
+template <class T>
+using IsMovable = StrictConjunction<
+    std::is_move_constructible<T>,
+    std::is_move_assignable<T>>;
+
+template <class T>
+using IsNothrowCopyable = StrictConjunction<
+    std::is_nothrow_copy_constructible<T>,
+    std::is_nothrow_copy_assignable<T>>;
+
+template <class T>
+using IsNothrowMovable = StrictConjunction<
+    std::is_nothrow_move_constructible<T>,
+    std::is_nothrow_move_assignable<T>>;
+
+template <class From, class To>
+using IsConvertible = StrictConjunction<
+    std::is_constructible<To, From>,
+    std::is_assignable<To&, From>>;
+
+template <class T, class U>
+auto doEmplaceAssign(int, T& t, U&& u) -> decltype(void(t = (U &&)u)) {
+  t = (U &&)u;
+}
+
+template <class T, class U>
+auto doEmplaceAssign(long, T& t, U&& u) -> decltype(void(T((U &&)u))) {
+  t.~T();
+  ::new ((void*)std::addressof(t)) T((U &&)u);
+}
+
+template <class T, class... Us>
+auto doEmplaceAssign(int, T& t, Us&&... us)
+    -> decltype(void(t = T((Us &&)us...))) {
+  t = T((Us &&)us...);
+}
+
+template <class T, class... Us>
+auto doEmplaceAssign(long, T& t, Us&&... us)
+    -> decltype(void(T((Us &&)us...))) {
+  t.~T();
+  ::new ((void*)std::addressof(t)) T((Us &&)us...);
+}
+
+struct EmptyTag {};
+struct ValueTag {};
+struct ErrorTag {};
+enum class Which : unsigned char { eEmpty, eValue, eError };
+enum class StorageType { ePODStruct, ePODUnion, eUnion };
+
+template <class Value, class Error>
+constexpr StorageType getStorageType() {
+  return StrictAllOf<IsTriviallyCopyable, Value, Error>::value
+      ? (sizeof(std::pair<Value, Error>) <= sizeof(void * [2]) &&
+                 StrictAllOf<std::is_trivial, Value, Error>::value
+             ? StorageType::ePODStruct
+             : StorageType::ePODUnion)
+      : StorageType::eUnion;
+}
+
+template <
+    class Value,
+    class Error,
+    StorageType = expected_detail::getStorageType<Value, Error>()> // ePODUnion
+struct ExpectedStorage {
+  using value_type = Value;
+  using error_type = Error;
+  union {
+    Value value_;
+    Error error_;
+    char ch_;
+  };
+  Which which_;
+
+  template <class E = Error, class = decltype(E())>
+  constexpr ExpectedStorage() noexcept(noexcept(E()))
+      : error_(), which_(Which::eError) {}
+  explicit constexpr ExpectedStorage(EmptyTag) noexcept
+      : ch_(), which_(Which::eEmpty) {}
+  template <class... Vs>
+  explicit constexpr ExpectedStorage(ValueTag, Vs&&... vs) noexcept(
+      noexcept(Value(static_cast<Vs&&>(vs)...)))
+      : value_(static_cast<Vs&&>(vs)...), which_(Which::eValue) {}
+  template <class... Es>
+  explicit constexpr ExpectedStorage(ErrorTag, Es&&... es) noexcept(
+      noexcept(Error(static_cast<Es&&>(es)...)))
+      : error_(static_cast<Es&&>(es)...), which_(Which::eError) {}
+  void clear() noexcept {}
+  static constexpr bool uninitializedByException() noexcept {
+    // Although which_ may temporarily be eEmpty during construction, it
+    // is always either eValue or eError for a fully-constructed Expected.
+    return false;
+  }
+  template <class... Vs>
+  void assignValue(Vs&&... vs) {
+    expected_detail::doEmplaceAssign(0, value_, static_cast<Vs&&>(vs)...);
+    which_ = Which::eValue;
+  }
+  template <class... Es>
+  void assignError(Es&&... es) {
+    expected_detail::doEmplaceAssign(0, error_, static_cast<Es&&>(es)...);
+    which_ = Which::eError;
+  }
+  template <class Other>
+  void assign(Other&& that) {
+    switch (that.which_) {
+      case Which::eValue:
+        this->assignValue(static_cast<Other&&>(that).value());
+        break;
+      case Which::eError:
+        this->assignError(static_cast<Other&&>(that).error());
+        break;
+      default:
+        this->clear();
+        break;
+    }
+  }
+  Value& value() & {
+    return value_;
+  }
+  const Value& value() const& {
+    return value_;
+  }
+  Value&& value() && {
+    return std::move(value_);
+  }
+  Error& error() & {
+    return error_;
+  }
+  const Error& error() const& {
+    return error_;
+  }
+  Error&& error() && {
+    return std::move(error_);
+  }
+};
+
+template <class Value, class Error>
+struct ExpectedUnion {
+  union {
+    Value value_;
+    Error error_;
+    char ch_;
+  };
+  Which which_;
+
+  explicit constexpr ExpectedUnion(EmptyTag = {}) noexcept
+      : ch_(), which_(Which::eEmpty) {}
+  template <class... Vs>
+  explicit constexpr ExpectedUnion(ValueTag, Vs&&... vs) noexcept(
+      noexcept(Value(static_cast<Vs&&>(vs)...)))
+      : value_(static_cast<Vs&&>(vs)...), which_(Which::eValue) {}
+  template <class... Es>
+  explicit constexpr ExpectedUnion(ErrorTag, Es&&... es) noexcept(
+      noexcept(Error(static_cast<Es&&>(es)...)))
+      : error_(static_cast<Es&&>(es)...), which_(Which::eError) {}
+  ExpectedUnion(const ExpectedUnion&) {}
+  ExpectedUnion(ExpectedUnion&&) noexcept {}
+  ExpectedUnion& operator=(const ExpectedUnion&) {
+    return *this;
+  }
+  ExpectedUnion& operator=(ExpectedUnion&&) noexcept {
+    return *this;
+  }
+  ~ExpectedUnion() {}
+  Value& value() & {
+    return value_;
+  }
+  const Value& value() const& {
+    return value_;
+  }
+  Value&& value() && {
+    return std::move(value_);
+  }
+  Error& error() & {
+    return error_;
+  }
+  const Error& error() const& {
+    return error_;
+  }
+  Error&& error() && {
+    return std::move(error_);
+  }
+};
+
+template <class Derived, bool, bool Noexcept>
+struct CopyConstructible {
+  CopyConstructible() = default;
+  CopyConstructible(const CopyConstructible& that) noexcept(Noexcept) {
+    static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that));
+  }
+  CopyConstructible(CopyConstructible&&) = default;
+  CopyConstructible& operator=(const CopyConstructible&) = default;
+  CopyConstructible& operator=(CopyConstructible&&) = default;
+};
+
+template <class Derived, bool Noexcept>
+struct CopyConstructible<Derived, false, Noexcept> {
+  CopyConstructible() = default;
+  CopyConstructible(const CopyConstructible&) = delete;
+  CopyConstructible(CopyConstructible&&) = default;
+  CopyConstructible& operator=(const CopyConstructible&) = default;
+  CopyConstructible& operator=(CopyConstructible&&) = default;
+};
+
+template <class Derived, bool, bool Noexcept>
+struct MoveConstructible {
+  MoveConstructible() = default;
+  MoveConstructible(const MoveConstructible&) = default;
+  MoveConstructible(MoveConstructible&& that) noexcept(Noexcept) {
+    static_cast<Derived*>(this)->assign(std::move(static_cast<Derived&>(that)));
+  }
+  MoveConstructible& operator=(const MoveConstructible&) = default;
+  MoveConstructible& operator=(MoveConstructible&&) = default;
+};
+
+template <class Derived, bool Noexcept>
+struct MoveConstructible<Derived, false, Noexcept> {
+  MoveConstructible() = default;
+  MoveConstructible(const MoveConstructible&) = default;
+  MoveConstructible(MoveConstructible&&) = delete;
+  MoveConstructible& operator=(const MoveConstructible&) = default;
+  MoveConstructible& operator=(MoveConstructible&&) = default;
+};
+
+template <class Derived, bool, bool Noexcept>
+struct CopyAssignable {
+  CopyAssignable() = default;
+  CopyAssignable(const CopyAssignable&) = default;
+  CopyAssignable(CopyAssignable&&) = default;
+  CopyAssignable& operator=(const CopyAssignable& that) noexcept(Noexcept) {
+    static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that));
+    return *this;
+  }
+  CopyAssignable& operator=(CopyAssignable&&) = default;
+};
+
+template <class Derived, bool Noexcept>
+struct CopyAssignable<Derived, false, Noexcept> {
+  CopyAssignable() = default;
+  CopyAssignable(const CopyAssignable&) = default;
+  CopyAssignable(CopyAssignable&&) = default;
+  CopyAssignable& operator=(const CopyAssignable&) = delete;
+  CopyAssignable& operator=(CopyAssignable&&) = default;
+};
+
+template <class Derived, bool, bool Noexcept>
+struct MoveAssignable {
+  MoveAssignable() = default;
+  MoveAssignable(const MoveAssignable&) = default;
+  MoveAssignable(MoveAssignable&&) = default;
+  MoveAssignable& operator=(const MoveAssignable&) = default;
+  MoveAssignable& operator=(MoveAssignable&& that) noexcept(Noexcept) {
+    static_cast<Derived*>(this)->assign(std::move(static_cast<Derived&>(that)));
+    return *this;
+  }
+};
+
+template <class Derived, bool Noexcept>
+struct MoveAssignable<Derived, false, Noexcept> {
+  MoveAssignable() = default;
+  MoveAssignable(const MoveAssignable&) = default;
+  MoveAssignable(MoveAssignable&&) = default;
+  MoveAssignable& operator=(const MoveAssignable&) = default;
+  MoveAssignable& operator=(MoveAssignable&& that) = delete;
+};
+
+template <class Value, class Error>
+struct ExpectedStorage<Value, Error, StorageType::eUnion>
+    : ExpectedUnion<Value, Error>,
+      CopyConstructible<
+          ExpectedStorage<Value, Error, StorageType::eUnion>,
+          StrictAllOf<std::is_copy_constructible, Value, Error>::value,
+          StrictAllOf<std::is_nothrow_copy_constructible, Value, Error>::value>,
+      MoveConstructible<
+          ExpectedStorage<Value, Error, StorageType::eUnion>,
+          StrictAllOf<std::is_move_constructible, Value, Error>::value,
+          StrictAllOf<std::is_nothrow_move_constructible, Value, Error>::value>,
+      CopyAssignable<
+          ExpectedStorage<Value, Error, StorageType::eUnion>,
+          StrictAllOf<IsCopyable, Value, Error>::value,
+          StrictAllOf<IsNothrowCopyable, Value, Error>::value>,
+      MoveAssignable<
+          ExpectedStorage<Value, Error, StorageType::eUnion>,
+          StrictAllOf<IsMovable, Value, Error>::value,
+          StrictAllOf<IsNothrowMovable, Value, Error>::value> {
+  using value_type = Value;
+  using error_type = Error;
+  using Base = ExpectedUnion<Value, Error>;
+  template <class E = Error, class = decltype(E())>
+  constexpr ExpectedStorage() noexcept(noexcept(E())) : Base{ErrorTag{}} {}
+  ExpectedStorage(const ExpectedStorage&) = default;
+  ExpectedStorage(ExpectedStorage&&) = default;
+  ExpectedStorage& operator=(const ExpectedStorage&) = default;
+  ExpectedStorage& operator=(ExpectedStorage&&) = default;
+  using ExpectedUnion<Value, Error>::ExpectedUnion;
+  ~ExpectedStorage() {
+    clear();
+  }
+  void clear() noexcept {
+    switch (this->which_) {
+      case Which::eValue:
+        this->value().~Value();
+        break;
+      case Which::eError:
+        this->error().~Error();
+        break;
+      default:
+        break;
+    }
+    this->which_ = Which::eEmpty;
+  }
+  bool uninitializedByException() const noexcept {
+    return this->which_ == Which::eEmpty;
+  }
+  template <class... Vs>
+  void assignValue(Vs&&... vs) {
+    if (this->which_ == Which::eValue) {
+      expected_detail::doEmplaceAssign(
+          0, this->value(), static_cast<Vs&&>(vs)...);
+    } else {
+      this->clear();
+      ::new ((void*)std::addressof(this->value()))
+          Value(static_cast<Vs&&>(vs)...);
+      this->which_ = Which::eValue;
+    }
+  }
+  template <class... Es>
+  void assignError(Es&&... es) {
+    if (this->which_ == Which::eError) {
+      expected_detail::doEmplaceAssign(
+          0, this->error(), static_cast<Es&&>(es)...);
+    } else {
+      this->clear();
+      ::new ((void*)std::addressof(this->error()))
+          Error(static_cast<Es&&>(es)...);
+      this->which_ = Which::eError;
+    }
+  }
+  bool isThis(const ExpectedStorage* that) const {
+    return this == that;
+  }
+  constexpr bool isSelfAssign(const void*) const {
+    return false;
+  }
+  template <class Other>
+  void assign(Other&& that) {
+    if (isSelfAssign(&that))
+      return;
+    switch (that.which_) {
+      case Which::eValue:
+        this->assignValue(static_cast<Other&&>(that).value());
+        break;
+      case Which::eError:
+        this->assignError(static_cast<Other&&>(that).error());
+        break;
+      default:
+        this->clear();
+        break;
+    }
+  }
+};
+
+// For small (pointer-sized) trivial types, a struct is faster than a union.
+template <class Value, class Error>
+struct ExpectedStorage<Value, Error, StorageType::ePODStruct> {
+  using value_type = Value;
+  using error_type = Error;
+  Which which_;
+  Error error_;
+  Value value_;
+
+  constexpr ExpectedStorage() noexcept
+      : which_(Which::eError), error_(), value_() {}
+  explicit constexpr ExpectedStorage(EmptyTag) noexcept
+      : which_(Which::eEmpty), error_(), value_() {}
+  template <class... Vs>
+  explicit constexpr ExpectedStorage(ValueTag, Vs&&... vs) noexcept(
+      noexcept(Value(static_cast<Vs&&>(vs)...)))
+      : which_(Which::eValue), error_(), value_(static_cast<Vs&&>(vs)...) {}
+  template <class... Es>
+  explicit constexpr ExpectedStorage(ErrorTag, Es&&... es) noexcept(
+      noexcept(Error(static_cast<Es&&>(es)...)))
+      : which_(Which::eError), error_(static_cast<Es&&>(es)...), value_() {}
+  void clear() noexcept {}
+  constexpr static bool uninitializedByException() noexcept {
+    return false;
+  }
+  template <class... Vs>
+  void assignValue(Vs&&... vs) {
+    expected_detail::doEmplaceAssign(0, value_, static_cast<Vs&&>(vs)...);
+    which_ = Which::eValue;
+  }
+  template <class... Es>
+  void assignError(Es&&... es) {
+    expected_detail::doEmplaceAssign(0, error_, static_cast<Es&&>(es)...);
+    which_ = Which::eError;
+  }
+  template <class Other>
+  void assign(Other&& that) {
+    switch (that.which_) {
+      case Which::eValue:
+        this->assignValue(static_cast<Other&&>(that).value());
+        break;
+      case Which::eError:
+        this->assignError(static_cast<Other&&>(that).error());
+        break;
+      default:
+        this->clear();
+        break;
+    }
+  }
+  Value& value() & {
+    return value_;
+  }
+  const Value& value() const& {
+    return value_;
+  }
+  Value&& value() && {
+    return std::move(value_);
+  }
+  Error& error() & {
+    return error_;
+  }
+  const Error& error() const& {
+    return error_;
+  }
+  Error&& error() && {
+    return std::move(error_);
+  }
+};
+
+namespace expected_detail_ExpectedHelper {
+// Tricky hack so that Expected::then can handle lambdas that return void
+template <class T>
+inline T&& operator,(T&& t, Unit) noexcept {
+  return static_cast<T&&>(t);
+}
+
+struct ExpectedHelper {
+  template <class Error, class T>
+  static constexpr Expected<T, Error> return_(T t) {
+    return folly::makeExpected<Error>(t);
+  }
+  template <
+      class Error,
+      class T,
+      class U FOLLY_REQUIRES_TRAILING(
+          expected_detail::IsConvertible<U&&, Error>::value)>
+  static constexpr Expected<T, Error> return_(Expected<T, U> t) {
+    return t;
+  }
+
+  template <class This>
+  static typename std::decay<This>::type then_(This&& ex) {
+    return static_cast<This&&>(ex);
+  }
+
+  template <
+      class This,
+      class Fn,
+      class... Fns,
+      class E = ExpectedErrorType<This>,
+      class T = ExpectedHelper>
+  static auto then_(This&& ex, Fn&& fn, Fns&&... fns) -> decltype(T::then_(
+      T::template return_<E>(
+          (std::declval<Fn>()(std::declval<This>().value()), unit)),
+      std::declval<Fns>()...)) {
+    if (LIKELY(ex.which_ == expected_detail::Which::eValue))
+      return T::then_(
+          T::template return_<E>(
+              // Uses the comma operator defined above IFF the lambda
+              // returns non-void.
+              (static_cast<Fn&&>(fn)(static_cast<This&&>(ex).value()), unit)),
+          static_cast<Fns&&>(fns)...);
+    return makeUnexpected(static_cast<This&&>(ex).error());
+  }
+
+  template <
+      class This,
+      class Yes,
+      class No,
+      class Ret = decltype(std::declval<Yes>()(std::declval<This>().value())),
+      class Err = decltype(std::declval<No>()(std::declval<This>().error()))
+          FOLLY_REQUIRES_TRAILING(!std::is_void<Err>::value)>
+  static Ret thenOrThrow_(This&& ex, Yes&& yes, No&& no) {
+    if (LIKELY(ex.which_ == expected_detail::Which::eValue))
+      return Ret(static_cast<Yes&&>(yes)(static_cast<This&&>(ex).value()));
+    throw static_cast<No&&>(no)(static_cast<This&&>(ex).error());
+  }
+
+  template <
+      class This,
+      class Yes,
+      class No,
+      class Ret = decltype(std::declval<Yes>()(std::declval<This>().value())),
+      class Err = decltype(std::declval<No>()(std::declval<This&>().error()))
+          FOLLY_REQUIRES_TRAILING(std::is_void<Err>::value)>
+  static Ret thenOrThrow_(This&& ex, Yes&& yes, No&& no) {
+    if (LIKELY(ex.which_ == expected_detail::Which::eValue))
+      return Ret(static_cast<Yes&&>(yes)(static_cast<This&&>(ex).value()));
+    static_cast<No&&>(no)(ex.error());
+    throw typename Unexpected<ExpectedErrorType<This>>::MakeBadExpectedAccess()(
+        static_cast<This&&>(ex).error());
+  }
+};
+}
+/* using override */ using expected_detail_ExpectedHelper::ExpectedHelper;
+
+struct UnexpectedTag {};
+
+} // namespace expected_detail
+
+using unexpected_t =
+    expected_detail::UnexpectedTag (&)(expected_detail::UnexpectedTag);
+
+inline expected_detail::UnexpectedTag unexpected(
+    expected_detail::UnexpectedTag = {}) {
+  return {};
+}
+
+/**
+ * An exception type thrown by Expected on catastrophic logic errors.
+ */
+class BadExpectedAccess : public std::logic_error {
+ public:
+  BadExpectedAccess() : std::logic_error("bad Expected access") {}
+};
+
+/**
+ * Unexpected - a helper type used to disambiguate the construction of
+ * Expected objects in the error state.
+ */
+template <class Error>
+class Unexpected final {
+  template <class E>
+  friend class Unexpected;
+  template <class V, class E>
+  friend class Expected;
+  friend struct expected_detail::ExpectedHelper;
+
+ public:
+  /**
+   * Unexpected::BadExpectedAccess - An exception type thrown by Expected
+   * when the user tries to access the nested value but the Expected object is
+   * actually storing an error code.
+   */
+  class BadExpectedAccess : public folly::BadExpectedAccess {
+   public:
+    explicit BadExpectedAccess(Error err)
+        : folly::BadExpectedAccess(), error_(std::move(err)) {}
+    /**
+     * The error code that was held by the Expected object when the user
+     * erroneously requested the value.
+     */
+    Error error() const {
+      return error_;
+    }
+
+   private:
+    Error error_;
+  };
+
+  /**
+   * Constructors
+   */
+  Unexpected() = default;
+  Unexpected(const Unexpected&) = default;
+  Unexpected(Unexpected&&) = default;
+  Unexpected& operator=(const Unexpected&) = default;
+  Unexpected& operator=(Unexpected&&) = default;
+  constexpr /* implicit */ Unexpected(const Error& err) : error_(err) {}
+  constexpr /* implicit */ Unexpected(Error&& err) : error_(std::move(err)) {}
+
+  template <class Other FOLLY_REQUIRES_TRAILING(
+      std::is_constructible<Error, Other&&>::value)>
+  constexpr /* implicit */ Unexpected(Unexpected<Other> that)
+      : error_(std::move(that.error())) {}
+
+  /**
+   * Assignment
+   */
+  template <class Other FOLLY_REQUIRES_TRAILING(
+      std::is_assignable<Error&, Other&&>::value)>
+  Unexpected& operator=(Unexpected<Other> that) {
+    error_ = std::move(that.error());
+  }
+
+  /**
+   * Observers
+   */
+  Error& error() & {
+    return error_;
+  }
+  const Error& error() const& {
+    return error_;
+  }
+  Error&& error() && {
+    return std::move(error_);
+  }
+
+  /**
+   * Relational operators
+   */
+  FOLLY_REQUIRES(IsEqualityComparable<Error>::value)
+  friend bool operator==(const Unexpected& lhs, const Unexpected& rhs) {
+    return lhs.error() == rhs.error();
+  }
+  FOLLY_REQUIRES(IsEqualityComparable<Error>::value)
+  friend bool operator!=(const Unexpected& lhs, const Unexpected& rhs) {
+    return !(lhs == rhs);
+  }
+
+ private:
+  struct MakeBadExpectedAccess {
+    template <class E>
+    BadExpectedAccess operator()(E&& err) const {
+      return BadExpectedAccess(static_cast<E&&>(err));
+    }
+  };
+
+  Error error_;
+};
+
+/**
+ * For constructing an Unexpected object from an error code. Unexpected objects
+ * are implicitly convertible to Expected object in the error state. Usage is
+ * as follows:
+ *
+ * enum class MyErrorCode { BAD_ERROR, WORSE_ERROR };
+ * Expected<int, MyErrorCode> myAPI() {
+ *   int i = // ...;
+ *   return i ? makeExpected<MyErrorCode>(i)
+ *            : makeUnexpected(MyErrorCode::BAD_ERROR);
+ * }
+ */
+template <class Error>
+constexpr Unexpected<typename std::decay<Error>::type> makeUnexpected(
+    Error&& err) {
+  return Unexpected<typename std::decay<Error>::type>{
+      static_cast<Error&&>(err)};
+}
+
+/**
+ * Expected - For holding a value or an error. Useful as an alternative to
+ * exceptions, for APIs where throwing on failure would be too expensive.
+ *
+ * Expected<Value, Error> is a variant over the types Value and Error.
+ *
+ * Expected does not offer support for references. Use
+ * Expected<std::reference_wrapper<T>, Error> if your API needs to return a
+ * reference or an error.
+ *
+ * Expected offers a continuation-based interface to reduce the boilerplate
+ * of checking error codes. The Expected::then member function takes a lambda
+ * that is to execute should the Expected object contain a value. The return
+ * value of the lambda is wrapped in an Expected and returned. If the lambda is
+ * not executed because the Expected contains an error, the error is returned
+ * immediately in a new Expected object.
+ *
+ * Expected<int, Error> funcTheFirst();
+ * Expected<std::string, Error> funcTheSecond() {
+ *   return funcTheFirst().then([](int i) { return std::to_string(i); });
+ * }
+ *
+ * The above line of code could more verbosely written as:
+ *
+ * Expected<std::string, Error> funcTheSecond() {
+ *   if (auto ex = funcTheFirst()) {
+ *     return std::to_string(*ex);
+ *   }
+ *   return makeUnexpected(ex.error());
+ * }
+ *
+ * Continuations can chain, like:
+ *
+ * Expected<D, Error> maybeD = someFunc()
+ *     .then([](A a){return B(a);})
+ *     .then([](B b){return C(b);})
+ *     .then([](C c){return D(c);});
+ *
+ * To avoid the redundant error checking that would happen if a call at the
+ * front of the chain returns an error, these call chains can be collaped into
+ * a single call to .then:
+ *
+ * Expected<D, Error> maybeD = someFunc()
+ *     .then([](A a){return B(a);},
+ *           [](B b){return C(b);},
+ *           [](C c){return D(c);});
+ *
+ * The result of .then() is wrapped into Expected< ~, Error > if it isn't
+ * of that form already. Consider the following code:
+ *
+ * extern Expected<std::string, Error> readLineFromIO();
+ * extern Expected<int, Error> parseInt(std::string);
+ * extern int increment(int);
+ *
+ * Expected<int, Error> x = readLineFromIO().then(parseInt).then(increment);
+ *
+ * From the code above, we see that .then() works both with functions that
+ * return an Expected< ~, Error > (like parseInt) and with ones that return
+ * a plain value (like increment). In the case of parseInt, .then() returns
+ * the result of parseInt as-is. In the case of increment, it wraps the int
+ * that increment returns into an Expected< int, Error >.
+ *
+ * Sometimes when using a continuation you would prefer an exception to be
+ * thrown for a value-less Expected. For that you can use .thenOrThrow, as
+ * follows:
+ *
+ * B b = someFunc()
+ *     .thenOrThrow([](A a){return B(a);});
+ *
+ * The above call to thenOrThrow will invoke the lambda if the Expected returned
+ * by someFunc() contains a value. Otherwise, it will throw an exception of type
+ * Unexpected<Error>::BadExpectedAccess. If you prefer it throw an exception of
+ * a different type, you can pass a second lambda to thenOrThrow:
+ *
+ * B b = someFunc()
+ *     .thenOrThrow([](A a){return B(a);},
+ *                  [](Error e) {throw MyException(e);});
+ *
+ * Like C++17's std::variant, Expected offers the almost-never-empty guarantee;
+ * that is, an Expected<Value, Error> almost always contains either a Value or
+ * and Error. Partially-formed Expected objects occur when an assignment to
+ * an Expected object that would change the type of the contained object (Value-
+ * to-Error or vice versa) throws. Trying to access either the contained value
+ * or error object causes Expected to throw folly::BadExpectedAccess.
+ *
+ * Expected models OptionalPointee, so calling 'get_pointer(ex)' will return a
+ * pointer to nullptr if the 'ex' is in the error state, and a pointer to the
+ * value otherwise:
+ *
+ *  Expected<int, Error> maybeInt = ...;
+ *  if (int* v = get_pointer(maybeInt)) {
+ *    cout << *v << endl;
+ *  }
+ */
+template <class Value, class Error>
+class Expected final : expected_detail::ExpectedStorage<Value, Error> {
+  template <class, class>
+  friend class Expected;
+  template <class, class, expected_detail::StorageType>
+  friend struct expected_detail::ExpectedStorage;
+  friend struct expected_detail::ExpectedHelper;
+  using Base = expected_detail::ExpectedStorage<Value, Error>;
+  using MakeBadExpectedAccess =
+      typename Unexpected<Error>::MakeBadExpectedAccess;
+  Base& base() & {
+    return *this;
+  }
+  const Base& base() const& {
+    return *this;
+  }
+  Base&& base() && {
+    return std::move(*this);
+  }
+
+ public:
+  using value_type = Value;
+  using error_type = Error;
+  using IsTriviallyCopyable = typename expected_detail::
+      StrictAllOf<IsTriviallyCopyable, Value, Error>::type;
+
+  template <class U>
+  using rebind = Expected<U, Error>;
+
+  static_assert(
+      !std::is_reference<Value>::value,
+      "Expected may not be used with reference types");
+  static_assert(
+      !std::is_abstract<Value>::value,
+      "Expected may not be used with abstract types");
+
+  /*
+   * Constructors
+   */
+  template <class B = Base, class = decltype(B{})>
+  Expected() noexcept(noexcept(B{})) : Base() {}
+  Expected(const Expected& that) = default;
+  Expected(Expected&& that) = default;
+
+  template <
+      class V,
+      class E FOLLY_REQUIRES_TRAILING(
+          !std::is_same<Expected<V, E>, Expected>::value &&
+          std::is_constructible<Value, V&&>::value &&
+          std::is_constructible<Error, E&&>::value)>
+  Expected(Expected<V, E> that) : Base{expected_detail::EmptyTag{}} {
+    *this = std::move(that);
+  }
+
+  FOLLY_REQUIRES(std::is_copy_constructible<Value>::value)
+  constexpr /* implicit */ Expected(const Value& val) noexcept(
+      noexcept(Value(val)))
+      : Base{expected_detail::ValueTag{}, val} {}
+
+  FOLLY_REQUIRES(std::is_move_constructible<Value>::value)
+  constexpr /* implicit */ Expected(Value&& val) noexcept(
+      noexcept(Value(std::move(val))))
+      : Base{expected_detail::ValueTag{}, std::move(val)} {}
+
+  template <class T FOLLY_REQUIRES_TRAILING(
+      std::is_convertible<T, Value>::value &&
+      !std::is_convertible<T, Error>::value)>
+  constexpr /* implicit */ Expected(T&& val) noexcept(
+      noexcept(Value(static_cast<T&&>(val))))
+      : Base{expected_detail::ValueTag{}, static_cast<T&&>(val)} {}
+
+  template <class... Ts FOLLY_REQUIRES_TRAILING(
+      std::is_constructible<Value, Ts&&...>::value)>
+  explicit constexpr Expected(in_place_t, Ts&&... ts) noexcept(
+      noexcept(Value(std::declval<Ts>()...)))
+      : Base{expected_detail::ValueTag{}, static_cast<Ts&&>(ts)...} {}
+
+  template <
+      class U,
+      class... Ts FOLLY_REQUIRES_TRAILING(
+          std::is_constructible<Value, std::initializer_list<U>&, Ts&&...>::
+              value)>
+  explicit constexpr Expected(
+      in_place_t,
+      std::initializer_list<U> il,
+      Ts&&... ts) noexcept(noexcept(Value(std::declval<Ts>()...)))
+      : Base{expected_detail::ValueTag{}, il, static_cast<Ts&&>(ts)...} {}
+
+  // If overload resolution selects one of these deleted functions, that
+  // means you need to use makeUnexpected
+  /* implicit */ Expected(const Error&) = delete;
+  /* implicit */ Expected(Error&&) = delete;
+
+  FOLLY_REQUIRES(std::is_copy_constructible<Error>::value)
+  constexpr Expected(unexpected_t, const Error& err) noexcept(
+      noexcept(Error(err)))
+      : Base{expected_detail::ErrorTag{}, err} {}
+
+  FOLLY_REQUIRES(std::is_move_constructible<Error>::value)
+  constexpr Expected(unexpected_t, Error&& err) noexcept(
+      noexcept(Error(std::move(err))))
+      : Base{expected_detail::ErrorTag{}, std::move(err)} {}
+
+  FOLLY_REQUIRES(std::is_copy_constructible<Error>::value)
+  constexpr /* implicit */ Expected(const Unexpected<Error>& err) noexcept(
+      noexcept(Error(err.error())))
+      : Base{expected_detail::ErrorTag{}, err.error()} {}
+
+  FOLLY_REQUIRES(std::is_move_constructible<Error>::value)
+  constexpr /* implicit */ Expected(Unexpected<Error>&& err) noexcept(
+      noexcept(Error(std::move(err.error()))))
+      : Base{expected_detail::ErrorTag{}, std::move(err.error())} {}
+
+  /*
+   * Assignment operators
+   */
+  Expected& operator=(const Expected& that) = default;
+  Expected& operator=(Expected&& that) = default;
+
+  template <
+      class V,
+      class E FOLLY_REQUIRES_TRAILING(
+          !std::is_same<Expected<V, E>, Expected>::value &&
+          expected_detail::IsConvertible<V&&, Value>::value &&
+          expected_detail::IsConvertible<E&&, Error>::value)>
+  Expected& operator=(Expected<V, E> that) {
+    this->assign(std::move(that));
+    return *this;
+  }
+
+  FOLLY_REQUIRES(expected_detail::IsCopyable<Value>::value)
+  Expected& operator=(const Value& val) noexcept(
+      expected_detail::IsNothrowCopyable<Value>::value) {
+    this->assignValue(val);
+    return *this;
+  }
+
+  FOLLY_REQUIRES(expected_detail::IsMovable<Value>::value)
+  Expected& operator=(Value&& val) noexcept(
+      expected_detail::IsNothrowMovable<Value>::value) {
+    this->assignValue(std::move(val));
+    return *this;
+  }
+
+  template <class T FOLLY_REQUIRES_TRAILING(
+      std::is_convertible<T, Value>::value &&
+      !std::is_convertible<T, Error>::value)>
+  Expected& operator=(T&& val) {
+    this->assignValue(static_cast<T&&>(val));
+    return *this;
+  }
+
+  FOLLY_REQUIRES(expected_detail::IsCopyable<Error>::value)
+  Expected& operator=(const Unexpected<Error>& err) noexcept(
+      expected_detail::IsNothrowCopyable<Error>::value) {
+    this->assignError(err.error());
+    return *this;
+  }
+
+  FOLLY_REQUIRES(expected_detail::IsMovable<Error>::value)
+  Expected& operator=(Unexpected<Error>&& err) noexcept(
+      expected_detail::IsNothrowMovable<Error>::value) {
+    this->assignError(std::move(err.error()));
+    return *this;
+  }
+
+  template <class... Ts FOLLY_REQUIRES_TRAILING(
+      std::is_constructible<Value, Ts&&...>::value)>
+  void emplace(Ts&&... ts) {
+    this->assignValue(static_cast<Ts&&>(ts)...);
+  }
+
+  /**
+   * swap
+   */
+  void swap(Expected& that) noexcept(
+      expected_detail::StrictAllOf<IsNothrowSwappable, Value, Error>::value) {
+    if (this->uninitializedByException() || that.uninitializedByException())
+      throw BadExpectedAccess();
+    using std::swap;
+    if (*this) {
+      if (that) {
+        swap(this->value_, that.value_);
+      } else {
+        Error e(std::move(that.error_));
+        that.assignValue(std::move(this->value_));
+        this->assignError(std::move(e));
+      }
+    } else {
+      if (!that) {
+        swap(this->error_, that.error_);
+      } else {
+        Error e(std::move(this->error_));
+        this->assignValue(std::move(that.value_));
+        that.assignError(std::move(e));
+      }
+    }
+  }
+
+  // If overload resolution selects one of these deleted functions, that
+  // means you need to use makeUnexpected
+  /* implicit */ Expected& operator=(const Error&) = delete;
+  /* implicit */ Expected& operator=(Error&&) = delete;
+
+  /**
+   * Relational Operators
+   */
+  FOLLY_REQUIRES(IsEqualityComparable<Value>::value)
+  friend bool operator==(const Expected& lhs, const Expected& rhs) {
+    if (UNLIKELY(lhs.which_ != rhs.which_))
+      return UNLIKELY(lhs.uninitializedByException())
+          ? false
+          : throw BadExpectedAccess();
+    if (UNLIKELY(lhs.uninitializedByException()))
+      throw BadExpectedAccess();
+    if (UNLIKELY(lhs.hasError()))
+      return true; // All error states are considered equal
+    return lhs.value_ == rhs.value_;
+  }
+
+  FOLLY_REQUIRES(IsEqualityComparable<Value>::value)
+  friend bool operator!=(const Expected& lhs, const Expected& rhs) {
+    return !(lhs == rhs);
+  }
+
+  FOLLY_REQUIRES(IsLessThanComparable<Value>::value)
+  friend bool operator<(const Expected& lhs, const Expected& rhs) {
+    if (UNLIKELY(
+            lhs.uninitializedByException() || rhs.uninitializedByException()))
+      throw BadExpectedAccess();
+    if (UNLIKELY(lhs.hasError()))
+      return !rhs.hasError();
+    if (UNLIKELY(rhs.hasError()))
+      return false;
+    return lhs.value_ < rhs.value_;
+  }
+
+  FOLLY_REQUIRES(IsLessThanComparable<Value>::value)
+  friend bool operator<=(const Expected& lhs, const Expected& rhs) {
+    return !(rhs < lhs);
+  }
+
+  FOLLY_REQUIRES(IsLessThanComparable<Value>::value)
+  friend bool operator>(const Expected& lhs, const Expected& rhs) {
+    return rhs < lhs;
+  }
+
+  FOLLY_REQUIRES(IsLessThanComparable<Value>::value)
+  friend bool operator>=(const Expected& lhs, const Expected& rhs) {
+    return !(lhs < rhs);
+  }
+
+  /*
+   * Accessors
+   */
+  constexpr bool hasValue() const noexcept {
+    return expected_detail::Which::eValue == this->which_;
+  }
+
+  constexpr bool hasError() const noexcept {
+    return expected_detail::Which::eError == this->which_;
+  }
+
+  using Base::uninitializedByException;
+
+  const Value& value() const& {
+    requireValue();
+    return this->Base::value();
+  }
+
+  Value& value() & {
+    requireValue();
+    return this->Base::value();
+  }
+
+  Value&& value() && {
+    requireValue();
+    return std::move(this->Base::value());
+  }
+
+  const Error& error() const& {
+    requireError();
+    return this->Base::error();
+  }
+
+  Error& error() & {
+    requireError();
+    return this->Base::error();
+  }
+
+  Error&& error() && {
+    requireError();
+    return std::move(this->Base::error());
+  }
+
+  // Return a copy of the value if set, or a given default if not.
+  template <class U>
+  Value value_or(U&& dflt) const& {
+    if (LIKELY(this->which_ == expected_detail::Which::eValue)) {
+      return this->value_;
+    }
+    return static_cast<U&&>(dflt);
+  }
+
+  template <class U>
+  Value value_or(U&& dflt) && {
+    if (LIKELY(this->which_ == expected_detail::Which::eValue)) {
+      return std::move(this->value_);
+    }
+    return static_cast<U&&>(dflt);
+  }
+
+  explicit constexpr operator bool() const noexcept {
+    return hasValue();
+  }
+
+  const Value& operator*() const& {
+    return this->value();
+  }
+
+  Value& operator*() & {
+    return this->value();
+  }
+
+  Value&& operator*() && {
+    return std::move(this->value());
+  }
+
+  const Value* operator->() const {
+    return std::addressof(this->value());
+  }
+
+  Value* operator->() {
+    return std::addressof(this->value());
+  }
+
+  const Value* get_pointer() const& noexcept {
+    return hasValue() ? std::addressof(this->value_) : nullptr;
+  }
+
+  Value* get_pointer() & noexcept {
+    return hasValue() ? std::addressof(this->value_) : nullptr;
+  }
+
+  Value* get_pointer() && = delete;
+
+  /**
+   * then
+   */
+  template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>
+  auto then(Fns&&... fns) const& -> decltype(
+      expected_detail::ExpectedHelper::then_(
+          std::declval<const Base&>(),
+          std::declval<Fns>()...)) {
+    if (this->uninitializedByException())
+      throw BadExpectedAccess();
+    return expected_detail::ExpectedHelper::then_(
+        base(), static_cast<Fns&&>(fns)...);
+  }
+
+  template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>
+  auto then(Fns&&... fns) & -> decltype(expected_detail::ExpectedHelper::then_(
+      std::declval<Base&>(),
+      std::declval<Fns>()...)) {
+    if (this->uninitializedByException())
+      throw BadExpectedAccess();
+    return expected_detail::ExpectedHelper::then_(
+        base(), static_cast<Fns&&>(fns)...);
+  }
+
+  template <class... Fns FOLLY_REQUIRES_TRAILING(sizeof...(Fns) >= 1)>
+  auto then(Fns&&... fns) && -> decltype(expected_detail::ExpectedHelper::then_(
+      std::declval<Base&&>(),
+      std::declval<Fns>()...)) {
+    if (this->uninitializedByException())
+      throw BadExpectedAccess();
+    return expected_detail::ExpectedHelper::then_(
+        std::move(base()), static_cast<Fns&&>(fns)...);
+  }
+
+  /**
+   * thenOrThrow
+   */
+  template <class Yes, class No = MakeBadExpectedAccess>
+  auto thenOrThrow(Yes&& yes, No&& no = No()) const& -> decltype(
+      std::declval<Yes>()(std::declval<const Value&>())) {
+    using Ret = decltype(std::declval<Yes>()(std::declval<const Value&>()));
+    if (this->uninitializedByException())
+      throw BadExpectedAccess();
+    return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
+        base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
+  }
+
+  template <class Yes, class No = MakeBadExpectedAccess>
+  auto thenOrThrow(Yes&& yes, No&& no = No()) & -> decltype(
+      std::declval<Yes>()(std::declval<Value&>())) {
+    using Ret = decltype(std::declval<Yes>()(std::declval<Value&>()));
+    if (this->uninitializedByException())
+      throw BadExpectedAccess();
+    return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
+        base(), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
+  }
+
+  template <class Yes, class No = MakeBadExpectedAccess>
+  auto thenOrThrow(Yes&& yes, No&& no = No()) && -> decltype(
+      std::declval<Yes>()(std::declval<Value&&>())) {
+    using Ret = decltype(std::declval<Yes>()(std::declval<Value&&>()));
+    if (this->uninitializedByException())
+      throw BadExpectedAccess();
+    return Ret(expected_detail::ExpectedHelper::thenOrThrow_(
+        std::move(base()), static_cast<Yes&&>(yes), static_cast<No&&>(no)));
+  }
+
+ private:
+  void requireValue() const {
+    if (UNLIKELY(!hasValue())) {
+      if (LIKELY(hasError()))
+        throw typename Unexpected<Error>::BadExpectedAccess(this->error_);
+      throw BadExpectedAccess();
+    }
+  }
+
+  void requireError() const {
+    if (UNLIKELY(!hasError())) {
+      throw BadExpectedAccess();
+    }
+  }
+
+  expected_detail::Which which() const noexcept {
+    return this->which_;
+  }
+};
+
+/**
+ * swap Expected values
+ */
+template <class Error, class Value>
+void swap(Expected<Error, Value>& lhs, Expected<Value, Error>& rhs) noexcept(
+    expected_detail::StrictAllOf<IsNothrowSwappable, Value, Error>::value) {
+  lhs.swap(rhs);
+}
+
+template <class Value, class Error>
+const Value* get_pointer(const Expected<Value, Error>& ex) noexcept {
+  return ex.get_pointer();
+}
+
+template <class Value, class Error>
+Value* get_pointer(Expected<Value, Error>& ex) noexcept {
+  return ex.get_pointer();
+}
+
+/**
+ * For constructing an Expected object from a value, with the specified
+ * Error type. Usage is as follows:
+ *
+ * enum MyErrorCode { BAD_ERROR, WORSE_ERROR };
+ * Expected<int, MyErrorCode> myAPI() {
+ *   int i = // ...;
+ *   return i ? makeExpected<MyErrorCode>(i) : makeUnexpected(BAD_ERROR);
+ * }
+ */
+template <class Error, class Value>
+constexpr Expected<typename std::decay<Value>::type, Error> makeExpected(
+    Value&& val) {
+  return Expected<typename std::decay<Value>::type, Error>{
+      in_place, static_cast<Value&&>(val)};
+}
+
+// Suppress comparability of Optional<T> with T, despite implicit conversion.
+template <class Value, class Error>
+bool operator==(const Expected<Value, Error>&, const Value& other) = delete;
+template <class Value, class Error>
+bool operator!=(const Expected<Value, Error>&, const Value& other) = delete;
+template <class Value, class Error>
+bool operator<(const Expected<Value, Error>&, const Value& other) = delete;
+template <class Value, class Error>
+bool operator<=(const Expected<Value, Error>&, const Value& other) = delete;
+template <class Value, class Error>
+bool operator>=(const Expected<Value, Error>&, const Value& other) = delete;
+template <class Value, class Error>
+bool operator>(const Expected<Value, Error>&, const Value& other) = delete;
+template <class Value, class Error>
+bool operator==(const Value& other, const Expected<Value, Error>&) = delete;
+template <class Value, class Error>
+bool operator!=(const Value& other, const Expected<Value, Error>&) = delete;
+template <class Value, class Error>
+bool operator<(const Value& other, const Expected<Value, Error>&) = delete;
+template <class Value, class Error>
+bool operator<=(const Value& other, const Expected<Value, Error>&) = delete;
+template <class Value, class Error>
+bool operator>=(const Value& other, const Expected<Value, Error>&) = delete;
+template <class Value, class Error>
+bool operator>(const Value& other, const Expected<Value, Error>&) = delete;
+
+} // namespace folly
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+#undef FOLLY_REQUIRES
+#undef FOLLY_REQUIRES_TRAILING
index 1a976ba7cff9d7030ee831273ba75f4d885649e6..328f96a38b456015546eab9f87f98870b95fa9f1 100644 (file)
@@ -84,11 +84,12 @@ nobase_follyinclude_HEADERS = \
        dynamic.h \
        dynamic-inl.h \
        Enumerate.h \
+       EvictingCacheMap.h \
        Exception.h \
        ExceptionString.h \
        ExceptionWrapper.h \
        Executor.h \
-       EvictingCacheMap.h \
+       Expected.h \
        experimental/AsymmetricMemoryBarrier.h \
        experimental/AutoTimer.h \
        experimental/Bits.h \
index 09b831f8ae2804317d6a0a74559e1ad6824bb1ff..2b12ccc6a310d5a708cf945403bc0d91a2245c01 100644 (file)
@@ -206,6 +206,9 @@ class Optional {
     return storage_.value;
   }
 
+  // TODO: This should return Value&& instead of Value. Like with
+  // std::move, the calling code should decide whether it wants the move
+  // to happen or not. See std::optional.
   Value value() && {
     require_value();
     return std::move(storage_.value);
@@ -227,6 +230,9 @@ class Optional {
 
   const Value& operator*() const&  { return value(); }
         Value& operator*()      &  { return value(); }
+        // TODO: This should return Value&& instead of Value. Like with
+        // std::move, the calling code should decide whether it wants the move
+        // to happen or not. See std::optional.
         Value  operator*()      && { return std::move(value()); }
 
   const Value* operator->() const { return &value(); }
index c50948ccff8a9438dfe586e8173050469ad4da7a..3421d665754a249fc11815fcb41ddb4e8879fec4 100644 (file)
@@ -105,29 +105,94 @@ FOLLY_HAS_TRUE_XXX(IsZeroInitializable)
 FOLLY_HAS_TRUE_XXX(IsTriviallyCopyable)
 
 #undef FOLLY_HAS_TRUE_XXX
+
+// Older versions of libstdc++ do not provide std::is_trivially_copyable
+#if defined(__clang__) && !defined(_LIBCPP_VERSION)
+template <class T>
+struct is_trivially_copyable
+    : std::integral_constant<bool, __is_trivially_copyable(T)> {};
+#elif defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5
+template <class T>
+struct is_trivially_copyable : std::is_trivial<T> {};
+#else
+template <class T>
+using is_trivially_copyable = std::is_trivially_copyable<T>;
+#endif
+}
+
+struct Ignore {
+  template <class T>
+  /* implicit */ Ignore(const T&) {}
+  template <class T>
+  const Ignore& operator=(T const&) const { return *this; }
+};
+
+template <class...>
+using Ignored = Ignore;
+
+namespace traits_detail_IsEqualityComparable {
+Ignore operator==(Ignore, Ignore);
+
+template <class T, class U = T>
+struct IsEqualityComparable
+    : std::is_convertible<
+          decltype(std::declval<T>() == std::declval<U>()),
+          bool
+      > {};
+}
+
+/* using override */ using traits_detail_IsEqualityComparable::
+    IsEqualityComparable;
+
+namespace traits_detail_IsLessThanComparable {
+Ignore operator<(Ignore, Ignore);
+
+template <class T, class U = T>
+struct IsLessThanComparable
+    : std::is_convertible<
+          decltype(std::declval<T>() < std::declval<U>()),
+          bool
+      > {};
+}
+
+/* using override */ using traits_detail_IsLessThanComparable::
+    IsLessThanComparable;
+
+namespace traits_detail_IsNothrowSwappable {
+/* using override */ using std::swap;
+
+template <class T>
+struct IsNothrowSwappable
+    : std::integral_constant<bool,
+        std::is_nothrow_move_constructible<T>::value &&
+        noexcept(swap(std::declval<T&>(), std::declval<T&>()))
+      > {};
 }
 
+/* using override */ using traits_detail_IsNothrowSwappable::IsNothrowSwappable;
+
 template <class T> struct IsTriviallyCopyable
-  : std::integral_constant<bool,
-      !std::is_class<T>::value ||
-      // TODO: add alternate clause is_trivially_copyable, when available
-      traits_detail::has_true_IsTriviallyCopyable<T>::value
-    > {};
+  : std::conditional<
+      traits_detail::has_IsTriviallyCopyable<T>::value,
+      traits_detail::has_true_IsTriviallyCopyable<T>,
+      traits_detail::is_trivially_copyable<T>
+    >::type {};
 
 template <class T> struct IsRelocatable
-  : std::integral_constant<bool,
-      !std::is_class<T>::value ||
+  : std::conditional<
+      traits_detail::has_IsRelocatable<T>::value,
+      traits_detail::has_true_IsRelocatable<T>,
       // TODO add this line (and some tests for it) when we upgrade to gcc 4.7
       //std::is_trivially_move_constructible<T>::value ||
-      IsTriviallyCopyable<T>::value ||
-      traits_detail::has_true_IsRelocatable<T>::value
-    > {};
+      IsTriviallyCopyable<T>
+    >::type {};
 
 template <class T> struct IsZeroInitializable
-  : std::integral_constant<bool,
-      !std::is_class<T>::value ||
-      traits_detail::has_true_IsZeroInitializable<T>::value
-    > {};
+  : std::conditional<
+      traits_detail::has_IsZeroInitializable<T>::value,
+      traits_detail::has_true_IsZeroInitializable<T>,
+      std::integral_constant<bool, !std::is_class<T>::value>
+    >::type {};
 
 template <typename...>
 struct Conjunction : std::true_type {};
@@ -148,6 +213,19 @@ struct Disjunction<T, TList...>
 template <typename T>
 struct Negation : std::integral_constant<bool, !T::value> {};
 
+template <bool... Bs>
+struct Bools {
+  using valid_type = bool;
+  static constexpr std::size_t size() {
+    return sizeof...(Bs);
+  }
+};
+
+// Lighter-weight than Conjunction, but evaluates all sub-conditions eagerly.
+template <class... Ts>
+using StrictConjunction =
+    std::is_same<Bools<Ts::value..., true>, Bools<true, Ts::value...>>;
+
 } // namespace folly
 
 /**
@@ -398,12 +476,53 @@ bool greater_than(LHS const lhs) {
   >(lhs);
 }
 
+namespace traits_detail {
+struct InPlaceTag {};
+template <class>
+struct InPlaceTypeTag {};
+template <std::size_t>
+struct InPlaceIndexTag {};
+}
+
 /**
  * Like std::piecewise_construct, a tag type & instance used for in-place
  * construction of non-movable contained types, e.g. by Synchronized.
+ * Follows the naming and design of std::in_place suggested in
+ * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0032r2.pdf
  */
-struct construct_in_place_t {};
-constexpr construct_in_place_t construct_in_place{};
+using in_place_t = traits_detail::InPlaceTag (&)(traits_detail::InPlaceTag);
+
+template <class T>
+using in_place_type_t =
+    traits_detail::InPlaceTypeTag<T> (&)(traits_detail::InPlaceTypeTag<T>);
+
+template <std::size_t I>
+using in_place_index_t =
+    traits_detail::InPlaceIndexTag<I> (&)(traits_detail::InPlaceIndexTag<I>);
+
+inline traits_detail::InPlaceTag in_place(traits_detail::InPlaceTag = {}) {
+  return {};
+}
+
+template <class T>
+inline traits_detail::InPlaceTypeTag<T> in_place(
+    traits_detail::InPlaceTypeTag<T> = {}) {
+  return {};
+}
+
+template <std::size_t I>
+inline traits_detail::InPlaceIndexTag<I> in_place(
+    traits_detail::InPlaceIndexTag<I> = {}) {
+  return {};
+}
+
+// For backwards compatibility:
+using construct_in_place_t = in_place_t;
+
+inline traits_detail::InPlaceTag construct_in_place(
+    traits_detail::InPlaceTag = {}) {
+  return {};
+}
 
 /**
  * Initializer lists are a powerful compile time syntax introduced in C++11
diff --git a/folly/test/ExpectedTest.cpp b/folly/test/ExpectedTest.cpp
new file mode 100644 (file)
index 0000000..771d81e
--- /dev/null
@@ -0,0 +1,717 @@
+/*
+ * Copyright 2016 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/Expected.h>
+#include <folly/Portability.h>
+
+#include <algorithm>
+#include <iomanip>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+
+using std::unique_ptr;
+using std::shared_ptr;
+
+namespace folly {
+
+enum class E { E1, E2 };
+
+std::ostream& operator<<(std::ostream& os, E e) {
+  switch (e) {
+    case E::E1:
+      return os << "E::E1";
+    case E::E2:
+      return os << "E::E2";
+    default:;
+  }
+  return os;
+}
+
+template <class V, class E>
+std::ostream& operator<<(std::ostream& os, const Expected<V, E>& e) {
+  if (e) {
+    os << "Expected(" << e.value() << ')';
+  } else {
+    os << "Unexpected(" << e.error() << ')';
+  }
+  return os;
+}
+
+struct NoDefault {
+  NoDefault(int, int) {}
+  char a, b, c;
+};
+
+TEST(Expected, NoDefault) {
+  static_assert(
+      std::is_default_constructible<Expected<NoDefault, int>>::value, "");
+  Expected<NoDefault, int> x{in_place, 42, 42};
+  EXPECT_TRUE(x);
+  x.emplace(4, 5);
+  EXPECT_TRUE(bool(x));
+  x = makeUnexpected(42);
+  EXPECT_FALSE(x);
+  EXPECT_EQ(42, x.error());
+}
+
+TEST(Expected, String) {
+  Expected<std::string, int> maybeString;
+  EXPECT_FALSE(maybeString);
+  EXPECT_EQ(0, maybeString.error());
+  maybeString = "hello";
+  EXPECT_TRUE(bool(maybeString));
+  EXPECT_EQ("hello", *maybeString);
+}
+
+TEST(Expected, Ambiguous) {
+  // Potentially ambiguous and confusing construction and assignment disallowed:
+  EXPECT_FALSE((std::is_constructible<Expected<int, int>, int>::value));
+  EXPECT_FALSE((std::is_assignable<Expected<int, int>&, int>::value));
+}
+
+TEST(Expected, Const) {
+  { // default construct
+    Expected<const int, int> ex;
+    EXPECT_FALSE(bool(ex));
+    EXPECT_EQ(0, ex.error());
+    ex.emplace(4);
+    EXPECT_EQ(4, *ex);
+    ex.emplace(5);
+    EXPECT_EQ(5, *ex);
+    ex = makeUnexpected(42);
+    EXPECT_FALSE(bool(ex));
+    EXPECT_EQ(42, ex.error());
+  }
+  { // copy-constructed
+    const int x = 6;
+    Expected<const int, int> ex{in_place, x};
+    Expected<const int, int> ex2 = ex;
+    EXPECT_EQ(6, *ex2);
+  }
+  { // move-constructed
+    const int x = 7;
+    Expected<const int, int> ex{in_place, std::move(x)};
+    Expected<const int, int> ex2 = std::move(ex);
+    EXPECT_EQ(7, *ex2);
+  }
+  // no assignment allowed
+  EXPECT_FALSE((std::is_copy_assignable<Expected<const int, int>>::value));
+}
+
+TEST(Expected, Simple) {
+  Expected<int, int> ex;
+  EXPECT_FALSE(bool(ex));
+  EXPECT_EQ(42, ex.value_or(42));
+  ex.emplace(4);
+  EXPECT_TRUE(bool(ex));
+  EXPECT_EQ(4, *ex);
+  EXPECT_EQ(4, ex.value_or(42));
+  ex = makeUnexpected(-1);
+  EXPECT_FALSE(bool(ex));
+  EXPECT_EQ(-1, ex.error());
+  EXPECT_EQ(42, ex.value_or(42));
+}
+
+class MoveTester {
+ public:
+  /* implicit */ MoveTester(const char* s) : s_(s) {}
+  MoveTester(const MoveTester&) = default;
+  MoveTester(MoveTester&& other) noexcept {
+    s_ = std::move(other.s_);
+    other.s_ = "";
+  }
+  MoveTester& operator=(const MoveTester&) = default;
+  MoveTester& operator=(MoveTester&& other) noexcept {
+    s_ = std::move(other.s_);
+    other.s_ = "";
+    return *this;
+  }
+
+ private:
+  friend bool operator==(const MoveTester& o1, const MoveTester& o2);
+  std::string s_;
+};
+
+bool operator==(const MoveTester& o1, const MoveTester& o2) {
+  return o1.s_ == o2.s_;
+}
+
+TEST(Expected, value_or_rvalue_arg) {
+  Expected<MoveTester, int> ex = makeUnexpected(-1);
+  MoveTester dflt = "hello";
+  EXPECT_EQ("hello", ex.value_or(dflt));
+  EXPECT_EQ("hello", dflt);
+  EXPECT_EQ("hello", ex.value_or(std::move(dflt)));
+  EXPECT_EQ("", dflt);
+  EXPECT_EQ("world", ex.value_or("world"));
+
+  dflt = "hello";
+  // Make sure that the const overload works on const objects
+  const auto& exc = ex;
+  EXPECT_EQ("hello", exc.value_or(dflt));
+  EXPECT_EQ("hello", dflt);
+  EXPECT_EQ("hello", exc.value_or(std::move(dflt)));
+  EXPECT_EQ("", dflt);
+  EXPECT_EQ("world", exc.value_or("world"));
+
+  dflt = "hello";
+  ex = "meow";
+  EXPECT_EQ("meow", ex.value_or(dflt));
+  EXPECT_EQ("hello", dflt);
+  EXPECT_EQ("meow", ex.value_or(std::move(dflt)));
+  EXPECT_EQ("hello", dflt); // only moved if used
+}
+
+TEST(Expected, value_or_noncopyable) {
+  Expected<std::unique_ptr<int>, int> ex{unexpected, 42};
+  std::unique_ptr<int> dflt(new int(42));
+  EXPECT_EQ(42, *std::move(ex).value_or(std::move(dflt)));
+}
+
+struct ExpectingDeleter {
+  explicit ExpectingDeleter(int expected) : expected(expected) {}
+  int expected;
+  void operator()(const int* ptr) {
+    EXPECT_EQ(*ptr, expected);
+    delete ptr;
+  }
+};
+
+TEST(Expected, value_move) {
+  auto ptr = Expected<std::unique_ptr<int, ExpectingDeleter>, int>(
+                 in_place, new int(42), ExpectingDeleter{1337})
+                 .value();
+  *ptr = 1337;
+}
+
+TEST(Expected, dereference_move) {
+  auto ptr = *Expected<std::unique_ptr<int, ExpectingDeleter>, int>(
+      in_place, new int(42), ExpectingDeleter{1337});
+  *ptr = 1337;
+}
+
+TEST(Expected, EmptyConstruct) {
+  Expected<int, int> ex{unexpected, 42};
+  EXPECT_FALSE(bool(ex));
+  Expected<int, int> test1(ex);
+  EXPECT_FALSE(bool(test1));
+  Expected<int, int> test2(std::move(ex));
+  EXPECT_FALSE(bool(test2));
+  EXPECT_EQ(42, test2.error());
+}
+
+TEST(Expected, Unique) {
+  Expected<unique_ptr<int>, int> ex;
+
+  ex = makeUnexpected(-1);
+  EXPECT_FALSE(bool(ex));
+  // empty->emplaced
+  ex.emplace(new int(5));
+  EXPECT_TRUE(bool(ex));
+  EXPECT_EQ(5, **ex);
+
+  ex = makeUnexpected(-1);
+  // empty->moved
+  ex = unique_ptr<int>(new int(6));
+  EXPECT_EQ(6, **ex);
+  // full->moved
+  ex = unique_ptr<int>(new int(7));
+  EXPECT_EQ(7, **ex);
+
+  // move it out by move construct
+  Expected<unique_ptr<int>, int> moved(std::move(ex));
+  EXPECT_TRUE(bool(moved));
+  EXPECT_TRUE(bool(ex));
+  EXPECT_EQ(nullptr, ex->get());
+  EXPECT_EQ(7, **moved);
+
+  EXPECT_TRUE(bool(moved));
+  ex = std::move(moved); // move it back by move assign
+  EXPECT_TRUE(bool(moved));
+  EXPECT_EQ(nullptr, moved->get());
+  EXPECT_TRUE(bool(ex));
+  EXPECT_EQ(7, **ex);
+}
+
+TEST(Expected, Shared) {
+  shared_ptr<int> ptr;
+  Expected<shared_ptr<int>, int> ex{unexpected, -1};
+  EXPECT_FALSE(bool(ex));
+  // empty->emplaced
+  ex.emplace(new int(5));
+  EXPECT_TRUE(bool(ex));
+  ptr = ex.value();
+  EXPECT_EQ(ptr.get(), ex->get());
+  EXPECT_EQ(2, ptr.use_count());
+  ex = makeUnexpected(-1);
+  EXPECT_EQ(1, ptr.use_count());
+  // full->copied
+  ex = ptr;
+  EXPECT_EQ(2, ptr.use_count());
+  EXPECT_EQ(ptr.get(), ex->get());
+  ex = makeUnexpected(-1);
+  EXPECT_EQ(1, ptr.use_count());
+  // full->moved
+  ex = std::move(ptr);
+  EXPECT_EQ(1, ex->use_count());
+  EXPECT_EQ(nullptr, ptr.get());
+  {
+    EXPECT_EQ(1, ex->use_count());
+    Expected<shared_ptr<int>, int> copied(ex);
+    EXPECT_EQ(2, ex->use_count());
+    Expected<shared_ptr<int>, int> moved(std::move(ex));
+    EXPECT_EQ(2, moved->use_count());
+    moved.emplace(new int(6));
+    EXPECT_EQ(1, moved->use_count());
+    copied = moved;
+    EXPECT_EQ(2, moved->use_count());
+  }
+}
+
+TEST(Expected, Order) {
+  std::vector<Expected<int, E>> vect{
+      {unexpected, E::E1}, {3}, {1}, {unexpected, E::E1}, {2},
+  };
+  std::vector<Expected<int, E>> expected{
+      {unexpected, E::E1}, {unexpected, E::E1}, {1}, {2}, {3},
+  };
+  std::sort(vect.begin(), vect.end());
+  EXPECT_EQ(vect, expected);
+}
+
+TEST(Expected, Swap) {
+  Expected<std::string, E> a;
+  Expected<std::string, E> b;
+
+  swap(a, b);
+  EXPECT_FALSE(a.hasValue());
+  EXPECT_FALSE(b.hasValue());
+
+  a = "hello";
+  EXPECT_TRUE(a.hasValue());
+  EXPECT_FALSE(b.hasValue());
+  EXPECT_EQ("hello", a.value());
+
+  swap(a, b);
+  EXPECT_FALSE(a.hasValue());
+  EXPECT_TRUE(b.hasValue());
+  EXPECT_EQ("hello", b.value());
+
+  a = "bye";
+  EXPECT_TRUE(a.hasValue());
+  EXPECT_EQ("bye", a.value());
+
+  swap(a, b);
+}
+
+TEST(Expected, Comparisons) {
+  Expected<int, E> o_;
+  Expected<int, E> o1(1);
+  Expected<int, E> o2(2);
+
+  EXPECT_TRUE(o_ <= (o_));
+  EXPECT_TRUE(o_ == (o_));
+  EXPECT_TRUE(o_ >= (o_));
+
+  EXPECT_TRUE(o1 < o2);
+  EXPECT_TRUE(o1 <= o2);
+  EXPECT_TRUE(o1 <= (o1));
+  EXPECT_TRUE(o1 == (o1));
+  EXPECT_TRUE(o1 != o2);
+  EXPECT_TRUE(o1 >= (o1));
+  EXPECT_TRUE(o2 >= o1);
+  EXPECT_TRUE(o2 > o1);
+
+  EXPECT_FALSE(o2 < o1);
+  EXPECT_FALSE(o2 <= o1);
+  EXPECT_FALSE(o2 <= o1);
+  EXPECT_FALSE(o2 == o1);
+  EXPECT_FALSE(o1 != (o1));
+  EXPECT_FALSE(o1 >= o2);
+  EXPECT_FALSE(o1 >= o2);
+  EXPECT_FALSE(o1 > o2);
+
+  /* folly::Expected explicitly doesn't support comparisons with contained value
+  EXPECT_TRUE(1 < o2);
+  EXPECT_TRUE(1 <= o2);
+  EXPECT_TRUE(1 <= o1);
+  EXPECT_TRUE(1 == o1);
+  EXPECT_TRUE(2 != o1);
+  EXPECT_TRUE(1 >= o1);
+  EXPECT_TRUE(2 >= o1);
+  EXPECT_TRUE(2 > o1);
+
+  EXPECT_FALSE(o2 < 1);
+  EXPECT_FALSE(o2 <= 1);
+  EXPECT_FALSE(o2 <= 1);
+  EXPECT_FALSE(o2 == 1);
+  EXPECT_FALSE(o2 != 2);
+  EXPECT_FALSE(o1 >= 2);
+  EXPECT_FALSE(o1 >= 2);
+  EXPECT_FALSE(o1 > 2);
+  */
+}
+
+TEST(Expected, Conversions) {
+  Expected<bool, E> mbool;
+  Expected<short, E> mshort;
+  Expected<char*, E> mstr;
+  Expected<int, E> mint;
+
+  EXPECT_FALSE((std::is_convertible<Expected<bool, E>&, bool>::value));
+  EXPECT_FALSE((std::is_convertible<Expected<short, E>&, short>::value));
+  EXPECT_FALSE((std::is_convertible<Expected<char*, E>&, char*>::value));
+  EXPECT_FALSE((std::is_convertible<Expected<int, E>&, int>::value));
+
+  // intended explicit operator bool, for if (ex).
+  bool b(mbool);
+  EXPECT_FALSE(b);
+
+  // Truthy tests work and are not ambiguous
+  if (mbool && mshort && mstr && mint) { // only checks not-empty
+    if (*mbool && *mshort && *mstr && *mint) { // only checks value
+      ;
+    }
+  }
+
+  mbool = false;
+  EXPECT_TRUE(bool(mbool));
+  EXPECT_FALSE(*mbool);
+
+  mbool = true;
+  EXPECT_TRUE(bool(mbool));
+  EXPECT_TRUE(*mbool);
+
+  mbool = {unexpected, E::E1};
+  EXPECT_FALSE(bool(mbool));
+
+  // No conversion allowed; does not compile
+  // mbool == false;
+}
+
+TEST(Expected, Pointee) {
+  Expected<int, E> x;
+  EXPECT_FALSE(get_pointer(x));
+  x = 1;
+  EXPECT_TRUE(get_pointer(x));
+  *get_pointer(x) = 2;
+  EXPECT_TRUE(*x == 2);
+  x = {unexpected, E::E1};
+  EXPECT_FALSE(get_pointer(x));
+}
+
+TEST(Expected, MakeOptional) {
+  // const L-value version
+  const std::string s("abc");
+  auto exStr = makeExpected<E>(s);
+  ASSERT_TRUE(exStr.hasValue());
+  EXPECT_EQ(*exStr, "abc");
+  *exStr = "cde";
+  EXPECT_EQ(s, "abc");
+  EXPECT_EQ(*exStr, "cde");
+
+  // L-value version
+  std::string s2("abc");
+  auto exStr2 = makeExpected<E>(s2);
+  ASSERT_TRUE(exStr2.hasValue());
+  EXPECT_EQ(*exStr2, "abc");
+  *exStr2 = "cde";
+  // it's vital to check that s2 wasn't clobbered
+  EXPECT_EQ(s2, "abc");
+
+  // L-value reference version
+  std::string& s3(s2);
+  auto exStr3 = makeExpected<E>(s3);
+  ASSERT_TRUE(exStr3.hasValue());
+  EXPECT_EQ(*exStr3, "abc");
+  *exStr3 = "cde";
+  EXPECT_EQ(s3, "abc");
+
+  // R-value ref version
+  unique_ptr<int> pInt(new int(3));
+  auto exIntPtr = makeExpected<E>(std::move(pInt));
+  EXPECT_TRUE(pInt.get() == nullptr);
+  ASSERT_TRUE(exIntPtr.hasValue());
+  EXPECT_EQ(**exIntPtr, 3);
+}
+
+#if __CLANG_PREREQ(3, 6)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wself-move"
+#endif
+
+TEST(Expected, SelfAssignment) {
+  Expected<std::string, E> a = "42";
+  a = a;
+  ASSERT_TRUE(a.hasValue() && a.value() == "42");
+
+  Expected<std::string, E> b = "23333333";
+  b = std::move(b);
+  ASSERT_TRUE(b.hasValue() && b.value() == "23333333");
+}
+
+#if __CLANG_PREREQ(3, 6)
+#pragma clang diagnostic pop
+#endif
+
+class ContainsExpected {
+ public:
+  ContainsExpected() {}
+  explicit ContainsExpected(int x) : ex_(x) {}
+  bool hasValue() const {
+    return ex_.hasValue();
+  }
+  int value() const {
+    return ex_.value();
+  }
+
+  ContainsExpected(const ContainsExpected& other) = default;
+  ContainsExpected& operator=(const ContainsExpected& other) = default;
+  ContainsExpected(ContainsExpected&& other) = default;
+  ContainsExpected& operator=(ContainsExpected&& other) = default;
+
+ private:
+  Expected<int, E> ex_;
+};
+
+/**
+ * Test that a class containing an Expected can be copy and move assigned.
+ * This was broken under gcc 4.7 until assignment operators were explicitly
+ * defined.
+ */
+TEST(Expected, AssignmentContained) {
+  {
+    ContainsExpected source(5), target;
+    target = source;
+    EXPECT_TRUE(target.hasValue());
+    EXPECT_EQ(5, target.value());
+  }
+
+  {
+    ContainsExpected source(5), target;
+    target = std::move(source);
+    EXPECT_TRUE(target.hasValue());
+    EXPECT_EQ(5, target.value());
+    EXPECT_TRUE(source.hasValue());
+  }
+
+  {
+    ContainsExpected ex_uninit, target(10);
+    target = ex_uninit;
+    EXPECT_FALSE(target.hasValue());
+  }
+}
+
+TEST(Expected, Exceptions) {
+  Expected<int, E> empty;
+  EXPECT_THROW(empty.value(), Unexpected<E>::BadExpectedAccess);
+}
+
+struct ThrowingBadness {
+  ThrowingBadness() noexcept(false);
+  ThrowingBadness(const ThrowingBadness&) noexcept(false);
+  ThrowingBadness(ThrowingBadness&&) noexcept(false);
+  ThrowingBadness& operator=(const ThrowingBadness&) noexcept(false);
+  ThrowingBadness& operator=(ThrowingBadness&&) noexcept(false);
+};
+
+TEST(Expected, NoThrowDefaultConstructible) {
+  EXPECT_TRUE(
+      (std::is_nothrow_default_constructible<Expected<bool, E>>::value));
+  EXPECT_TRUE(
+      (std::is_nothrow_default_constructible<Expected<std::string, E>>::value));
+  EXPECT_TRUE((std::is_nothrow_default_constructible<
+               Expected<ThrowingBadness, E>>::value));
+  EXPECT_FALSE((std::is_nothrow_default_constructible<
+                Expected<int, ThrowingBadness>>::value));
+}
+
+TEST(Expected, NoThrowMoveConstructible) {
+  EXPECT_TRUE((std::is_nothrow_move_constructible<Expected<bool, E>>::value));
+  EXPECT_TRUE((std::is_nothrow_move_constructible<
+               Expected<std::unique_ptr<int>, E>>::value));
+  EXPECT_FALSE((
+      std::is_nothrow_move_constructible<Expected<ThrowingBadness, E>>::value));
+}
+
+TEST(Expected, NoThrowMoveAssignable) {
+  EXPECT_TRUE((std::is_nothrow_move_assignable<Expected<bool, E>>::value));
+  EXPECT_TRUE((std::is_nothrow_move_assignable<
+               Expected<std::unique_ptr<int>, E>>::value));
+  EXPECT_FALSE(
+      (std::is_nothrow_move_assignable<Expected<ThrowingBadness, E>>::value));
+}
+
+struct NoDestructor {};
+
+struct WithDestructor {
+  ~WithDestructor();
+};
+
+TEST(Expected, TriviallyDestructible) {
+  // These could all be static_asserts but EXPECT_* give much nicer output on
+  // failure.
+  EXPECT_TRUE(
+      (std::is_trivially_destructible<Expected<NoDestructor, E>>::value));
+  EXPECT_TRUE((std::is_trivially_destructible<Expected<int, E>>::value));
+  EXPECT_FALSE(
+      (std::is_trivially_destructible<Expected<WithDestructor, E>>::value));
+}
+
+struct NoConstructor {};
+
+struct WithConstructor {
+  WithConstructor();
+};
+
+TEST(Expected, TriviallyCopyable) {
+  // These could all be static_asserts but EXPECT_* give much nicer output on
+  // failure.
+  EXPECT_TRUE((IsTriviallyCopyable<Expected<int, E>>::value));
+  EXPECT_TRUE((IsTriviallyCopyable<Expected<char*, E>>::value));
+  EXPECT_TRUE(
+      (IsTriviallyCopyable<Expected<NoDestructor, E>>::value));
+  EXPECT_FALSE(
+      (IsTriviallyCopyable<Expected<WithDestructor, E>>::value));
+  EXPECT_TRUE(
+      (IsTriviallyCopyable<Expected<NoConstructor, E>>::value));
+  EXPECT_FALSE(
+      (IsTriviallyCopyable<Expected<std::string, E>>::value));
+  EXPECT_FALSE(
+      (IsTriviallyCopyable<Expected<int, std::string>>::value));
+// libstdc++ with GCC 4.x doesn't have std::is_trivially_copyable
+#if (defined(__clang__) && !defined(_LIBCPP_VERSION)) || \
+    !(defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 5)
+  EXPECT_TRUE(
+      (IsTriviallyCopyable<Expected<WithConstructor, E>>::value));
+#endif
+  EXPECT_TRUE(
+      (IsTriviallyCopyable<Expected<Expected<int, E>, E>>::value));
+}
+
+TEST(Expected, Then) {
+  // Lifting
+  {
+    Expected<int, E> ex = Expected<std::unique_ptr<int>, E>{
+        in_place, new int(42)}.then([](std::unique_ptr<int> p) { return *p; });
+    EXPECT_TRUE(bool(ex));
+    EXPECT_EQ(42, *ex);
+  }
+
+  // Flattening
+  {
+    Expected<int, E> ex =
+        Expected<std::unique_ptr<int>, E>{in_place, new int(42)}.then(
+            [](std::unique_ptr<int> p) { return makeExpected<E>(*p); });
+    EXPECT_TRUE(bool(ex));
+    EXPECT_EQ(42, *ex);
+  }
+
+  // Void
+  {
+    Expected<Unit, E> ex = Expected<std::unique_ptr<int>, E>{
+        in_place, new int(42)}.then([](std::unique_ptr<int>) {});
+    EXPECT_TRUE(bool(ex));
+  }
+
+  // Non-flattening (different error codes)
+  {
+    Expected<Expected<int, int>, E> ex =
+        Expected<std::unique_ptr<int>, E>{in_place, new int(42)}.then(
+            [](std::unique_ptr<int> p) { return makeExpected<int>(*p); });
+    EXPECT_TRUE(bool(ex));
+    EXPECT_TRUE(bool(*ex));
+    EXPECT_EQ(42, **ex);
+  }
+
+  {
+    // Error case:
+    Expected<int, E> ex = Expected<std::unique_ptr<int>, E>{
+        unexpected, E::E1}.then([](std::unique_ptr<int> p) -> int {
+      EXPECT_TRUE(false);
+      return *p;
+    });
+    EXPECT_FALSE(bool(ex));
+    EXPECT_EQ(E::E1, ex.error());
+  }
+
+  // Chaining
+  {
+    Expected<std::string, E> ex =
+        Expected<std::unique_ptr<int>, E>{in_place, new int(42)}.then(
+            [](std::unique_ptr<int> p) { return makeExpected<E>(*p); },
+            [](int i) { return i == 42 ? "yes" : "no"; });
+    EXPECT_TRUE(bool(ex));
+    EXPECT_EQ("yes", *ex);
+  }
+
+  // Chaining with errors
+  {
+    Expected<std::string, E> ex =
+        Expected<std::unique_ptr<int>, E>{in_place, new int(42)}.then(
+            [](std::unique_ptr<int>) {
+              return Expected<int, E>(unexpected, E::E1);
+            },
+            [](int i) { return i == 42 ? "yes" : "no"; });
+    EXPECT_FALSE(bool(ex));
+    EXPECT_EQ(E::E1, ex.error());
+  }
+}
+
+TEST(Expected, ThenOrThrow) {
+  {
+    int e =
+        Expected<std::unique_ptr<int>, E>{in_place, new int(42)}.thenOrThrow(
+            [](std::unique_ptr<int> p) { return *p; });
+    EXPECT_EQ(42, e);
+  }
+
+  {
+    EXPECT_THROW(
+        (Expected<std::unique_ptr<int>, E>{unexpected, E::E1}.thenOrThrow(
+            [](std::unique_ptr<int> p) { return *p; })),
+        Unexpected<E>::BadExpectedAccess);
+  }
+
+  {
+    EXPECT_THROW(
+        (Expected<std::unique_ptr<int>, E>{unexpected, E::E1}.thenOrThrow(
+            [](std::unique_ptr<int> p) { return *p; },
+            [](E) { return std::runtime_error(""); })),
+        std::runtime_error);
+  }
+
+  {
+    EXPECT_THROW(
+        (Expected<std::unique_ptr<int>, E>{unexpected, E::E1}.thenOrThrow(
+            [](std::unique_ptr<int> p) { return *p; },
+            [](E) { throw std::runtime_error(""); })),
+        std::runtime_error);
+  }
+
+  {
+    EXPECT_THROW(
+        (Expected<std::unique_ptr<int>, E>{unexpected, E::E1}.thenOrThrow(
+            [](std::unique_ptr<int> p) { return *p; }, [](E) {})),
+        Unexpected<E>::BadExpectedAccess);
+  }
+}
+}
index 99d2e3bb5161b21bc5a689e0b09737a452dda450..24e241f48b338b7c8d6cf5bb3c82c2d5a3b2dd56 100644 (file)
@@ -10,6 +10,7 @@ TESTS= \
        hash_test \
        timeout_queue_test \
        conv_test \
+       expected_test \
        range_test \
        bits_test \
        bit_iterator_test
@@ -128,6 +129,9 @@ timeout_queue_test_LDADD = libfollytestmain.la
 conv_test_SOURCES = ConvTest.cpp
 conv_test_LDADD = libfollytestmain.la $(top_builddir)/libfollybenchmark.la
 
+expected_test_SOURCES = ExpectedTest.cpp
+expected_test_LDADD = libfollytestmain.la $(top_builddir)/libfollybenchmark.la
+
 range_test_SOURCES = RangeTest.cpp
 range_test_LDADD = libfollytestmain.la
 
index 453c29ed6a39db703629977a0b966b9ca474a46d..23541b8e386617cc0920ff6d2e7e8a8fc5111c8e 100644 (file)
@@ -52,7 +52,7 @@ TEST(Traits, scalars) {
 
 TEST(Traits, containers) {
   EXPECT_TRUE  (IsRelocatable<vector<F1>>::value);
-  EXPECT_FALSE((IsRelocatable<pair<F1, F1>>::value));
+  EXPECT_TRUE ((IsRelocatable<pair<F1, F1>>::value));
   EXPECT_TRUE ((IsRelocatable<pair<T1, T2>>::value));
 }
 
@@ -69,8 +69,8 @@ TEST(Traits, typedefd) {
 }
 
 TEST(Traits, unset) {
-  EXPECT_FALSE(IsRelocatable<F1>::value);
-  EXPECT_FALSE(IsRelocatable<F4>::value);
+  EXPECT_TRUE(IsRelocatable<F1>::value);
+  EXPECT_TRUE(IsRelocatable<F4>::value);
 }
 
 TEST(Traits, bitprop) {