dereference
authorTom Jackson <tjackson@fb.com>
Sun, 4 Aug 2013 21:30:06 +0000 (14:30 -0700)
committerSara Golemon <sgolemon@fb.com>
Wed, 28 Aug 2013 21:30:11 +0000 (14:30 -0700)
Summary:
For dealing with sequences of pointers. Works with pointer wrappers,
and filters out nullptrs, see tests.

Test Plan: Unit tests

Reviewed By: tulloch@fb.com

FB internal diff: D914069

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

index 1d975267605827df3df32c2ecf8da4e2793c1e33..7a752d0feffb25718ab0a6bbf609903c539f17d3 100644 (file)
@@ -1955,6 +1955,65 @@ class Cycle : public Operator<Cycle> {
   }
 };
 
+/**
+ * Dereference - For dereferencing a sequence of pointers while filtering out
+ * null pointers.
+ *
+ * This type is usually used through the 'dereference' static value, like:
+ *
+ *   auto refs = from(ptrs) | dereference;
+ */
+class Dereference : public Operator<Dereference> {
+ public:
+  Dereference() {}
+
+  template<class Value,
+           class Source,
+           class Result = decltype(*std::declval<Value>())>
+  class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
+    Source source_;
+  public:
+    explicit Generator(Source source)
+      : source_(std::move(source)) {}
+
+    template<class Body>
+    void foreach(Body&& body) const {
+      source_.foreach([&](Value value) {
+        if (value) {
+          return body(*value);
+        }
+      });
+    }
+
+    template<class Handler>
+    bool apply(Handler&& handler) const {
+      return source_.apply([&](Value value) -> bool {
+        if (value) {
+          return handler(*value);
+        }
+        return true;
+      });
+    }
+
+    // not actually infinite, since an empty generator will end the cycles.
+    static constexpr bool infinite = Source::infinite;
+  };
+
+  template<class Source,
+           class Value,
+           class Gen = Generator<Value, Source>>
+  Gen compose(GenImpl<Value, Source>&& source) const {
+    return Gen(std::move(source.self()));
+  }
+
+  template<class Source,
+           class Value,
+           class Gen = Generator<Value, Source>>
+  Gen compose(const GenImpl<Value, Source>& source) const {
+    return Gen(source.self());
+  }
+};
+
 } //::detail
 
 /**
@@ -2067,6 +2126,8 @@ static const detail::RangeConcat rconcat;
  */
 static const detail::Cycle cycle;
 
+static const detail::Dereference dereference;
+
 inline detail::Take take(size_t count) {
   return detail::Take(count);
 }
index fa1ec1b2981e73128c850931080c7eee5d50f554..e0f61f0b476f7d0a224bb313217595f4a2a0259a 100644 (file)
@@ -312,6 +312,8 @@ class Cycle;
 
 class Batch;
 
+class Dereference;
+
 /*
  * Sinks
  */
index 9b41f0b7181cfec4e4b1febef81e8fe811c53cd1..499ab0526ed92ced78b024a95ce671fb858e2d36 100644 (file)
 #include <random>
 #include <set>
 #include <vector>
-#include "folly/experimental/Gen.h"
-#include "folly/experimental/StringGen.h"
-#include "folly/experimental/CombineGen.h"
-#include "folly/experimental/FileGen.h"
-#include "folly/experimental/TestUtil.h"
+
 #include "folly/FBString.h"
 #include "folly/FBVector.h"
 #include "folly/Format.h"
+#include "folly/MapUtil.h"
+#include "folly/Memory.h"
 #include "folly/dynamic.h"
+#include "folly/experimental/CombineGen.h"
+#include "folly/experimental/FileGen.h"
+#include "folly/experimental/Gen.h"
+#include "folly/experimental/StringGen.h"
+#include "folly/experimental/TestUtil.h"
 
 using namespace folly::gen;
 using namespace folly;
+using std::make_tuple;
 using std::ostream;
 using std::pair;
 using std::set;
-using std::unique_ptr;
-using std::vector;
 using std::string;
 using std::tuple;
-using std::make_tuple;
+using std::unique_ptr;
+using std::vector;
 
 #define EXPECT_SAME(A, B) \
   static_assert(std::is_same<A, B>::value, "Mismatched: " #A ", " #B)
@@ -1038,6 +1041,60 @@ TEST(Gen, Cycle) {
   }
 }
 
+TEST(Gen, Dereference) {
+  {
+    const int x = 4, y = 2;
+    auto s = from<const int*>({&x, nullptr, &y});
+    EXPECT_EQ(6, s | dereference | sum);
+  }
+  {
+    vector<int> a { 1, 2 };
+    vector<int> b { 3, 4 };
+    vector<vector<int>*> pv { &a, nullptr, &b };
+    from(pv)
+      | dereference
+      | [&](vector<int>& v) {
+          v.push_back(5);
+        };
+    EXPECT_EQ(3, a.size());
+    EXPECT_EQ(3, b.size());
+    EXPECT_EQ(5, a.back());
+    EXPECT_EQ(5, b.back());
+  }
+  {
+    vector<std::map<int, int>> maps {
+      {
+        { 2, 31 },
+        { 3, 41 },
+      },
+      {
+        { 3, 52 },
+        { 4, 62 },
+      },
+      {
+        { 4, 73 },
+        { 5, 83 },
+      },
+    };
+    EXPECT_EQ(
+      93,
+      from(maps)
+      | map([](std::map<int, int>& m) {
+          return get_ptr(m, 3);
+        })
+      | dereference
+      | sum);
+  }
+  {
+    vector<unique_ptr<int>> ups;
+    ups.emplace_back(new int(3));
+    ups.emplace_back();
+    ups.emplace_back(new int(7));
+    EXPECT_EQ(10, from(ups) | dereference | sum);
+    EXPECT_EQ(10, from(ups) | move | dereference | sum);
+  }
+}
+
 TEST(StringGen, Split) {
   auto collect = eachTo<std::string>() | as<vector>();
   {