2 * Copyright 2014-present Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #ifndef FOLLY_GEN_COMBINE_H_
18 #error This file may only be included from folly/gen/Combine.h
22 #include <system_error>
24 #include <type_traits>
33 * Alternate values from a sequence with values from a sequence container.
34 * Stops once we run out of values from either source.
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_;
41 explicit Interleave(Container container)
42 : container_(new Container(std::move(container))) {}
44 template <class Value, class Source>
45 class Generator : public GenImpl<Value, Generator<Value, Source>> {
47 const std::shared_ptr<const Container> container_;
48 typedef const typename Container::value_type& ConstRefType;
50 static_assert(std::is_same<const Value&, ConstRefType>::value,
51 "Only matching types may be interleaved");
54 explicit Generator(Source source,
55 const std::shared_ptr<const Container> container)
56 : source_(std::move(source)),
57 container_(container) { }
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()) {
66 if (!handler(value)) {
69 if (!handler(*iter)) {
78 template <class Value2, class Source, class Gen = Generator<Value2, Source>>
79 Gen compose(GenImpl<Value2, Source>&& source) const {
80 return Gen(std::move(source.self()), container_);
83 template <class Value2, class Source, class Gen = Generator<Value2, Source>>
84 Gen compose(const GenImpl<Value2, Source>& source) const {
85 return Gen(source.self(), container_);
92 * Combine inputs from Source with values from a sequence container by merging
96 template <class Container>
97 class Zip : public Operator<Zip<Container>> {
98 // see comment about copies in CopiedSource
99 const std::shared_ptr<const Container> container_;
101 explicit Zip(Container container)
102 : container_(new Container(std::move(container))) {}
107 class Value2 = decltype(*std::begin(*container_)),
108 class Result = std::tuple<
109 typename std::decay<Value1>::type,
110 typename std::decay<Value2>::type>>
111 class Generator : public GenImpl<Result,
112 Generator<Value1,Source,Value2,Result>> {
114 const std::shared_ptr<const Container> container_;
117 explicit Generator(Source source,
118 const std::shared_ptr<const Container> container)
119 : source_(std::move(source)),
120 container_(container) { }
122 template <class Handler>
123 bool apply(Handler&& handler) const {
124 auto iter = container_->begin();
125 return (source_.apply([&](Value1 value) -> bool {
126 if (iter == container_->end()) {
129 if (!handler(std::make_tuple(std::forward<Value1>(value), *iter))) {
138 template <class Source, class Value, class Gen = Generator<Value, Source>>
139 Gen compose(GenImpl<Value, Source>&& source) const {
140 return Gen(std::move(source.self()), container_);
143 template <class Source, class Value, class Gen = Generator<Value, Source>>
144 Gen compose(const GenImpl<Value, Source>& source) const {
145 return Gen(source.self(), container_);
149 template <class... Types1, class... Types2>
150 auto add_to_tuple(std::tuple<Types1...> t1, std::tuple<Types2...> t2) ->
151 std::tuple<Types1..., Types2...> {
152 return std::tuple_cat(std::move(t1), std::move(t2));
155 template <class... Types1, class Type2>
156 auto add_to_tuple(std::tuple<Types1...> t1, Type2&& t2) ->
157 decltype(std::tuple_cat(std::move(t1),
158 std::make_tuple(std::forward<Type2>(t2)))) {
159 return std::tuple_cat(std::move(t1),
160 std::make_tuple(std::forward<Type2>(t2)));
163 template <class Type1, class... Types2>
164 auto add_to_tuple(Type1&& t1, std::tuple<Types2...> t2) ->
165 decltype(std::tuple_cat(std::make_tuple(std::forward<Type1>(t1)),
167 return std::tuple_cat(std::make_tuple(std::forward<Type1>(t1)),
171 template <class Type1, class Type2>
172 auto add_to_tuple(Type1&& t1, Type2&& t2) ->
173 decltype(std::make_tuple(std::forward<Type1>(t1),
174 std::forward<Type2>(t2))) {
175 return std::make_tuple(std::forward<Type1>(t1),
176 std::forward<Type2>(t2));
179 // Merges a 2-tuple into a single tuple (get<0> could already be a tuple)
182 template <class Tuple>
183 auto operator()(Tuple&& value) const ->
184 decltype(add_to_tuple(std::get<0>(std::forward<Tuple>(value)),
185 std::get<1>(std::forward<Tuple>(value)))) {
186 static_assert(std::tuple_size<
187 typename std::remove_reference<Tuple>::type
189 "Can only merge tuples of size 2");
190 return add_to_tuple(std::get<0>(std::forward<Tuple>(value)),
191 std::get<1>(std::forward<Tuple>(value)));
195 } // namespace detail
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.
201 class Zip = detail::Zip<typename std::decay<Source>::type>>
202 Zip zip(Source&& source) {
203 return Zip(std::forward<Source>(source));