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 | |
26 | namespace folly { |
27 | namespace gen { |
28 | namespace 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 | */ |
36 | template <class Container> |
37 | class 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 | */ |
98 | template <class Container> |
99 | class 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 | |
152 | template <class... Types1, class... Types2> |
153 | auto 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 | |
158 | template <class... Types1, class Type2> |
159 | auto 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 | |
165 | template <class Type1, class... Types2> |
166 | auto 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 | |
172 | template <class Type1, class Type2> |
173 | auto 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) |
179 | class 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. |
199 | template < |
200 | class Source, |
201 | class Zip = detail::Zip<typename std::decay<Source>::type>> |
202 | Zip zip(Source&& source) { |
203 | return Zip(std::forward<Source>(source)); |
204 | } |
205 | |
206 | } // namespace gen |
207 | } // namespace folly |
208 | |