Commit 96e8ac5b authored by Christopher Dykes's avatar Christopher Dykes Committed by Facebook Github Bot 9

Fix folly::Expected under MSVC

Summary:
There are 3 separate issues that this addresses to get Expected working correctly under MSVC.
The first is simply that it doesn't like `StrictConjunction`. Switching that to `std::conjunction`, which is part of C++17 and already implemented in MSVC, solves that issue.
The second is that MSVC was complaining that all members must be initialized in a `constexpr` constructor, but only one of the base classes of `ExpectedStorage` was being initialized, and, as there were no default `constexpr` constructors for the other bases, they couldn't be initialized. This was solved by making the base class's default constructors `constexpr`.
The last was the most fun, as the simple solutions all resulted in internal compiler errors. MSVC doesn't like SFINAE evaluation in the context of a template type parameter on a static operator defined inline in the class via `friend`. The solution is to simply move the definitions after the class and only include the declarations to be able to mark them as `friend`.

Reviewed By: ericniebler

Differential Revision: D3731833

fbshipit-source-id: ea9244b247b69046866f27cee9fdbd1b2405cafb
parent 060485b6
...@@ -92,8 +92,15 @@ using ExpectedErrorType = ...@@ -92,8 +92,15 @@ using ExpectedErrorType =
// Details... // Details...
namespace expected_detail { namespace expected_detail {
#ifdef _MSC_VER
// MSVC 2015 can't handle the StrictConjunction, so we have
// to use std::conjunction instead.
template <template <class...> class Trait, class... Ts>
using StrictAllOf = std::conjunction<Trait<Ts>...>;
#else
template <template <class...> class Trait, class... Ts> template <template <class...> class Trait, class... Ts>
using StrictAllOf = StrictConjunction<Trait<Ts>...>; using StrictAllOf = StrictConjunction<Trait<Ts>...>;
#endif
template <class T> template <class T>
using IsCopyable = StrictConjunction< using IsCopyable = StrictConjunction<
...@@ -287,28 +294,28 @@ struct ExpectedUnion { ...@@ -287,28 +294,28 @@ struct ExpectedUnion {
template <class Derived, bool, bool Noexcept> template <class Derived, bool, bool Noexcept>
struct CopyConstructible { struct CopyConstructible {
CopyConstructible() = default; constexpr CopyConstructible() = default;
CopyConstructible(const CopyConstructible& that) noexcept(Noexcept) { CopyConstructible(const CopyConstructible& that) noexcept(Noexcept) {
static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that)); static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that));
} }
CopyConstructible(CopyConstructible&&) = default; constexpr CopyConstructible(CopyConstructible&&) = default;
CopyConstructible& operator=(const CopyConstructible&) = default; CopyConstructible& operator=(const CopyConstructible&) = default;
CopyConstructible& operator=(CopyConstructible&&) = default; CopyConstructible& operator=(CopyConstructible&&) = default;
}; };
template <class Derived, bool Noexcept> template <class Derived, bool Noexcept>
struct CopyConstructible<Derived, false, Noexcept> { struct CopyConstructible<Derived, false, Noexcept> {
CopyConstructible() = default; constexpr CopyConstructible() = default;
CopyConstructible(const CopyConstructible&) = delete; CopyConstructible(const CopyConstructible&) = delete;
CopyConstructible(CopyConstructible&&) = default; constexpr CopyConstructible(CopyConstructible&&) = default;
CopyConstructible& operator=(const CopyConstructible&) = default; CopyConstructible& operator=(const CopyConstructible&) = default;
CopyConstructible& operator=(CopyConstructible&&) = default; CopyConstructible& operator=(CopyConstructible&&) = default;
}; };
template <class Derived, bool, bool Noexcept> template <class Derived, bool, bool Noexcept>
struct MoveConstructible { struct MoveConstructible {
MoveConstructible() = default; constexpr MoveConstructible() = default;
MoveConstructible(const MoveConstructible&) = default; constexpr MoveConstructible(const MoveConstructible&) = default;
MoveConstructible(MoveConstructible&& that) noexcept(Noexcept) { MoveConstructible(MoveConstructible&& that) noexcept(Noexcept) {
static_cast<Derived*>(this)->assign(std::move(static_cast<Derived&>(that))); static_cast<Derived*>(this)->assign(std::move(static_cast<Derived&>(that)));
} }
...@@ -318,8 +325,8 @@ struct MoveConstructible { ...@@ -318,8 +325,8 @@ struct MoveConstructible {
template <class Derived, bool Noexcept> template <class Derived, bool Noexcept>
struct MoveConstructible<Derived, false, Noexcept> { struct MoveConstructible<Derived, false, Noexcept> {
MoveConstructible() = default; constexpr MoveConstructible() = default;
MoveConstructible(const MoveConstructible&) = default; constexpr MoveConstructible(const MoveConstructible&) = default;
MoveConstructible(MoveConstructible&&) = delete; MoveConstructible(MoveConstructible&&) = delete;
MoveConstructible& operator=(const MoveConstructible&) = default; MoveConstructible& operator=(const MoveConstructible&) = default;
MoveConstructible& operator=(MoveConstructible&&) = default; MoveConstructible& operator=(MoveConstructible&&) = default;
...@@ -327,9 +334,9 @@ struct MoveConstructible<Derived, false, Noexcept> { ...@@ -327,9 +334,9 @@ struct MoveConstructible<Derived, false, Noexcept> {
template <class Derived, bool, bool Noexcept> template <class Derived, bool, bool Noexcept>
struct CopyAssignable { struct CopyAssignable {
CopyAssignable() = default; constexpr CopyAssignable() = default;
CopyAssignable(const CopyAssignable&) = default; constexpr CopyAssignable(const CopyAssignable&) = default;
CopyAssignable(CopyAssignable&&) = default; constexpr CopyAssignable(CopyAssignable&&) = default;
CopyAssignable& operator=(const CopyAssignable& that) noexcept(Noexcept) { CopyAssignable& operator=(const CopyAssignable& that) noexcept(Noexcept) {
static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that)); static_cast<Derived*>(this)->assign(static_cast<const Derived&>(that));
return *this; return *this;
...@@ -339,18 +346,18 @@ struct CopyAssignable { ...@@ -339,18 +346,18 @@ struct CopyAssignable {
template <class Derived, bool Noexcept> template <class Derived, bool Noexcept>
struct CopyAssignable<Derived, false, Noexcept> { struct CopyAssignable<Derived, false, Noexcept> {
CopyAssignable() = default; constexpr CopyAssignable() = default;
CopyAssignable(const CopyAssignable&) = default; constexpr CopyAssignable(const CopyAssignable&) = default;
CopyAssignable(CopyAssignable&&) = default; constexpr CopyAssignable(CopyAssignable&&) = default;
CopyAssignable& operator=(const CopyAssignable&) = delete; CopyAssignable& operator=(const CopyAssignable&) = delete;
CopyAssignable& operator=(CopyAssignable&&) = default; CopyAssignable& operator=(CopyAssignable&&) = default;
}; };
template <class Derived, bool, bool Noexcept> template <class Derived, bool, bool Noexcept>
struct MoveAssignable { struct MoveAssignable {
MoveAssignable() = default; constexpr MoveAssignable() = default;
MoveAssignable(const MoveAssignable&) = default; constexpr MoveAssignable(const MoveAssignable&) = default;
MoveAssignable(MoveAssignable&&) = default; constexpr MoveAssignable(MoveAssignable&&) = default;
MoveAssignable& operator=(const MoveAssignable&) = default; MoveAssignable& operator=(const MoveAssignable&) = default;
MoveAssignable& operator=(MoveAssignable&& that) noexcept(Noexcept) { MoveAssignable& operator=(MoveAssignable&& that) noexcept(Noexcept) {
static_cast<Derived*>(this)->assign(std::move(static_cast<Derived&>(that))); static_cast<Derived*>(this)->assign(std::move(static_cast<Derived&>(that)));
...@@ -360,9 +367,9 @@ struct MoveAssignable { ...@@ -360,9 +367,9 @@ struct MoveAssignable {
template <class Derived, bool Noexcept> template <class Derived, bool Noexcept>
struct MoveAssignable<Derived, false, Noexcept> { struct MoveAssignable<Derived, false, Noexcept> {
MoveAssignable() = default; constexpr MoveAssignable() = default;
MoveAssignable(const MoveAssignable&) = default; constexpr MoveAssignable(const MoveAssignable&) = default;
MoveAssignable(MoveAssignable&&) = default; constexpr MoveAssignable(MoveAssignable&&) = default;
MoveAssignable& operator=(const MoveAssignable&) = default; MoveAssignable& operator=(const MoveAssignable&) = default;
MoveAssignable& operator=(MoveAssignable&& that) = delete; MoveAssignable& operator=(MoveAssignable&& that) = delete;
}; };
...@@ -558,6 +565,9 @@ struct ExpectedHelper { ...@@ -558,6 +565,9 @@ struct ExpectedHelper {
return static_cast<This&&>(ex); return static_cast<This&&>(ex);
} }
FOLLY_PUSH_WARNING
// Don't warn about not using the overloaded comma operator.
FOLLY_MSVC_DISABLE_WARNING(4913)
template < template <
class This, class This,
class Fn, class Fn,
...@@ -605,6 +615,7 @@ struct ExpectedHelper { ...@@ -605,6 +615,7 @@ struct ExpectedHelper {
throw typename Unexpected<ExpectedErrorType<This>>::MakeBadExpectedAccess()( throw typename Unexpected<ExpectedErrorType<This>>::MakeBadExpectedAccess()(
static_cast<This&&>(ex).error()); static_cast<This&&>(ex).error());
} }
FOLLY_POP_WARNING
}; };
} }
/* using override */ using expected_detail_ExpectedHelper::ExpectedHelper; /* using override */ using expected_detail_ExpectedHelper::ExpectedHelper;
...@@ -701,18 +712,6 @@ class Unexpected final { ...@@ -701,18 +712,6 @@ class Unexpected final {
return std::move(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: private:
struct MakeBadExpectedAccess { struct MakeBadExpectedAccess {
template <class E> template <class E>
...@@ -724,6 +723,22 @@ class Unexpected final { ...@@ -724,6 +723,22 @@ class Unexpected final {
Error error_; Error error_;
}; };
template <
class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Error>::value)>
inline bool operator==(
const Unexpected<Error>& lhs,
const Unexpected<Error>& rhs) {
return lhs.error() == rhs.error();
}
template <
class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Error>::value)>
inline bool operator!=(
const Unexpected<Error>& lhs,
const Unexpected<Error>& rhs) {
return !(lhs == rhs);
}
/** /**
* For constructing an Unexpected object from an error code. Unexpected objects * For constructing an Unexpected object from an error code. Unexpected objects
* are implicitly convertible to Expected object in the error state. Usage is * are implicitly convertible to Expected object in the error state. Usage is
...@@ -1044,50 +1059,12 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> { ...@@ -1044,50 +1059,12 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
/** /**
* Relational Operators * Relational Operators
*/ */
FOLLY_REQUIRES(IsEqualityComparable<Value>::value) template <class Val, class Err>
friend bool operator==(const Expected& lhs, const Expected& rhs) { friend typename std::enable_if<IsEqualityComparable<Val>::value, bool>::type
if (UNLIKELY(lhs.which_ != rhs.which_)) operator==(const Expected<Val, Err>& lhs, const Expected<Val, Err>& rhs);
return UNLIKELY(lhs.uninitializedByException()) template <class Val, class Err>
? false friend typename std::enable_if<IsLessThanComparable<Val>::value, bool>::type
: throw BadExpectedAccess(); operator<(const Expected<Val, Err>& lhs, const Expected<Val, Err>& rhs);
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 * Accessors
...@@ -1270,6 +1247,72 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> { ...@@ -1270,6 +1247,72 @@ class Expected final : expected_detail::ExpectedStorage<Value, Error> {
} }
}; };
template <class Value, class Error>
inline typename std::enable_if<IsEqualityComparable<Value>::value, bool>::type
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();
if (UNLIKELY(lhs.hasError()))
return true; // All error states are considered equal
return lhs.value_ == rhs.value_;
}
template <
class Value,
class Error FOLLY_REQUIRES_TRAILING(IsEqualityComparable<Value>::value)>
inline bool operator!=(
const Expected<Value, Error>& lhs,
const Expected<Value, Error>& rhs) {
return !(rhs == lhs);
}
template <class Value, class Error>
inline typename std::enable_if<IsLessThanComparable<Value>::value, bool>::type
operator<(
const Expected<Value, Error>& lhs,
const Expected<Value, Error>& 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_;
}
template <
class Value,
class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)>
inline bool operator<=(
const Expected<Value, Error>& lhs,
const Expected<Value, Error>& rhs) {
return !(rhs < lhs);
}
template <
class Value,
class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)>
inline bool operator>(
const Expected<Value, Error>& lhs,
const Expected<Value, Error>& rhs) {
return rhs < lhs;
}
template <
class Value,
class Error FOLLY_REQUIRES_TRAILING(IsLessThanComparable<Value>::value)>
inline bool operator>=(
const Expected<Value, Error>& lhs,
const Expected<Value, Error>& rhs) {
return !(lhs < rhs);
}
/** /**
* swap Expected values * swap Expected values
*/ */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment