folly/folly/container/Foreach-inl.h

/*
 * 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.
 */

#include <cassert>
#include <cstdint>
#include <initializer_list>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <utility>

#include <folly/Portability.h>
#include <folly/Traits.h>
#include <folly/Utility.h>
#include <folly/container/Access.h>
#include <folly/functional/Invoke.h>

namespace folly {

namespace for_each_detail {

namespace adl {

/* using override */
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 <std::size_t Index, typename Type>
auto adl_get(Type&& instance) -> decltype(get<Index>(std::declval<Type>())) {}

} // namespace adl

/**
 * Enable if the tuple supports fetching via a member get<>()
 */
EnableIfMemberGetFound;
template <typename, typename T>
struct IsMemberGetFound : std::bool_constant<!require_sizeof<T>> {};
IsMemberGetFound<EnableIfMemberGetFound<T>, T>;

/**
 * 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<!IsMemberGetFound<void, Type>::value, int> = 0>
auto get_impl(Type&& instance)
    -> decltype(adl::adl_get<Index>(static_cast<Type&&>(instance))) {}
template <
    std::size_t Index,
    typename Type,
    std::enable_if_t<IsMemberGetFound<void, Type>::value, int> = 0>
auto get_impl(Type&& instance)
    -> decltype(static_cast<Type&&>(instance).template get<Index>()) {}

/**
 * Check if the sequence is a tuple
 */
EnableIfTuple;
template <typename, typename T>
struct IsTuple : std::bool_constant<!require_sizeof<T>> {};
IsTuple<EnableIfTuple<T>, T>;

/**
 * Check if the sequence is a range
 */
EnableIfRange;
template <typename, typename T>
struct IsRange : std::bool_constant<!require_sizeof<T>> {};
IsRange<EnableIfRange<T>, T>;

struct TupleTag {};
struct RangeTag {};

/**
 * Should ideally check if it is a tuple and if not return void, but msvc fails
 */
SequenceTag;

struct BeginAddTag {};
struct IndexingTag {};

ForEachImplTag;

template <
    typename Func,
    typename... Args,
    std::enable_if_t<is_invocable_r_v<LoopControl, Func, Args...>, int> = 0>
LoopControl invoke_returning_loop_control(Func&& f, Args&&... a) {}
template <
    typename Func,
    typename... Args,
    std::enable_if_t<!is_invocable_r_v<LoopControl, Func, Args...>, int> = 0>
LoopControl invoke_returning_loop_control(Func&& f, Args&&... a) {}

/**
 * Implementations for the runtime function
 */
template <typename Sequence, typename Func>
void for_each_range_impl(index_constant<3>, Sequence&& range, Func& func) {}
template <typename Sequence, typename Func>
void for_each_range_impl(index_constant<2>, Sequence&& range, Func& func) {}

template <typename Sequence, typename Func>
void for_each_range_impl(index_constant<1>, Sequence&& range, Func& func) {}

/**
 * Handlers for iteration
 */
template <typename Sequence, typename Func, std::size_t... Indices>
void for_each_tuple_impl(
    std::index_sequence<Indices...>, Sequence&& seq, Func& func) {}

/**
 * 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 <typename Sequence, typename Func>
void for_each_tuple_impl(index_constant<2>, Sequence&& seq, Func& func) {}
template <typename Sequence, typename Func>
void for_each_tuple_impl(index_constant<1>, Sequence&& seq, Func& func) {}

/**
 * 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 <typename Sequence, typename Func>
void for_each_impl(TupleTag, Sequence&& range, Func& func) {}
template <typename Sequence, typename Func>
void for_each_impl(RangeTag, Sequence&& range, Func& func) {}

template <typename Sequence, typename Index>
decltype(auto) fetch_impl(IndexingTag, Sequence&& sequence, Index&& index) {}
template <typename Sequence, typename Index>
decltype(auto) fetch_impl(BeginAddTag, Sequence&& sequence, Index index) {}

template <typename Sequence, typename Index>
decltype(auto) fetch_impl(TupleTag, Sequence&& sequence, Index index) {}
template <typename Sequence, typename Index>
decltype(auto) fetch_impl(RangeTag, Sequence&& sequence, Index&& index) {}

} // namespace for_each_detail

template <typename Sequence, typename Func>
constexpr Func for_each(Sequence&& sequence, Func func) {}

template <typename Sequence, typename Index>
constexpr decltype(auto) fetch(Sequence&& sequence, Index&& index) {}

} // namespace folly