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