folly/folly/Traits.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 <cstdint>
#include <functional>
#include <limits>
#include <memory>
#include <tuple>
#include <type_traits>

#include <folly/Portability.h>

namespace folly {

#if defined(__cpp_lib_type_identity) && __cpp_lib_type_identity >= 201806L

using std::type_identity;
using std::type_identity_t;

#else

/// type_identity_t
/// type_identity
///
/// mimic: std::type_identity_t, std::type_identity, c++20
template <typename T>
struct type_identity {};
type_identity_t;

#endif

/// tag_t
/// tag
///
/// A generic type-list value type and value.
///
/// A type-list is a class template parameterized by a pack of types.
template <typename...>
struct tag_t {};
tag;

/// vtag_t
/// vtag
///
/// A generic value-list value type and value.
///
/// A value-list is a class template parameterized by a pack of values.
template <auto...>
struct vtag_t {};
vtag;

index_constant;

/// always_false
///
/// A variable template that is always false but requires template arguments to
/// be provided (which are then ignored). This is useful in very specific cases
/// where we want type-dependent expressions to defer static_assert's.
///
/// A common use-case is for exhaustive constexpr if branches:
///
///   template <typename T>
///   void foo(T value) {
///     if constexpr (std::is_integral_v<T>) foo_integral(value);
///     else if constexpr (std::is_same_v<T, std::string>) foo_string(value);
///     else static_assert(always_false<T>, "Unsupported type");
///   }
///
/// If we had used static_assert(false), then this would always fail to compile,
/// even if foo is never instantiated!
///
/// Another use case is if a template that is expected to always be specialized
/// is erroneously instantiated with the base template.
///
///   template <typename T>
///   struct Foo {
///     static_assert(always_false<T>, "Unsupported type");
///   };
///   template <>
///   struct Foo<int> {};
///
///   Foo<int> a;         // fine
///   Foo<std::string> b; // fails! And you get a nice (custom) error message
///
/// This is similar to leaving the base template undefined but we get a nicer
/// compiler error message with static_assert.
always_false;

namespace detail {

template <typename Void, typename T>
struct require_sizeof_ {};
require_sizeof_<decltype(void(sizeof(T))), T>;

} // namespace detail

/// require_sizeof
///
/// Equivalent to sizeof, but with a static_assert enforcing that application of
/// sizeof would not fail substitution.
require_sizeof;

/// is_unbounded_array_v
/// is_unbounded_array
///
/// A trait variable and type to check if a given type is an unbounded array.
///
/// mimic: std::is_unbounded_array_d, std::is_unbounded_array (C++20)
is_unbounded_array_v;
is_unbounded_array_v;
template <typename T>
struct is_unbounded_array : std::bool_constant<is_unbounded_array_v<T>> {};

/// is_bounded_array_v
/// is_bounded_array
///
/// A trait variable and type to check if a given type is a bounded array.
///
/// mimic: std::is_bounded_array_d, std::is_bounded_array (C++20)
is_bounded_array_v;
is_bounded_array_v;
template <typename T>
struct is_bounded_array : std::bool_constant<is_bounded_array_v<T>> {};

/// is_instantiation_of_v
/// is_instantiation_of
/// instantiated_from
/// uncvref_instantiated_from
///
/// A trait variable and type to check if a given type is an instantiation of a
/// class template. And corresponding concepts.
///
/// Note that this only works with type template parameters. It does not work
/// with non-type template parameters, template template parameters, or alias
/// templates.
is_instantiation_of_v;
is_instantiation_of_v;
template <template <typename...> class C, typename... T>
struct is_instantiation_of
    : std::bool_constant<is_instantiation_of_v<C, T...>> {};

#if defined(__cpp_concepts)

template <typename T, template <typename...> class Templ>
concept instantiated_from = is_instantiation_of_v<Templ, T>;

template <typename T, template <typename...> class Templ>
concept uncvref_instantiated_from =
    is_instantiation_of_v<Templ, std::remove_cvref_t<T>>;

#endif

/// member_pointer_traits
///
/// For a member-pointer, reveals its constituent member-type and object-type.
///
/// Works for both member-object-pointer and member-function-pointer.
template <typename>
struct member_pointer_traits;
member_pointer_traits<M O::*>;

namespace detail {

struct is_constexpr_default_constructible_ {};

} // namespace detail

/// is_constexpr_default_constructible_v
/// is_constexpr_default_constructible
///
/// A trait variable and type which determines whether the type parameter is
/// constexpr default-constructible, that is, default-constructible in a
/// constexpr context.
is_constexpr_default_constructible_v;
template <typename T>
struct is_constexpr_default_constructible
    : std::bool_constant<is_constexpr_default_constructible_v<T>> {};

/***
 *  _t
 *
 *  Instead of:
 *
 *    using decayed = typename std::decay<T>::type;
 *
 *  With the C++14 standard trait aliases, we could use:
 *
 *    using decayed = std::decay_t<T>;
 *
 *  Without them, we could use:
 *
 *    using decayed = _t<std::decay<T>>;
 *
 *  Also useful for any other library with template types having dependent
 *  member types named `type`, like the standard trait types.
 */
_t;

/**
 * A type trait to remove all const volatile and reference qualifiers on a
 * type T
 */
template <typename T>
struct remove_cvref {};
remove_cvref_t;

namespace detail {
template <typename Src>
struct like_ {};
like_<const Src>;
like_<volatile Src>;
like_<const volatile Src>;
like_<Src &>;
like_<Src &&>;
} // namespace detail

//  mimic: like_t, p0847r0
like_t;

//  mimic: like, p0847r0
template <typename Src, typename Dst>
struct like {};

#if defined(__cpp_concepts)

/**
 *  Concept to check that a type is same as a given type,
 *  when stripping qualifiers and refernces.
 *  Especially useful for perfect forwarding of a specific type.
 *
 *  Example:
 *
 *    void foo(folly::uncvref_same_as<std::vector<int>> auto&& vec);
 *
 */
template <typename Ref, typename To>
concept uncvref_same_as = std::is_same_v<std::remove_cvref_t<Ref>, To>;

#endif

/**
 *  type_t
 *
 *  A type alias for the first template type argument. `type_t` is useful for
 *  controlling class-template and function-template partial specialization.
 *
 *  Example:
 *
 *    template <typename Value>
 *    class Container {
 *     public:
 *      template <typename... Args>
 *      Container(
 *          type_t<in_place_t, decltype(Value(std::declval<Args>()...))>,
 *          Args&&...);
 *    };
 *
 *  void_t
 *
 *  A type alias for `void`. `void_t` is useful for controlling class-template
 *  and function-template partial specialization.
 *
 *  Example:
 *
 *    // has_value_type<T>::value is true if T has a nested type `value_type`
 *    template <class T, class = void>
 *    struct has_value_type
 *        : std::false_type {};
 *
 *    template <class T>
 *    struct has_value_type<T, folly::void_t<typename T::value_type>>
 *        : std::true_type {};
 */

/**
 * There is a bug in libstdc++, libc++, and MSVC's STL that causes it to
 * ignore unused template parameter arguments in template aliases and does not
 * cause substitution failures. This defect has been recorded here:
 * http://open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1558.
 *
 * This causes the implementation of std::void_t to be buggy, as it is likely
 * defined as something like the following:
 *
 *  template <typename...>
 *  using void_t = void;
 *
 * This causes the compiler to ignore all the template arguments and does not
 * help when one wants to cause substitution failures.  Rather declarations
 * which have void_t in orthogonal specializations are treated as the same.
 * For example, assuming the possible `T` types are only allowed to have
 * either the alias `one` or `two` and never both or none:
 *
 *  template <typename T,
 *            typename std::void_t<std::decay_t<T>::one>* = nullptr>
 *  void foo(T&&) {}
 *  template <typename T,
 *            typename std::void_t<std::decay_t<T>::two>* = nullptr>
 *  void foo(T&&) {}
 *
 * The second foo() will be a redefinition because it conflicts with the first
 * one; void_t does not cause substitution failures - the template types are
 * just ignored.
 */

namespace traits_detail {
template <class T, class...>
struct type_t_ {};
} // namespace traits_detail

type_t;
void_t;

/// nonesuch
///
/// A tag type which traits may use to indicate lack of a result type.
///
/// Similar to void in that no values of this type may be constructed. Different
/// from void in that no functions may be defined with this return type and no
/// complete expressions may evaluate with this expression type.
///
/// mimic: std::experimental::nonesuch, Library Fundamentals TS v2
struct nonesuch {};

namespace detail {

template <typename Void, typename D, template <typename...> class, typename...>
struct detected_ {};
detected_<void_t<T<A...>>, D, T, A...>;

} // namespace detail

/// detected_or
///
/// If T<A...> substitutes, has member type alias value_t as std::true_type
/// and has member type alias type as T<A...>. Otherwise, has member type
/// alias value_t as std::false_type and has member type alias type as D.
///
/// mimic: std::experimental::detected_or, Library Fundamentals TS v2
///
/// Note: not resilient against incomplete types; may violate ODR.
detected_or;

/// detected_or_t
///
/// A trait type alias which results in T<A...> if substitution would succeed
/// and in D otherwise.
///
/// Equivalent to detected_or<D, T, A...>::type.
///
/// mimic: std::experimental::detected_or_t, Library Fundamentals TS v2
///
/// Note: not resilient against incomplete types; may violate ODR.
detected_or_t;

/// detected_t
///
/// A trait type alias which results in T<A...> if substitution would succeed
/// and in nonesuch otherwise.
///
/// Equivalent to detected_or_t<nonesuch, T, A...>.
///
/// mimic: std::experimental::detected_t, Library Fundamentals TS v2
///
/// Note: not resilient against incomplete types; may violate ODR.
detected_t;

//  is_detected_v
//  is_detected
//
//  A trait variable and type to test whether some metafunction from types to
//  types would succeed or fail in substitution over a given set of arguments.
//
//  The trait variable is_detected_v<T, A...> is equivalent to
//  detected_or<nonesuch, T, A...>::value_t::value.
//  The trait type is_detected<T, A...> unambiguously inherits
//  std::bool_constant<V> where V is is_detected_v<T, A...>.
//
//  mimic: std::experimental::is_detected, std::experimental::is_detected_v,
//    Library Fundamentals TS v2
//
//  Note: not resilient against incomplete types; may violate ODR.
//
//  Note: the trait type is_detected differs here by being deferred.
is_detected_v;
template <template <typename...> class T, typename... A>
struct is_detected : detected_or<nonesuch, T, A...>::value_t {};

aligned_storage_for_t;

//  ----

namespace fallback {
is_nothrow_convertible_v;
template <typename From, typename To>
struct is_nothrow_convertible
    : std::bool_constant<is_nothrow_convertible_v<From, To>> {};
} // namespace fallback

//  is_nothrow_convertible
//  is_nothrow_convertible_v
//
//  Import or backport:
//  * std::is_nothrow_convertible
//  * std::is_nothrow_convertible_v
//
//  mimic: is_nothrow_convertible, C++20
#if defined(__cpp_lib_is_nothrow_convertible) && \
    __cpp_lib_is_nothrow_convertible >= 201806L
using std::is_nothrow_convertible;
using std::is_nothrow_convertible_v;
#else
is_nothrow_convertible;
is_nothrow_convertible_v;
#endif

/**
 * IsRelocatable<T>::value describes the ability of moving around
 * memory a value of type T by using memcpy (as opposed to the
 * conservative approach of calling the copy constructor and then
 * destroying the old temporary. Essentially for a relocatable type,
 * the following two sequences of code should be semantically
 * equivalent:
 *
 * void move1(T * from, T * to) {
 *   new(to) T(from);
 *   (*from).~T();
 * }
 *
 * void move2(T * from, T * to) {
 *   memcpy(to, from, sizeof(T));
 * }
 *
 * Most C++ types are relocatable; the ones that aren't would include
 * internal pointers or (very rarely) would need to update remote
 * pointers to pointers tracking them. All C++ primitive types and
 * type constructors are relocatable.
 *
 * This property can be used in a variety of optimizations. Currently
 * fbvector uses this property intensively.
 *
 * The default conservatively assumes the type is not
 * relocatable. Several specializations are defined for known
 * types. You may want to add your own specializations. Do so in
 * namespace folly and make sure you keep the specialization of
 * IsRelocatable<SomeStruct> in the same header as SomeStruct.
 *
 * You may also declare a type to be relocatable by including
 *    `typedef std::true_type IsRelocatable;`
 * in the class header.
 *
 * It may be unset in a base class by overriding the typedef to false_type.
 */
/*
 * IsZeroInitializable describes the property that value-initialization
 * is the same as memset(dst, 0, sizeof(T)).
 */

namespace traits_detail {

#define FOLLY_HAS_TRUE_XXX

FOLLY_HAS_TRUE_XXX;
FOLLY_HAS_TRUE_XXX;

#undef FOLLY_HAS_TRUE_XXX

} // namespace traits_detail

struct Ignore {};

Ignored;

namespace traits_detail_IsEqualityComparable {
Ignore operator==(Ignore, Ignore);

template <class T, class U = T>
struct IsEqualityComparable
    : std::is_convertible<
          decltype(std::declval<T>() == std::declval<U>()),
          bool> {};
} // namespace traits_detail_IsEqualityComparable

/* using override */ IsEqualityComparable;

namespace traits_detail_IsLessThanComparable {
Ignore operator<(Ignore, Ignore);

template <class T, class U = T>
struct IsLessThanComparable
    : std::is_convertible<
          decltype(std::declval<T>() < std::declval<U>()),
          bool> {};
} // namespace traits_detail_IsLessThanComparable

/* using override */ IsLessThanComparable;

template <class T>
struct IsRelocatable
    : std::conditional<
          !require_sizeof<T> ||
              is_detected_v<traits_detail::detect_IsRelocatable, T>,
          traits_detail::has_true_IsRelocatable<T>,
#if defined(__cpp_lib_is_trivially_relocatable) // P1144
          std::is_trivially_relocatable<T>
#else
          std::is_trivially_copyable<T>
#endif
          >::type {};

template <class T>
struct IsZeroInitializable
    : std::conditional<
          !require_sizeof<T> ||
              is_detected_v<traits_detail::detect_IsZeroInitializable, T>,
          traits_detail::has_true_IsZeroInitializable<T>,
          std::bool_constant< //
              !std::is_class<T>::value && //
              !std::is_union<T>::value && //
              !std::is_member_object_pointer<T>::value && // itanium
              true>>::type {};

namespace detail {
template <bool>
struct conditional_;
template <>
struct conditional_<false> {};
template <>
struct conditional_<true> {};
} // namespace detail

/// conditional_t
///
/// Like std::conditional_t but with only two total class template instances,
/// rather than as many class template instances as there are uses.
///
/// As one effect, the result can be used in deducible contexts, allowing
/// deduction of conditional_t<V, T, F> to work when T or F is a template param.
conditional_t;

template <typename...>
struct Conjunction : std::true_type {};
Conjunction<T>;
Conjunction<T, TList...>;

template <typename...>
struct Disjunction : std::false_type {};
Disjunction<T>;
Disjunction<T, TList...>;

template <typename T>
struct Negation : std::bool_constant<!T::value> {};

template <bool... Bs>
struct Bools {};

//  Lighter-weight than Conjunction, but evaluates all sub-conditions eagerly.
template <class... Ts>
struct StrictConjunction
    : std::is_same<Bools<Ts::value...>, Bools<(Ts::value || true)...>> {};

template <class... Ts>
struct StrictDisjunction
    : Negation<
          std::is_same<Bools<Ts::value...>, Bools<(Ts::value && false)...>>> {};

namespace detail {
is_transparent_;
} // namespace detail

/// is_transparent_v
/// is_transparent
///
/// A trait variable and type to test whether a less, equal-to, or hash type
/// follows the is-transparent protocol used by containers with optional
/// heterogeneous access.
is_transparent_v;
template <typename T>
struct is_transparent : std::bool_constant<is_transparent_v<T>> {};

namespace detail {

is_allocator_;
is_allocator_;

} // namespace detail

/// is_allocator_v
/// is_allocator
///
/// A trait variable and type to test whether a type is an allocator according
/// to the minimum protocol required by std::allocator_traits.
is_allocator_v;
template <typename T>
struct is_allocator : std::bool_constant<is_allocator_v<T>> {};

} // namespace folly

