/* * 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_CORE_H_ #error This file may only be included from folly/gen/Core.h #endif #include #include #include // Ignore shadowing warnings within this file, so includers can use -Wshadow. FOLLY_PUSH_WARNING FOLLY_GNU_DISABLE_WARNING("-Wshadow") namespace folly { namespace gen { /** * IsCompatibleSignature - Trait type for testing whether a given Functor * matches an expected signature. * * Usage: * IsCompatibleSignature::value */ template class IsCompatibleSignature { static constexpr bool value = false; }; template class IsCompatibleSignature { template < class F, class ActualReturn = decltype(std::declval()(std::declval()...)), bool good = std::is_same::value> static constexpr bool testArgs(int*) { return good; } template static constexpr bool testArgs(...) { return false; } public: static constexpr bool value = testArgs(nullptr); }; /** * FBounded - Helper type for the curiously recurring template pattern, used * heavily here to enable inlining and obviate virtual functions */ template struct FBounded { const Self& self() const { return *static_cast(this); } Self& self() { return *static_cast(this); } }; /** * Operator - Core abstraction of an operation which may be applied to a * generator. All operators implement a method compose(), which takes a * generator and produces an output generator. */ template class Operator : public FBounded { public: /** * compose() - Must be implemented by child class to compose a new Generator * out of a given generator. This function left intentionally unimplemented. */ template ResultGen compose(const GenImpl& source) const; protected: Operator() = default; Operator(Operator&&) noexcept = default; Operator(const Operator&) = default; Operator& operator=(Operator&&) noexcept = default; Operator& operator=(const Operator&) = default; }; /** * operator|() - For composing two operators without binding it to a * particular generator. */ template < class Left, class Right, class Composed = detail::Composed> Composed operator|(const Operator& left, const Operator& right) { return Composed(left.self(), right.self()); } template < class Left, class Right, class Composed = detail::Composed> Composed operator|(const Operator& left, Operator&& right) { return Composed(left.self(), std::move(right.self())); } template < class Left, class Right, class Composed = detail::Composed> Composed operator|(Operator&& left, const Operator& right) { return Composed(std::move(left.self()), right.self()); } template < class Left, class Right, class Composed = detail::Composed> Composed operator|(Operator&& left, Operator&& right) { return Composed(std::move(left.self()), std::move(right.self())); } /** * GenImpl - Core abstraction of a generator, an object which produces values by * passing them to a given handler lambda. All generator implementations must * implement apply(). foreach() may also be implemented to special case the * condition where the entire sequence is consumed. */ template class GenImpl : public FBounded { protected: // To prevent slicing GenImpl() = default; GenImpl(GenImpl&&) = default; GenImpl(const GenImpl&) = default; GenImpl& operator=(GenImpl&&) = default; GenImpl& operator=(const GenImpl&) = default; public: typedef Value ValueType; typedef typename std::decay::type StorageType; /** * apply() - Send all values produced by this generator to given handler until * the handler returns false. Returns false if and only if the handler passed * in returns false. Note: It should return true even if it completes (without * the handler returning false), as 'Chain' uses the return value of apply to * determine if it should process the second object in its chain. */ template bool apply(Handler&& handler) const; /** * foreach() - Send all values produced by this generator to given lambda. */ template void foreach(Body&& body) const { this->self().apply([&](Value value) -> bool { static_assert(!infinite, "Cannot call foreach on infinite GenImpl"); body(std::forward(value)); return true; }); } // Child classes should override if the sequence generated is *definitely* // infinite. 'infinite' may be false_type for some infinite sequences // (due the the Halting Problem). // // In general, almost all sources are finite (only seq(n) produces an infinite // source), almost all operators keep the finiteness of the source (only cycle // makes an infinite generator from a finite one, only until and take make a // finite generator from an infinite one, and concat needs both the inner and // outer generators to be finite to make a finite one), and most sinks // cannot accept and infinite generators (first being the expection). static constexpr bool infinite = false; }; template < class LeftValue, class Left, class RightValue, class Right, class Chain = detail::Chain> Chain operator+( const GenImpl& left, const GenImpl& right) { static_assert( std::is_same::value, "Generators may ony be combined if Values are the exact same type."); return Chain(left.self(), right.self()); } template < class LeftValue, class Left, class RightValue, class Right, class Chain = detail::Chain> Chain operator+( const GenImpl& left, GenImpl&& right) { static_assert( std::is_same::value, "Generators may ony be combined if Values are the exact same type."); return Chain(left.self(), std::move(right.self())); } template < class LeftValue, class Left, class RightValue, class Right, class Chain = detail::Chain> Chain operator+( GenImpl&& left, const GenImpl& right) { static_assert( std::is_same::value, "Generators may ony be combined if Values are the exact same type."); return Chain(std::move(left.self()), right.self()); } template < class LeftValue, class Left, class RightValue, class Right, class Chain = detail::Chain> Chain operator+( GenImpl&& left, GenImpl&& right) { static_assert( std::is_same::value, "Generators may ony be combined if Values are the exact same type."); return Chain(std::move(left.self()), std::move(right.self())); } /** * operator|() which enables foreach-like usage: * gen | [](Value v) -> void {...}; */ template typename std::enable_if< IsCompatibleSignature::value>::type operator|(const GenImpl& gen, Handler&& handler) { static_assert( !Gen::infinite, "Cannot pull all values from an infinite sequence."); gen.self().foreach(std::forward(handler)); } /** * operator|() which enables foreach-like usage with 'break' support: * gen | [](Value v) -> bool { return shouldContinue(); }; */ template typename std:: enable_if::value, bool>::type operator|(const GenImpl& gen, Handler&& handler) { return gen.self().apply(std::forward(handler)); } /** * operator|() for composing generators with operators, similar to boosts' range * adaptors: * gen | map(square) | sum */ template auto operator|(const GenImpl& gen, const Operator& op) -> decltype(op.self().compose(gen.self())) { return op.self().compose(gen.self()); } template auto operator|(GenImpl&& gen, const Operator& op) -> decltype(op.self().compose(std::move(gen.self()))) { return op.self().compose(std::move(gen.self())); } namespace detail { /** * Composed - For building up a pipeline of operations to perform, absent any * particular source generator. Useful for building up custom pipelines. * * This type is usually used by just piping two operators together: * * auto valuesOf = filter([](Optional& o) { return o.hasValue(); }) * | map([](Optional& o) -> int& { return o.value(); }); * * auto valuesIncluded = from(optionals) | valuesOf | as(); */ template class Composed : public Operator> { First first_; Second second_; public: Composed() = default; Composed(First first, Second second) : first_(std::move(first)), second_(std::move(second)) {} template < class Source, class Value, class FirstRet = decltype(std::declval().compose(std::declval())), class SecondRet = decltype(std::declval().compose(std::declval()))> SecondRet compose(const GenImpl& source) const { return second_.compose(first_.compose(source.self())); } template < class Source, class Value, class FirstRet = decltype(std::declval().compose(std::declval())), class SecondRet = decltype(std::declval().compose(std::declval()))> SecondRet compose(GenImpl&& source) const { return second_.compose(first_.compose(std::move(source.self()))); } }; /** * Chain - For concatenating the values produced by two Generators. * * This type is primarily used through using '+' to combine generators, like: * * auto nums = seq(1, 10) + seq(20, 30); * int total = nums | sum; */ template class Chain : public GenImpl> { First first_; Second second_; public: explicit Chain(First first, Second second) : first_(std::move(first)), second_(std::move(second)) {} template bool apply(Handler&& handler) const { return first_.apply(std::forward(handler)) && second_.apply(std::forward(handler)); } template void foreach(Body&& body) const { first_.foreach(std::forward(body)); second_.foreach(std::forward(body)); } static constexpr bool infinite = First::infinite || Second::infinite; }; } // namespace detail } // namespace gen } // namespace folly FOLLY_POP_WARNING