201 lines
6.0 KiB
C++
201 lines
6.0 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its 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
|