/**
 * Use this macro ONLY inside namespace folly. When using it with a
 * regular type, use it like this:
 *
 * // Make sure you're at namespace ::folly scope
 * template <> FOLLY_ASSUME_RELOCATABLE(MyType)
 *
 * When using it with a template type, use it like this:
 *
 * // Make sure you're at namespace ::folly scope
 * template <class T1, class T2>
 * FOLLY_ASSUME_RELOCATABLE(MyType<T1, T2>)
 */
#define FOLLY_ASSUME_RELOCATABLE(...)

/**
 * The FOLLY_ASSUME_FBVECTOR_COMPATIBLE* macros below encode the
 * assumption that the type is relocatable per IsRelocatable
 * above. Many types can be assumed to satisfy this condition, but
 * it is the responsibility of the user to state that assumption.
 * User-defined classes will not be optimized for use with
 * fbvector (see FBVector.h) unless they state that assumption.
 *
 * Use FOLLY_ASSUME_FBVECTOR_COMPATIBLE with regular types like this:
 *
 * FOLLY_ASSUME_FBVECTOR_COMPATIBLE(MyType)
 *
 * The versions FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1, _2, _3, and _4
 * allow using the macro for describing templatized classes with 1, 2,
 * 3, and 4 template parameters respectively. For template classes
 * just use the macro with the appropriate number and pass the name of
 * the template to it. Example:
 *
 * template <class T1, class T2> class MyType { ... };
 * ...
 * // Make sure you're at global scope
 * FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(MyType)
 */

