/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FOLLY_GEN_COMBINE_H_
#error This file may only be included from folly/gen/Combine.h
#endif
#include <iterator>
#include <system_error>
#include <tuple>
#include <type_traits>
namespace folly {
namespace gen {
namespace detail {
/**
* Interleave
*
* Alternate values from a sequence with values from a sequence container.
* Stops once we run out of values from either source.
*/
template <class Container>
class Interleave : public Operator<Interleave<Container>> {
// see comment about copies in CopiedSource
const std::shared_ptr<Container> container_;
public:
explicit Interleave(Container container)
: container_(new Container(std::move(container))) {}
template <class Value, class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
const std::shared_ptr<Container> container_;
public:
explicit Generator(
Source source, const std::shared_ptr<Container> container)
: source_(std::move(source)), container_(container) {}
template <class Handler>
bool apply(Handler&& handler) const {
auto iter = container_->begin();
return source_.apply([&](Value value) -> bool {
if (iter == container_->end()) {
return false;
}
if (!handler(std::forward<Value>(value))) {
return false;
}
if (!handler(std::move(*iter))) {
return false;
}
iter++;
return true;
});
}
};
template <class Value2, class Source, class Gen = Generator<Value2, Source>>
Gen compose(GenImpl<Value2, Source>&& source) const {
return Gen(std::move(source.self()), container_);
}
template <class Value2, class Source, class Gen = Generator<Value2, Source>>
Gen compose(const GenImpl<Value2, Source>& source) const {
return Gen(source.self(), container_);
}
};
/**
* Zip
*
* Combine inputs from Source with values from a sequence container by merging
* them into a tuple.
*
*/
template <class Container>
class Zip : public Operator<Zip<Container>> {
// see comment about copies in CopiedSource
const std::shared_ptr<Container> container_;
public:
explicit Zip(Container container)
: container_(new Container(std::move(container))) {}
template <
class Value,
class Source,
class Result = std::tuple<
typename std::decay<Value>::type,
typename std::decay<typename Container::value_type>::type>>
class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
Source source_;
const std::shared_ptr<Container> container_;
public:
explicit Generator(
Source source, const std::shared_ptr<Container> container)
: source_(std::move(source)), container_(container) {}
template <class Handler>
bool apply(Handler&& handler) const {
auto iter = container_->begin();
return source_.apply([&](Value value) -> bool {
if (iter == container_->end()) {
return false;
}
if (!handler(std::make_tuple(
std::forward<Value>(value), std::move(*iter)))) {
return false;
}
++iter;
return true;
});
}
};
template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), container_);
}
template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), container_);
}
};
template <class... Types1, class... Types2>
auto add_to_tuple(std::tuple<Types1...> t1, std::tuple<Types2...> t2)
-> std::tuple<Types1..., Types2...> {
return std::tuple_cat(std::move(t1), std::move(t2));
}
template <class... Types1, class Type2>
auto add_to_tuple(std::tuple<Types1...> t1, Type2&& t2)
-> decltype(std::tuple_cat(
std::move(t1), std::make_tuple(std::forward<Type2>(t2)))) {
return std::tuple_cat(
std::move(t1), std::make_tuple(std::forward<Type2>(t2)));
}
template <class Type1, class... Types2>
auto add_to_tuple(Type1&& t1, std::tuple<Types2...> t2)
-> decltype(std::tuple_cat(
std::make_tuple(std::forward<Type1>(t1)), std::move(t2))) {
return std::tuple_cat(
std::make_tuple(std::forward<Type1>(t1)), std::move(t2));
}
template <class Type1, class Type2>
auto add_to_tuple(Type1&& t1, Type2&& t2) -> decltype(std::make_tuple(
std::forward<Type1>(t1),
std::forward<Type2>(t2))) {
return std::make_tuple(std::forward<Type1>(t1), std::forward<Type2>(t2));
}
// Merges a 2-tuple into a single tuple (get<0> could already be a tuple)
class MergeTuples {
public:
template <class Tuple>
auto operator()(Tuple&& value) const
-> decltype(add_to_tuple(
std::get<0>(std::forward<Tuple>(value)),
std::get<1>(std::forward<Tuple>(value)))) {
static_assert(
std::tuple_size<typename std::remove_reference<Tuple>::type>::value ==
2,
"Can only merge tuples of size 2");
return add_to_tuple(
std::get<0>(std::forward<Tuple>(value)),
std::get<1>(std::forward<Tuple>(value)));
}
};
} // namespace detail
// TODO(mcurtiss): support zip() for N>1 operands.
template <
class Source,
class Zip = detail::Zip<typename std::decay<Source>::type>>
Zip zip(Source&& source) {
return Zip(std::forward<Source>(source));
}
} // namespace gen
} // namespace folly