Future::unit()
authorHans Fugal <fugalh@fb.com>
Thu, 11 Jun 2015 20:28:57 +0000 (13:28 -0700)
committerSara Golemon <sgolemon@fb.com>
Thu, 11 Jun 2015 21:49:22 +0000 (14:49 -0700)
Summary: Discard a result but propagate an exception. cf https://www.facebook.com/groups/715931878455430/permalink/879624552086161/

Reviewed By: @​sammerat

Differential Revision: D2144567

folly/futures/Future-inl.h
folly/futures/Future.h
folly/futures/test/UnitTest.cpp

index 59498af986ef9768a745b502e7968af11e32a555..446a88f34baa6c139b0519d53113ee7bfdfb01fd 100644 (file)
@@ -49,14 +49,10 @@ Future<T>::Future(T2&& val)
   : core_(new detail::Core<T>(Try<T>(std::forward<T2>(val)))) {}
 
 template <class T>
-template <class T2,
-          typename std::enable_if<
-            folly::is_void_or_unit<T2>::value,
-            int>::type>
+template <typename, typename>
 Future<T>::Future()
   : core_(new detail::Core<T>(Try<T>())) {}
 
-
 template <class T>
 Future<T>::~Future() {
   detach();
@@ -452,6 +448,16 @@ bool Future<T>::isReady() const {
   return core_->ready();
 }
 
+template <class T>
+bool Future<T>::hasValue() {
+  return getTry().hasValue();
+}
+
+template <class T>
+bool Future<T>::hasException() {
+  return getTry().hasException();
+}
+
 template <class T>
 void Future<T>::raise(exception_wrapper exception) {
   core_->raise(std::move(exception));
index 40687b42b4f5cc7deb7dabcf7ace69105ad42e91..98e6eabb64e8efc73888cb02f17eb7eec955dd28 100644 (file)
@@ -60,10 +60,9 @@ class Future {
               !isFuture<typename std::decay<T2>::type>::value>::type>
   /* implicit */ Future(T2&& val);
 
-  template <class T2 = T,
+  template <class T2 = T, typename =
             typename std::enable_if<
-              folly::is_void_or_unit<T2>::value,
-              int>::type = 0>
+              folly::is_void_or_unit<T2>::value>::type>
   Future();
 
   ~Future();
@@ -111,6 +110,12 @@ class Future {
   /** True when the result (or exception) is ready. */
   bool isReady() const;
 
+  /// sugar for getTry().hasValue()
+  bool hasValue();
+
+  /// sugar for getTry().hasException()
+  bool hasException();
+
   /** A reference to the Try of the value */
   Try<T>& getTry();
 
@@ -416,6 +421,11 @@ class Future {
   auto thenMultiWithExecutor(Executor* x, Callback&& fn)
     -> decltype(this->then(std::forward<Callback>(fn)));
 
+  /// Discard a result, but propagate an exception.
+  Future<Unit> unit() {
+    return then([]{ return Unit{}; });
+  }
+
  protected:
   typedef detail::Core<T>* corePtr;
 
index ae49065bd00807321bbbb5bb61f842ef42d1b898..965a830e98be6a6e7af4f8f06c4f3e172c7fab9b 100644 (file)
@@ -20,6 +20,8 @@
 
 using namespace folly;
 
+std::runtime_error eggs("eggs");
+
 TEST(Unit, futureDefaultCtor) {
   Future<Unit>();
 }
@@ -48,3 +50,29 @@ TEST(Unit, liftVoid) {
   auto v = std::is_same<Unit, Lifted::type>::value;
   EXPECT_TRUE(v);
 }
+
+TEST(Unit, futureToUnit) {
+  Future<Unit> fu = makeFuture(42).unit();
+  fu.value();
+  EXPECT_TRUE(makeFuture<int>(eggs).unit().hasException());
+}
+
+TEST(Unit, voidFutureToUnit) {
+  Future<Unit> fu = makeFuture().unit();
+  fu.value();
+  EXPECT_TRUE(makeFuture<void>(eggs).unit().hasException());
+}
+
+TEST(Unit, unitFutureToUnitIdentity) {
+  Future<Unit> fu = makeFuture(Unit{}).unit();
+  fu.value();
+  EXPECT_TRUE(makeFuture<Unit>(eggs).unit().hasException());
+}
+
+TEST(Unit, toUnitWhileInProgress) {
+  Promise<int> p;
+  Future<Unit> fu = p.getFuture().unit();
+  EXPECT_FALSE(fu.isReady());
+  p.setValue(42);
+  EXPECT_TRUE(fu.isReady());
+}