/* * 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. */ #include #include #include #include #include #include #include #include #include #include #include namespace folly { namespace for_each_detail { namespace adl { /* using override */ using std::begin; /* using override */ using std::end; /* using override */ using std::get; /** * The adl_ functions below lookup the function name in the namespace of the * type of the object being passed into the function. If no function with that * name exists for the passed object then the default std:: versions are going * to be called */ template auto adl_get(Type&& instance) -> decltype(get(std::declval())) { return get(std::forward(instance)); } template auto adl_begin(Type&& instance) -> decltype(begin(instance)) { return begin(instance); } template auto adl_end(Type&& instance) -> decltype(end(instance)) { return end(instance); } } // namespace adl /** * Enable if the tuple supports fetching via a member get<>() */ template using EnableIfMemberGetFound = void_t().template get<0>())>; template struct IsMemberGetFound : std::false_type {}; template struct IsMemberGetFound, T> : std::true_type {}; /** * A get that tries member get<> first and if that is not found tries ADL get<>. * This mechanism is as found in the structured bindings proposal here 11.5.3. * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf */ template < std::size_t Index, typename Type, std::enable_if_t::value, int> = 0> auto get_impl(Type&& instance) -> decltype(adl::adl_get(static_cast(instance))) { return adl::adl_get(static_cast(instance)); } template < std::size_t Index, typename Type, std::enable_if_t::value, int> = 0> auto get_impl(Type&& instance) -> decltype(static_cast(instance).template get()) { return static_cast(instance).template get(); } /** * Check if the sequence is a tuple */ template ::type> using EnableIfTuple = void_t< decltype(get_impl<0>(std::declval())), decltype(std::tuple_size::value)>; template struct IsTuple : std::false_type {}; template struct IsTuple, T> : std::true_type {}; /** * Check if the sequence is a range */ template ::type> using EnableIfRange = void_t< decltype(adl::adl_begin(std::declval())), decltype(adl::adl_end(std::declval()))>; template struct IsRange : std::false_type {}; template struct IsRange, T> : std::true_type {}; struct TupleTag {}; struct RangeTag {}; /** * Should ideally check if it is a tuple and if not return void, but msvc fails */ template using SequenceTag = std::conditional_t::value, RangeTag, TupleTag>; struct BeginAddTag {}; struct IndexingTag {}; template using ForEachImplTag = std::conditional_t< is_invocable_v, Iter>, index_constant<3>, std::conditional_t< is_invocable_v>, index_constant<2>, std::conditional_t< is_invocable_v, index_constant<1>, void>>>; template < typename Func, typename... Args, std::enable_if_t, int> = 0> LoopControl invoke_returning_loop_control(Func&& f, Args&&... a) { return static_cast(f)(static_cast(a)...); } template < typename Func, typename... Args, std::enable_if_t, int> = 0> LoopControl invoke_returning_loop_control(Func&& f, Args&&... a) { static_assert( std::is_void>::value, "return either LoopControl or void"); return static_cast(f)(static_cast(a)...), loop_continue; } /** * Implementations for the runtime function */ template void for_each_range_impl(index_constant<3>, Sequence&& range, Func& func) { auto first = adl::adl_begin(range); auto last = adl::adl_end(range); for (auto index = std::size_t{0}; first != last; ++index) { auto next = std::next(first); auto control = invoke_returning_loop_control(func, *first, index, first); if (loop_break == control) { break; } first = next; } } template void for_each_range_impl(index_constant<2>, Sequence&& range, Func& func) { // make a three arg adaptor for the function passed in so that the main // implementation function can be used auto three_arg_adaptor = [&func]( auto&& ele, auto index, auto) -> decltype(auto) { return func(std::forward(ele), index); }; for_each_range_impl( index_constant<3>{}, std::forward(range), three_arg_adaptor); } template void for_each_range_impl(index_constant<1>, Sequence&& range, Func& func) { // make a three argument adaptor for the function passed in that just ignores // the second and third argument auto three_arg_adaptor = [&func](auto&& ele, auto, auto) -> decltype(auto) { return func(std::forward(ele)); }; for_each_range_impl( index_constant<3>{}, std::forward(range), three_arg_adaptor); } /** * Handlers for iteration */ template void for_each_tuple_impl( std::index_sequence, Sequence&& seq, Func& func) { using _ = int[]; // unroll the loop in an initializer list construction parameter expansion // pack auto control = loop_continue; // cast to void to ignore the result; use the int[] initialization to do the // loop execution, the ternary conditional will decide whether or not to // evaluate the result // // if func does not return loop-control, expect the optimizer to see through // invoke_returning_loop_control always returning loop_continue void( _{(((control == loop_continue) ? (control = invoke_returning_loop_control( func, get_impl(std::forward(seq)), index_constant{})) : (loop_continue)), 0)...}); } /** * The two top level compile time loop iteration functions handle the dispatch * based on the number of arguments the passed in function can be passed, if 2 * arguments can be passed then the implementation dispatches work further to * the implementation classes above. If not then an adaptor is constructed * which is passed on to the 2 argument specialization, which then in turn * forwards implementation to the implementation classes above */ template void for_each_tuple_impl(index_constant<2>, Sequence&& seq, Func& func) { // pass the length as an index sequence to the implementation as an // optimization over manual template "tail recursion" unrolling using size = std::tuple_size::type>; for_each_tuple_impl( std::make_index_sequence{}, std::forward(seq), func); } template void for_each_tuple_impl(index_constant<1>, Sequence&& seq, Func& func) { // make an adaptor for the function passed in, in case it can only be passed // on argument auto two_arg_adaptor = [&func](auto&& ele, auto) -> decltype(auto) { return func(std::forward(ele)); }; for_each_tuple_impl( index_constant<2>{}, std::forward(seq), two_arg_adaptor); } /** * Top level handlers for the for_each loop, with one overload for tuples and * one overload for ranges * * This implies that if type is both a range and a tuple, it is treated as a * range rather than as a tuple */ template void for_each_impl(TupleTag, Sequence&& range, Func& func) { using type = decltype(get_impl<0>(std::declval())); using tag = ForEachImplTag; static_assert(!std::is_same::value, "unknown invocability"); for_each_tuple_impl(tag{}, std::forward(range), func); } template void for_each_impl(RangeTag, Sequence&& range, Func& func) { using iter = decltype(adl::adl_begin(std::declval())); using type = decltype(*std::declval()); using tag = ForEachImplTag; static_assert(!std::is_same::value, "unknown invocability"); for_each_range_impl(tag{}, std::forward(range), func); } template decltype(auto) fetch_impl(IndexingTag, Sequence&& sequence, Index&& index) { return std::forward(sequence)[std::forward(index)]; } template decltype(auto) fetch_impl(BeginAddTag, Sequence&& sequence, Index index) { return *(adl::adl_begin(std::forward(sequence)) + index); } template decltype(auto) fetch_impl(TupleTag, Sequence&& sequence, Index index) { return get_impl(std::forward(sequence)); } template decltype(auto) fetch_impl(RangeTag, Sequence&& sequence, Index&& index) { using iter = decltype(adl::adl_begin(std::declval())); using iter_traits = std::iterator_traits>; using iter_cat = typename iter_traits::iterator_category; using tag = std::conditional_t< std::is_same::value, BeginAddTag, IndexingTag>; return fetch_impl( tag{}, std::forward(sequence), std::forward(index)); } } // namespace for_each_detail template constexpr Func for_each(Sequence&& sequence, Func func) { namespace fed = for_each_detail; using tag = fed::SequenceTag; fed::for_each_impl(tag{}, std::forward(sequence), func); return func; } template constexpr decltype(auto) fetch(Sequence&& sequence, Index&& index) { namespace fed = for_each_detail; using tag = fed::SequenceTag; return for_each_detail::fetch_impl( tag{}, std::forward(sequence), std::forward(index)); } } // namespace folly