guard<Exception>()
authorTom Jackson <tjackson@fb.com>
Wed, 3 Jul 2013 18:38:32 +0000 (11:38 -0700)
committerSara Golemon <sgolemon@fb.com>
Tue, 9 Jul 2013 19:05:34 +0000 (12:05 -0700)
Summary: For handling exceptions from downstream operations.

Test Plan: Unit tests

Reviewed By: marcelo.juchem@fb.com

FB internal diff: D874344

folly/experimental/Gen-inl.h
folly/experimental/Gen.h
folly/experimental/test/GenTest.cpp

index 8b0c4dc748e79173dc7b8df62ec1ed19c98d8d8a..064bd6b51cad17227d7af8b81f23cef8a0e2004b 100644 (file)
@@ -1675,11 +1675,11 @@ class RangeConcat : public Operator<RangeConcat> {
  public:
   RangeConcat() { }
 
-  template<class Source,
-           class Range,
+  template<class Range,
+           class Source,
            class InnerValue = typename ValueTypeOfRange<Range>::RefType>
   class Generator
-    : public GenImpl<InnerValue, Generator<Source, Range, InnerValue>> {
+    : public GenImpl<InnerValue, Generator<Range, Source, InnerValue>> {
     Source source_;
    public:
     explicit Generator(Source source)
@@ -1709,19 +1709,89 @@ class RangeConcat : public Operator<RangeConcat> {
 
   template<class Value,
            class Source,
-           class Gen = Generator<Source, Value>>
+           class Gen = Generator<Value, Source>>
   Gen compose(GenImpl<Value, Source>&& source) const {
     return Gen(std::move(source.self()));
   }
 
   template<class Value,
            class Source,
-           class Gen = Generator<Source, Value>>
+           class Gen = Generator<Value, Source>>
   Gen compose(const GenImpl<Value, Source>& source) const {
     return Gen(source.self());
   }
 };
 
+
+/**
+ * Guard - For handling exceptions from downstream computation. Requires the
+ * type of exception to catch, and handler function to invoke in the event of
+ * the exception. Note that the handler may:
+ *   1) return true to continue processing the sequence
+ *   2) return false to end the sequence immediately
+ *   3) throw, to pass the exception to the next catch
+ * The handler must match the signature 'bool(Exception&, Value)'.
+ *
+ * This type is used through the `guard` helper, like so:
+ *
+ *  auto indexes
+ *    = byLine(STDIN_FILENO)
+ *    | guard<std::runtime_error>([](std::runtime_error& e,
+ *                                   StringPiece sp) {
+ *        LOG(ERROR) << sp << ": " << e.str();
+ *        return true; // continue processing subsequent lines
+ *      })
+ *    | eachTo<int>()
+ *    | as<vector>();
+ **/
+template<class Exception,
+         class ErrorHandler>
+class Guard : public Operator<Guard<Exception, ErrorHandler>> {
+  ErrorHandler handler_;
+ public:
+  Guard(ErrorHandler handler)
+    : handler_(std::move(handler)) {}
+
+  template<class Value,
+           class Source>
+  class Generator : public GenImpl<Value, Generator<Value, Source>> {
+    Source source_;
+    ErrorHandler handler_;
+  public:
+    explicit Generator(Source source,
+                       ErrorHandler handler)
+      : source_(std::move(source)),
+        handler_(std::move(handler)) {}
+
+    template<class Handler>
+    bool apply(Handler&& handler) const {
+      return source_.apply([&](Value value) {
+        try {
+          handler(std::forward<Value>(value));
+          return true;
+        } catch (Exception& e) {
+          return handler_(e, std::forward<Value>(value));
+        }
+      });
+    }
+
+    static constexpr bool infinite = Source::infinite;
+  };
+
+  template<class Value,
+           class Source,
+           class Gen = Generator<Value, Source>>
+  Gen compose(GenImpl<Value, Source>&& source) const {
+    return Gen(std::move(source.self()), handler_);
+  }
+
+  template<class Value,
+           class Source,
+           class Gen = Generator<Value, Source>>
+  Gen compose(const GenImpl<Value, Source>& source) const {
+    return Gen(source.self(), handler_);
+  }
+};
 } //::detail
 
 /**
index 47fab1976a81a51b788cc1660e52fb5f04855be3..12407fc776c54218da235c3a9341513c3dd37526 100644 (file)
@@ -343,6 +343,10 @@ struct GeneratorBuilder;
 template<class Needle>
 class Contains;
 
+template<class Exception,
+         class ErrorHandler>
+class Guard;
+
 }
 
 /**
@@ -622,6 +626,14 @@ Contains contains(Needle&& needle) {
   return Contains(std::forward<Needle>(needle));
 }
 
+template<class Exception,
+         class ErrorHandler,
+         class Guard = detail::Guard<Exception,
+                                     typename std::decay<ErrorHandler>::type>>
+Guard guard(ErrorHandler&& handler) {
+  return Guard(std::forward<ErrorHandler>(handler));
+}
+
 }} // folly::gen
 
 #include "folly/experimental/Gen-inl.h"
index ddb7bf8a74ed60ce29a629706b1d740724172d96..d2412af0e85ef030a6f65e882be8cff17a685967 100644 (file)
@@ -1214,6 +1214,38 @@ INSTANTIATE_TEST_CASE_P(
     FileGenBufferedTest,
     ::testing::Values(0, 1, 2, 4, 8, 64, 4096));
 
+TEST(Gen, Guard) {
+  using std::runtime_error;
+  EXPECT_THROW(from({"1", "a", "3"})
+               | eachTo<int>()
+               | sum,
+               runtime_error);
+  EXPECT_EQ(4,
+            from({"1", "a", "3"})
+            | guard<runtime_error>([](runtime_error&, const char*) {
+                return true; // continue
+              })
+            | eachTo<int>()
+            | sum);
+  EXPECT_EQ(1,
+            from({"1", "a", "3"})
+            | guard<runtime_error>([](runtime_error&, const char*) {
+                return false; // break
+              })
+            | eachTo<int>()
+            | sum);
+  EXPECT_THROW(from({"1", "a", "3"})
+                | guard<runtime_error>([](runtime_error&, const char* v) {
+                    if (v[0] == 'a') {
+                      throw;
+                    }
+                    return true;
+                  })
+               | eachTo<int>()
+               | sum,
+               runtime_error);
+}
+
 int main(int argc, char *argv[]) {
   testing::InitGoogleTest(&argc, argv);
   google::ParseCommandLineFlags(&argc, &argv, true);