Commit 4d932ecc authored by Eric Niebler's avatar Eric Niebler Committed by Facebook Github Bot

Add folly::Expected, an alternative to exceptions for non-throwing APIs that can fail.

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
parent 73f20857
This diff is collapsed.
......@@ -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 \
......
......@@ -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(); }
......
......@@ -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
......
This diff is collapsed.
......@@ -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
......
......@@ -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) {
......
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