1/*
2 * Copyright 2014-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef FOLLY_GEN_COMBINE_H_
18#error This file may only be included from folly/gen/Combine.h
19#endif
20
21#include <iterator>
22#include <system_error>
23#include <tuple>
24#include <type_traits>
25
26namespace folly {
27namespace gen {
28namespace detail {
29
30/**
31 * Interleave
32 *
33 * Alternate values from a sequence with values from a sequence container.
34 * Stops once we run out of values from either source.
35 */
36template <class Container>
37class Interleave : public Operator<Interleave<Container>> {
38 // see comment about copies in CopiedSource
39 const std::shared_ptr<const Container> container_;
40
41 public:
42 explicit Interleave(Container container)
43 : container_(new Container(std::move(container))) {}
44
45 template <class Value, class Source>
46 class Generator : public GenImpl<Value, Generator<Value, Source>> {
47 Source source_;
48 const std::shared_ptr<const Container> container_;
49 typedef const typename Container::value_type& ConstRefType;
50
51 static_assert(
52 std::is_same<const Value&, ConstRefType>::value,
53 "Only matching types may be interleaved");
54
55 public:
56 explicit Generator(
57 Source source,
58 const std::shared_ptr<const Container> container)
59 : source_(std::move(source)), container_(container) {}
60
61 template <class Handler>
62 bool apply(Handler&& handler) const {
63 auto iter = container_->begin();
64 return source_.apply([&](const Value& value) -> bool {
65 if (iter == container_->end()) {
66 return false;
67 }
68 if (!handler(value)) {
69 return false;
70 }
71 if (!handler(*iter)) {
72 return false;
73 }
74 iter++;
75 return true;
76 });
77 }
78 };
79
80 template <class Value2, class Source, class Gen = Generator<Value2, Source>>
81 Gen compose(GenImpl<Value2, Source>&& source) const {
82 return Gen(std::move(source.self()), container_);
83 }
84
85 template <class Value2, class Source, class Gen = Generator<Value2, Source>>
86 Gen compose(const GenImpl<Value2, Source>& source) const {
87 return Gen(source.self(), container_);
88 }
89};
90
91/**
92 * Zip
93 *
94 * Combine inputs from Source with values from a sequence container by merging
95 * them into a tuple.
96 *
97 */
98template <class Container>
99class Zip : public Operator<Zip<Container>> {
100 // see comment about copies in CopiedSource
101 const std::shared_ptr<const Container> container_;
102
103 public:
104 explicit Zip(Container container)
105 : container_(new Container(std::move(container))) {}
106
107 template <
108 class Value1,
109 class Source,
110 class Value2 = decltype(*std::begin(*container_)),
111 class Result = std::tuple<
112 typename std::decay<Value1>::type,
113 typename std::decay<Value2>::type>>
114 class Generator
115 : public GenImpl<Result, Generator<Value1, Source, Value2, Result>> {
116 Source source_;
117 const std::shared_ptr<const Container> container_;
118
119 public:
120 explicit Generator(
121 Source source,
122 const std::shared_ptr<const Container> container)
123 : source_(std::move(source)), container_(container) {}
124
125 template <class Handler>
126 bool apply(Handler&& handler) const {
127 auto iter = container_->begin();
128 return (source_.apply([&](Value1 value) -> bool {
129 if (iter == container_->end()) {
130 return false;
131 }
132 if (!handler(std::make_tuple(std::forward<Value1>(value), *iter))) {
133 return false;
134 }
135 ++iter;
136 return true;
137 }));
138 }
139 };
140
141 template <class Source, class Value, class Gen = Generator<Value, Source>>
142 Gen compose(GenImpl<Value, Source>&& source) const {
143 return Gen(std::move(source.self()), container_);
144 }
145
146 template <class Source, class Value, class Gen = Generator<Value, Source>>
147 Gen compose(const GenImpl<Value, Source>& source) const {
148 return Gen(source.self(), container_);
149 }
150};
151
152template <class... Types1, class... Types2>
153auto add_to_tuple(std::tuple<Types1...> t1, std::tuple<Types2...> t2)
154 -> std::tuple<Types1..., Types2...> {
155 return std::tuple_cat(std::move(t1), std::move(t2));
156}
157
158template <class... Types1, class Type2>
159auto add_to_tuple(std::tuple<Types1...> t1, Type2&& t2) -> decltype(
160 std::tuple_cat(std::move(t1), std::make_tuple(std::forward<Type2>(t2)))) {
161 return std::tuple_cat(
162 std::move(t1), std::make_tuple(std::forward<Type2>(t2)));
163}
164
165template <class Type1, class... Types2>
166auto add_to_tuple(Type1&& t1, std::tuple<Types2...> t2) -> decltype(
167 std::tuple_cat(std::make_tuple(std::forward<Type1>(t1)), std::move(t2))) {
168 return std::tuple_cat(
169 std::make_tuple(std::forward<Type1>(t1)), std::move(t2));
170}
171
172template <class Type1, class Type2>
173auto add_to_tuple(Type1&& t1, Type2&& t2) -> decltype(
174 std::make_tuple(std::forward<Type1>(t1), std::forward<Type2>(t2))) {
175 return std::make_tuple(std::forward<Type1>(t1), std::forward<Type2>(t2));
176}
177
178// Merges a 2-tuple into a single tuple (get<0> could already be a tuple)
179class MergeTuples {
180 public:
181 template <class Tuple>
182 auto operator()(Tuple&& value) const -> decltype(add_to_tuple(
183 std::get<0>(std::forward<Tuple>(value)),
184 std::get<1>(std::forward<Tuple>(value)))) {
185 static_assert(
186 std::tuple_size<typename std::remove_reference<Tuple>::type>::value ==
187 2,
188 "Can only merge tuples of size 2");
189 return add_to_tuple(
190 std::get<0>(std::forward<Tuple>(value)),
191 std::get<1>(std::forward<Tuple>(value)));
192 }
193};
194
195} // namespace detail
196
197// TODO(mcurtiss): support zip() for N>1 operands. Because of variadic problems,
198// this might not be easily possible until gcc4.8 is available.
199template <
200 class Source,
201 class Zip = detail::Zip<typename std::decay<Source>::type>>
202Zip zip(Source&& source) {
203 return Zip(std::forward<Source>(source));
204}
205
206} // namespace gen
207} // namespace folly
208