bb3ba6b6a22d8c76985d88eb9a1bc47ddd1e8015
[folly.git] / folly / experimental / CombineGen-inl.h
1 /*
2  * Copyright 2013 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_COMBINEGEN_H_
18 #error This file may only be included from folly/experimental/CombineGen.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  public:
41   explicit Interleave(Container container)
42     : container_(new Container(std::move(container))) {}
43
44   template<class Value,
45            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(std::is_same<const Value&, ConstRefType>::value,
52                   "Only matching types may be interleaved");
53   public:
54     explicit Generator(Source source,
55                        const std::shared_ptr<const Container> container)
56       : source_(std::move(source)),
57         container_(container) { }
58
59     template<class Handler>
60     bool apply(Handler&& handler) const {
61       auto iter = container_->begin();
62       return source_.apply([&](const Value& value) -> bool {
63             if (iter == container_->end()) {
64               return false;
65             }
66             if (!handler(value)) {
67               return false;
68             }
69             if (!handler(*iter)) {
70               return false;
71             }
72             iter++;
73             return true;
74         });
75     }
76   };
77
78   template<class Value2,
79            class Source,
80            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,
86            class Source,
87            class Gen = Generator<Value2,Source>>
88   Gen compose(const GenImpl<Value2, Source>& source) const {
89     return Gen(source.self(), container_);
90   }
91 };
92
93 /**
94  * Zip
95  *
96  * Combine inputs from Source with values from a sequence container by merging
97  * them into a tuple.
98  *
99  */
100 template<class Container>
101 class Zip : public Operator<Zip<Container>> {
102   // see comment about copies in CopiedSource
103   const std::shared_ptr<const Container> container_;
104  public:
105   explicit Zip(Container container)
106     : container_(new Container(std::move(container))) {}
107
108   template<class Value1,
109            class Source,
110            class Value2 = decltype(*std::begin(*container_)),
111            class Result = std::tuple<typename std::decay<Value1>::type,
112                                      typename std::decay<Value2>::type>>
113   class Generator : public GenImpl<Result,
114                                    Generator<Value1,Source,Value2,Result>> {
115     Source source_;
116     const std::shared_ptr<const Container> container_;
117   public:
118     explicit Generator(Source source,
119                        const std::shared_ptr<const Container> container)
120       : source_(std::move(source)),
121         container_(container) { }
122
123     template<class Handler>
124     bool apply(Handler&& handler) const {
125       auto iter = container_->begin();
126       return (source_.apply([&](Value1 value) -> bool {
127             if (iter == container_->end()) {
128               return false;
129             }
130             if (!handler(std::make_tuple(std::forward<Value1>(value), *iter))) {
131               return false;
132             }
133             ++iter;
134             return true;
135           }));
136     }
137   };
138
139   template<class Source,
140            class Value,
141            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,
147            class Value,
148            class Gen = Generator<Value, Source>>
149   Gen compose(const GenImpl<Value, Source>& source) const {
150     return Gen(source.self(), container_);
151   }
152 };
153
154 template<class... Types1,
155          class... Types2>
156 auto add_to_tuple(std::tuple<Types1...> t1, std::tuple<Types2...> t2) ->
157 std::tuple<Types1..., Types2...> {
158   return std::tuple_cat(std::move(t1), std::move(t2));
159 }
160
161 template<class... Types1,
162          class Type2>
163 auto add_to_tuple(std::tuple<Types1...> t1, Type2&& t2) ->
164 decltype(std::tuple_cat(std::move(t1),
165                         std::make_tuple(std::forward<Type2>(t2)))) {
166   return std::tuple_cat(std::move(t1),
167                         std::make_tuple(std::forward<Type2>(t2)));
168 }
169
170 template<class Type1,
171          class... Types2>
172 auto add_to_tuple(Type1&& t1, std::tuple<Types2...> t2) ->
173 decltype(std::tuple_cat(std::make_tuple(std::forward<Type1>(t1)),
174                         std::move(t2))) {
175   return std::tuple_cat(std::make_tuple(std::forward<Type1>(t1)),
176                         std::move(t2));
177 }
178
179 template<class Type1,
180          class Type2>
181 auto add_to_tuple(Type1&& t1, Type2&& t2) ->
182 decltype(std::make_tuple(std::forward<Type1>(t1),
183                          std::forward<Type2>(t2))) {
184   return std::make_tuple(std::forward<Type1>(t1),
185                          std::forward<Type2>(t2));
186 }
187
188 // Merges a 2-tuple into a single tuple (get<0> could already be a tuple)
189 class MergeTuples {
190  public:
191   template<class Tuple>
192   auto operator()(Tuple&& value) const ->
193   decltype(add_to_tuple(std::get<0>(std::forward<Tuple>(value)),
194                         std::get<1>(std::forward<Tuple>(value)))) {
195     static_assert(std::tuple_size<
196                     typename std::remove_reference<Tuple>::type
197                     >::value == 2,
198                   "Can only merge tuples of size 2");
199     return add_to_tuple(std::get<0>(std::forward<Tuple>(value)),
200                         std::get<1>(std::forward<Tuple>(value)));
201   }
202 };
203
204 }  // namespace detail
205
206 static const detail::Map<detail::MergeTuples> tuple_flatten;
207
208 // TODO(mcurtiss): support zip() for N>1 operands. Because of variadic problems,
209 // this might not be easily possible until gcc4.8 is available.
210 template<class Source,
211          class Zip = detail::Zip<typename std::decay<Source>::type>>
212 Zip zip(Source&& source) {
213   return Zip(std::forward<Source>(source));
214 }
215
216 }  // namespace gen
217 }  // namespace folly