// Use this macro ONLY at global level (no namespace)
#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE(...)
// Use this macro ONLY at global level (no namespace)
#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1(...)
// Use this macro ONLY at global level (no namespace)
#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2(...)
// Use this macro ONLY at global level (no namespace)
#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_3(...)
// Use this macro ONLY at global level (no namespace)
#define FOLLY_ASSUME_FBVECTOR_COMPATIBLE_4(...)

namespace folly {

// STL commonly-used types
IsRelocatable<std::pair<T, U>>;

// Is T one of T1, T2, ..., Tn?
IsOneOf;

/*
 * Complementary type traits for integral comparisons.
 *
 * For instance, `if(x < 0)` yields an error in clang for unsigned types
 * when -Werror is used due to -Wtautological-compare
 */

// same as `x < 0`
template <typename T>
constexpr bool is_negative(T x) {}

// same as `x <= 0`
template <typename T>
constexpr bool is_non_positive(T x) {}

// same as `x > 0`
template <typename T>
constexpr bool is_positive(T x) {}

// same as `x >= 0`
template <typename T>
constexpr bool is_non_negative(T x) {}

detail // namespace detail

template <typename RHS, RHS rhs, typename LHS>
bool less_than(LHS const lhs) {}

template <typename RHS, RHS rhs, typename LHS>
bool greater_than(LHS const lhs) {}
} // namespace folly

