HACK: New Gen operators: zip, interleave
[folly.git] / folly / experimental / CombineGen-inl.h
diff --git a/folly/experimental/CombineGen-inl.h b/folly/experimental/CombineGen-inl.h
new file mode 100644 (file)
index 0000000..bb3ba6b
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2013 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FOLLY_COMBINEGEN_H_
+#error This file may only be included from folly/experimental/CombineGen.h
+#endif
+
+#include <iterator>
+#include <system_error>
+#include <tuple>
+#include <type_traits>
+
+namespace folly {
+namespace gen {
+namespace detail {
+
+/**
+ * Interleave
+ *
+ * Alternate values from a sequence with values from a sequence container.
+ * Stops once we run out of values from either source.
+ */
+template<class Container>
+class Interleave : public Operator<Interleave<Container>> {
+  // see comment about copies in CopiedSource
+  const std::shared_ptr<const Container> container_;
+ public:
+  explicit Interleave(Container container)
+    : container_(new Container(std::move(container))) {}
+
+  template<class Value,
+           class Source>
+  class Generator : public GenImpl<Value, Generator<Value, Source>> {
+    Source source_;
+    const std::shared_ptr<const Container> container_;
+    typedef const typename Container::value_type& ConstRefType;
+
+    static_assert(std::is_same<const Value&, ConstRefType>::value,
+                  "Only matching types may be interleaved");
+  public:
+    explicit Generator(Source source,
+                       const std::shared_ptr<const Container> container)
+      : source_(std::move(source)),
+        container_(container) { }
+
+    template<class Handler>
+    bool apply(Handler&& handler) const {
+      auto iter = container_->begin();
+      return source_.apply([&](const Value& value) -> bool {
+            if (iter == container_->end()) {
+              return false;
+            }
+            if (!handler(value)) {
+              return false;
+            }
+            if (!handler(*iter)) {
+              return false;
+            }
+            iter++;
+            return true;
+        });
+    }
+  };
+
+  template<class Value2,
+           class Source,
+           class Gen = Generator<Value2,Source>>
+  Gen compose(GenImpl<Value2, Source>&& source) const {
+    return Gen(std::move(source.self()), container_);
+  }
+
+  template<class Value2,
+           class Source,
+           class Gen = Generator<Value2,Source>>
+  Gen compose(const GenImpl<Value2, Source>& source) const {
+    return Gen(source.self(), container_);
+  }
+};
+
+/**
+ * Zip
+ *
+ * Combine inputs from Source with values from a sequence container by merging
+ * them into a tuple.
+ *
+ */
+template<class Container>
+class Zip : public Operator<Zip<Container>> {
+  // see comment about copies in CopiedSource
+  const std::shared_ptr<const Container> container_;
+ public:
+  explicit Zip(Container container)
+    : container_(new Container(std::move(container))) {}
+
+  template<class Value1,
+           class Source,
+           class Value2 = decltype(*std::begin(*container_)),
+           class Result = std::tuple<typename std::decay<Value1>::type,
+                                     typename std::decay<Value2>::type>>
+  class Generator : public GenImpl<Result,
+                                   Generator<Value1,Source,Value2,Result>> {
+    Source source_;
+    const std::shared_ptr<const Container> container_;
+  public:
+    explicit Generator(Source source,
+                       const std::shared_ptr<const Container> container)
+      : source_(std::move(source)),
+        container_(container) { }
+
+    template<class Handler>
+    bool apply(Handler&& handler) const {
+      auto iter = container_->begin();
+      return (source_.apply([&](Value1 value) -> bool {
+            if (iter == container_->end()) {
+              return false;
+            }
+            if (!handler(std::make_tuple(std::forward<Value1>(value), *iter))) {
+              return false;
+            }
+            ++iter;
+            return true;
+          }));
+    }
+  };
+
+  template<class Source,
+           class Value,
+           class Gen = Generator<Value, Source>>
+  Gen compose(GenImpl<Value, Source>&& source) const {
+    return Gen(std::move(source.self()), container_);
+  }
+
+  template<class Source,
+           class Value,
+           class Gen = Generator<Value, Source>>
+  Gen compose(const GenImpl<Value, Source>& source) const {
+    return Gen(source.self(), container_);
+  }
+};
+
+template<class... Types1,
+         class... Types2>
+auto add_to_tuple(std::tuple<Types1...> t1, std::tuple<Types2...> t2) ->
+std::tuple<Types1..., Types2...> {
+  return std::tuple_cat(std::move(t1), std::move(t2));
+}
+
+template<class... Types1,
+         class Type2>
+auto add_to_tuple(std::tuple<Types1...> t1, Type2&& t2) ->
+decltype(std::tuple_cat(std::move(t1),
+                        std::make_tuple(std::forward<Type2>(t2)))) {
+  return std::tuple_cat(std::move(t1),
+                        std::make_tuple(std::forward<Type2>(t2)));
+}
+
+template<class Type1,
+         class... Types2>
+auto add_to_tuple(Type1&& t1, std::tuple<Types2...> t2) ->
+decltype(std::tuple_cat(std::make_tuple(std::forward<Type1>(t1)),
+                        std::move(t2))) {
+  return std::tuple_cat(std::make_tuple(std::forward<Type1>(t1)),
+                        std::move(t2));
+}
+
+template<class Type1,
+         class Type2>
+auto add_to_tuple(Type1&& t1, Type2&& t2) ->
+decltype(std::make_tuple(std::forward<Type1>(t1),
+                         std::forward<Type2>(t2))) {
+  return std::make_tuple(std::forward<Type1>(t1),
+                         std::forward<Type2>(t2));
+}
+
+// Merges a 2-tuple into a single tuple (get<0> could already be a tuple)
+class MergeTuples {
+ public:
+  template<class Tuple>
+  auto operator()(Tuple&& value) const ->
+  decltype(add_to_tuple(std::get<0>(std::forward<Tuple>(value)),
+                        std::get<1>(std::forward<Tuple>(value)))) {
+    static_assert(std::tuple_size<
+                    typename std::remove_reference<Tuple>::type
+                    >::value == 2,
+                  "Can only merge tuples of size 2");
+    return add_to_tuple(std::get<0>(std::forward<Tuple>(value)),
+                        std::get<1>(std::forward<Tuple>(value)));
+  }
+};
+
+}  // namespace detail
+
+static const detail::Map<detail::MergeTuples> tuple_flatten;
+
+// TODO(mcurtiss): support zip() for N>1 operands. Because of variadic problems,
+// this might not be easily possible until gcc4.8 is available.
+template<class Source,
+         class Zip = detail::Zip<typename std::decay<Source>::type>>
+Zip zip(Source&& source) {
+  return Zip(std::forward<Source>(source));
+}
+
+}  // namespace gen
+}  // namespace folly