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

#pragma once

#include <type_traits>
#include <utility>

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

/**
 * folly implementation of `std::overload` like functionality
 *
 * Example:
 *  struct One {};
 *  struct Two {};
 *  boost::variant<One, Two> value;
 *
 *  variant_match(value,
 *    [] (const One& one) { ... },
 *    [] (const Two& two) { ... });
 */

namespace folly {

namespace detail {

// MSVC does not implement noexcept deduction https://godbolt.org/z/Mxdjao1q6
#if !defined(_MSC_VER)
#define FOLLY_DETAIL_NOEXCEPT_SPECIFICATION
#define FOLLY_DETAIL_NOEXCEPT_DECLARATION
#else
#define FOLLY_DETAIL_NOEXCEPT_SPECIFICATION
#define FOLLY_DETAIL_NOEXCEPT_DECLARATION
#endif

template <typename T>
struct FunctionClassType {};

// You cannot derive from a pointer to function, so wrap it in a class

FunctionClassType<Return (*)(Args...) noexcept(Noexcept)>;

// You cannot derive from a pointer to member function, so wrap it in a class.
// This cannot be implemented with
// `std::enable_if_t<std::is_member_pointer_v<T>>` because you don't get
// preferred overload resolution on the object type to match const / ref
// qualifiers.

FunctionClassType<Return (Self::*)(Args...) noexcept(Noexcept)>;

FunctionClassType<Return (Self::*)(Args...) const noexcept(Noexcept)>;

FunctionClassType<Return (Self::*)(Args...) & noexcept(Noexcept)>;

FunctionClassType<Return (Self::*)(Args...) const & noexcept(Noexcept)>;

FunctionClassType<Return (Self::*)(Args...) && noexcept(Noexcept)>;

FunctionClassType<Return (Self::*)(Args...) const && noexcept(Noexcept)>;

FunctionClassType<T Self::*>;

#undef FOLLY_DETAIL_NOEXCEPT_DECLARATION
#undef FOLLY_DETAIL_NOEXCEPT_SPECIFICATION

template <typename...>
struct Overload {};

Overload<Case, Cases...>;

Overload<Case>;
} // namespace detail

/*
 * Combine multiple `Cases` in one function object
 *
 * Each element of `Cases` must be a class type with `operator()`, a pointer to
 * a function, a pointer to a member function, or a pointer to member data.
 * `final` types and pointers to `volatile`-qualified member functions are not
 * supported. If the `Case` type is a pointer to member, the first argument must
 * be a class type or reference to class type (pointer to class type is not
 * supported).
 */
template <typename... Cases>
constexpr decltype(auto) overload(Cases&&... cases) {}

namespace overload_detail {
FOLLY_CREATE_MEMBER_INVOKER();
FOLLY_PUSH_WARNING
FOLLY_MSVC_DISABLE_WARNING() /* not enough arguments to macro */
FOLLY_CREATE_FREE_INVOKER();
FOLLY_CREATE_FREE_INVOKER();
FOLLY_POP_WARNING
} // namespace overload_detail

/*
 * Match `Variant` with one of the `Cases`
 *
 * Note: you can also use `[] (const auto&) {...}` as default case
 *
 * Selects `visit` if `v.valueless_by_exception()` available and the call to
 * `visit` is valid (e.g. `std::variant`). Otherwise, selects `apply_visitor`
 * (e.g. `boost::variant`, `folly::DiscriminatedPtr`).
 */
template <typename Variant, typename... Cases>
decltype(auto) variant_match(Variant&& variant, Cases&&... cases) {}

template <typename R, typename Variant, typename... Cases>
R variant_match(Variant&& variant, Cases&&... cases) {}

} // namespace folly