// Assume nothing when compiling with MSVC.
#ifndef _MSC_VER
FOLLY_ASSUME_FBVECTOR_COMPATIBLE_2()
FOLLY_ASSUME_FBVECTOR_COMPATIBLE_1()
#endif

namespace folly {

//  Some compilers have signed __int128 and unsigned __int128 types, and some
//  libraries with some compilers have traits for those types. It's a mess.
//  Import things into folly and then fill in whatever is missing.
//
//  The aliases:
//    int128_t
//    uint128_t
//
//  The traits:
//    is_arithmetic
//    is_arithmetic_v
//    is_integral
//    is_integral_v
//    is_signed
//    is_signed_v
//    is_unsigned
//    is_unsigned_v
//    make_signed
//    make_signed_t
//    make_unsigned
//    make_unsigned_t

template <typename T>
struct is_arithmetic : std::is_arithmetic<T> {};
is_arithmetic_v;

template <typename T>
struct is_integral : std::is_integral<T> {};
is_integral_v;

template <typename T>
struct is_signed : std::is_signed<T> {};
is_signed_v;

template <typename T>
struct is_unsigned : std::is_unsigned<T> {};
is_unsigned_v;

template <typename T>
struct make_signed : std::make_signed<T> {};
make_signed_t;

template <typename T>
struct make_unsigned : std::make_unsigned<T> {};
make_unsigned_t;

#if FOLLY_HAVE_INT128_T

using int128_t = signed __int128;
using uint128_t = unsigned __int128;

template <>
struct is_arithmetic<int128_t> : std::true_type {};
template <>
struct is_arithmetic<uint128_t> : std::true_type {};

template <>
struct is_integral<int128_t> : std::true_type {};
template <>
struct is_integral<uint128_t> : std::true_type {};

template <>
struct is_signed<int128_t> : std::true_type {};
template <>
struct is_signed<uint128_t> : std::false_type {};
template <>
struct is_unsigned<int128_t> : std::false_type {};
template <>
struct is_unsigned<uint128_t> : std::true_type {};

template <>
struct make_signed<int128_t> {
  using type = int128_t;
};
template <>
struct make_signed<uint128_t> {
  using type = int128_t;
};

template <>
struct make_unsigned<int128_t> {
  using type = uint128_t;
};
template <>
struct make_unsigned<uint128_t> {
  using type = uint128_t;
};
#endif // FOLLY_HAVE_INT128_T

namespace traits_detail {
template <std::size_t>
struct uint_bits_t_ {};
template <>
struct uint_bits_t_<8> : type_t_<std::uint8_t> {};
template <>
struct uint_bits_t_<16> : type_t_<std::uint16_t> {};
template <>
struct uint_bits_t_<32> : type_t_<std::uint32_t> {};
template <>
struct uint_bits_t_<64> : type_t_<std::uint64_t> {};
#if FOLLY_HAVE_INT128_T
template <>
struct uint_bits_t_<128> : type_t_<uint128_t> {};
#endif // FOLLY_HAVE_INT128_T
} // namespace traits_detail

uint_bits_t;

uint_bits_lg_t;

int_bits_t;

int_bits_lg_t;

namespace traits_detail {

template <std::size_t I, typename T>
struct type_pack_element_indexed_type {};

template <typename, typename...>
struct type_pack_element_set;
type_pack_element_set<std::index_sequence<I...>, T...>;
type_pack_element_set_t;

template <std::size_t I>
struct type_pack_element_test {};

type_pack_element_fallback;

} // namespace traits_detail

/// type_pack_element_t
///
/// In the type pack Ts..., the Ith element.
///
/// Wraps the builtin __type_pack_element where the builtin is available; where
/// not, implemented directly.
///
/// Under gcc, the builtin is available but does not mangle. Therefore, this
/// trait must not be used anywhere it might be subject to mangling, such as in
/// a return-type expression.

#if FOLLY_HAS_BUILTIN(__type_pack_element)

type_pack_element_t;

#else

template <std::size_t I, typename... Ts>
using type_pack_element_t = traits_detail::type_pack_element_fallback<I, Ts...>;

#endif

/// type_pack_size_v
///
/// The size of a type pack.
///
/// A metafunction around sizeof...(Ts).
type_pack_size_v;

/// type_pack_size_t
///
/// The size of a type pack.
///
/// A metafunction around index_constant<sizeof...(Ts)>.
type_pack_size_t;

namespace traits_detail {

template <std::size_t I, template <typename...> class List, typename... T>
type_identity<type_pack_element_t<I, T...>> type_list_element_(
    List<T...> const*);

template <template <typename...> class List, typename... T>
index_constant<sizeof...(T)> type_list_size_(List<T...> const*);

} // namespace traits_detail

/// type_list_element_t
///
/// In the type list List<T...>, where List has kind template <typename...> and
/// T... is a type-pack, equivalent to type_pack_element_t<I, T...>.
type_list_element_t;

/// type_list_size_v
///
/// The size of a type list.
///
/// For List<T...>, equivalent to type_pack_size_v<T...>.
type_list_size_v;

/// type_list_size_t
///
/// The size of a type list.
///
/// For List<T...>, equivalent to type_pack_size_t<T...>.
type_list_size_t;

namespace traits_detail {

template <decltype(auto) V>
struct value_pack_constant {};

} // namespace traits_detail

/// value_pack_size_v
///
/// The size of a value pack.
///
/// A metafunction around sizeof...(V).
value_pack_size_v;

/// value_pack_size_t
///
/// The size of a value pack.
///
/// A metafunction around index_constant<sizeof...(V)>.
value_pack_size_t;

/// value_pack_element_type_t
///
/// In the value pack V..., the type of the Ith element.
value_pack_element_type_t;

/// value_pack_element_type_t
///
/// In the value pack V..., the Ith element.
value_pack_element_v;

namespace traits_detail {

template <typename List>
struct value_list_traits_;
value_list_traits_<List<V...>>;

} // namespace traits_detail

/// value_list_size_v
///
/// The size of a value list.
///
/// For List<V...>, equivalent to value_pack_size_v<V...>.
value_list_size_v;

/// value_list_size_t
///
/// The size of a value list.
///
/// For List<V...>, equivalent to value_pack_size_t<V...>.
value_list_size_t;

/// value_list_element_type_t
///
/// For List<V...>, the type of the Ith element.
value_list_element_type_t;

/// value_list_element_v
///
/// For List<V...>, the Ith element.
value_list_element_v;

/**
 * Checks the requirements that the Hasher class must satisfy
 * in order to be used with the standard library containers,
 * for example `std::unordered_set<T, Hasher>`.
 */
is_hasher_usable;

/**
 * Checks the requirements that the Hasher class must satisfy
 * in order to be used with the standard library containers,
 * for example `std::unordered_set<T, Hasher>`.
 */
is_hasher_usable_v;

/**
 * Checks that the given hasher template's specialization for the given type
 * is usable with the standard library containters,
 * for example `std::unordered_set<T, Hasher<T>>`.
 */
is_hashable;

/**
 * Checks that the given hasher template's specialization for the given type
 * is usable with the standard library containters,
 * for example `std::unordered_set<T, Hasher<T>>`.
 */
is_hashable_v;

namespace detail {

enable_hasher_helper_impl;

} // namespace detail

/**
 * A helper for defining partial specializations of a hasher class that rely
 * on other partial specializations of that hasher class being usable.
 *
 * Example:
 * ```
 * template <typename T>
 * struct hash<
 *     folly::enable_std_hash_helper<folly::Optional<T>, remove_const_t<T>>> {
 *   size_t operator()(folly::Optional<T> const& obj) const {
 *     return static_cast<bool>(obj) ? hash<remove_const_t<T>>()(*obj) : 0;
 *   }
 * };
 * ```
 */
enable_hasher_helper;

/**
 * A helper for defining partial specializations of a hasher class that rely
 * on other partial specializations of that hasher class being usable.
 *
 * Example:
 * ```
 * template <typename T>
 * struct hash<
 *     folly::enable_std_hash_helper<folly::Optional<T>, remove_const_t<T>>> {
 *   size_t operator()(folly::Optional<T> const& obj) const {
 *     return static_cast<bool>(obj) ? hash<remove_const_t<T>>()(*obj) : 0;
 *   }
 * };
 * ```
 */
enable_std_hash_helper;

} // namespace folly