933 lines
31 KiB
C++
933 lines
31 KiB
C++
/*
|
|
* 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 <limits>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include <folly/CPortability.h>
|
|
#include <folly/Portability.h>
|
|
#include <folly/Traits.h>
|
|
|
|
namespace folly {
|
|
|
|
/*
|
|
* FOLLY_DECLVAL(T)
|
|
*
|
|
* This macro works like std::declval<T>() but does the same thing in a way
|
|
* that does not require instantiating a function template.
|
|
*
|
|
* Use this macro instead of std::declval<T>() in places that are widely
|
|
* instantiated to reduce compile-time overhead of instantiating function
|
|
* templates.
|
|
*
|
|
* Note that, like std::declval<T>(), this macro can only be used in
|
|
* unevaluated contexts.
|
|
*
|
|
* There are some small differences between this macro and std::declval<T>().
|
|
* - This macro results in a value of type 'T' instead of 'T&&'.
|
|
* - This macro requires the type T to be a complete type at the
|
|
* point of use.
|
|
* If this is a problem then use FOLLY_DECLVAL(T&&) instead, or if T might
|
|
* be 'void', then use FOLLY_DECLVAL(std::add_rvalue_reference_t<T>).
|
|
*/
|
|
#define FOLLY_DECLVAL(...) static_cast<__VA_ARGS__ (*)() noexcept>(nullptr)()
|
|
|
|
namespace detail {
|
|
template <typename T>
|
|
T decay_1_(T const volatile&&);
|
|
template <typename T>
|
|
T decay_1_(T const&);
|
|
template <typename T>
|
|
T* decay_1_(T*);
|
|
|
|
template <typename T>
|
|
auto decay_0_(int) -> decltype(detail::decay_1_(FOLLY_DECLVAL(T&&)));
|
|
template <typename T>
|
|
auto decay_0_(short) -> void;
|
|
|
|
template <typename T>
|
|
using decay_t = decltype(detail::decay_0_<T>(0));
|
|
} // namespace detail
|
|
|
|
// decay_t
|
|
//
|
|
// Like std::decay_t but possibly faster to compile.
|
|
//
|
|
// mimic: std::decay_t, C++14
|
|
using detail::decay_t;
|
|
|
|
/**
|
|
* copy
|
|
*
|
|
* Usable when you have a function with two overloads:
|
|
*
|
|
* class MyData;
|
|
* void something(MyData&&);
|
|
* void something(const MyData&);
|
|
*
|
|
* Where the purpose is to make copies and moves explicit without having to
|
|
* spell out the full type names - in this case, for copies, to invoke copy
|
|
* constructors.
|
|
*
|
|
* When the caller wants to pass a copy of an lvalue, the caller may:
|
|
*
|
|
* void foo() {
|
|
* MyData data;
|
|
* something(folly::copy(data)); // explicit copy
|
|
* something(std::move(data)); // explicit move
|
|
* something(data); // const& - neither move nor copy
|
|
* }
|
|
*
|
|
* Note: If passed an rvalue, invokes the move-ctor, not the copy-ctor. This
|
|
* can be used to to force a move, where just using std::move would not:
|
|
*
|
|
* folly::copy(std::move(data)); // force-move, not just a cast to &&
|
|
*
|
|
* Note: The following text appears in the standard:
|
|
*
|
|
* In several places in this Clause the operation //DECAY_COPY(x)// is
|
|
* used. All such uses mean call the function `decay_copy(x)` and use the
|
|
* result, where `decay_copy` is defined as follows:
|
|
*
|
|
* template <class T> decay_t<T> decay_copy(T&& v)
|
|
* { return std::forward<T>(v); }
|
|
*
|
|
* http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf
|
|
* 30.2.6 `decay_copy` [thread.decaycopy].
|
|
*
|
|
* We mimic it, with a `noexcept` specifier for good measure.
|
|
*/
|
|
|
|
template <typename T>
|
|
constexpr detail::decay_t<T> copy(T&& value) noexcept(
|
|
noexcept(detail::decay_t<T>(static_cast<T&&>(value)))) {
|
|
return static_cast<T&&>(value);
|
|
}
|
|
|
|
// mimic: forward_like, p0847r0
|
|
template <typename Src, typename Dst>
|
|
constexpr like_t<Src, Dst>&& forward_like(Dst&& dst) noexcept {
|
|
return std::forward<like_t<Src, Dst>>(static_cast<Dst&&>(dst));
|
|
}
|
|
|
|
/**
|
|
* Initializer lists are a powerful compile time syntax introduced in C++11
|
|
* but due to their often conflicting syntax they are not used by APIs for
|
|
* construction.
|
|
*
|
|
* Further standard conforming compilers *strongly* favor an
|
|
* std::initializer_list overload for construction if one exists. The
|
|
* following is a simple tag used to disambiguate construction with
|
|
* initializer lists and regular uniform initialization.
|
|
*
|
|
* For example consider the following case
|
|
*
|
|
* class Something {
|
|
* public:
|
|
* explicit Something(int);
|
|
* Something(std::initializer_list<int>);
|
|
*
|
|
* operator int();
|
|
* };
|
|
*
|
|
* ...
|
|
* Something something{1}; // SURPRISE!!
|
|
*
|
|
* The last call to instantiate the Something object will go to the
|
|
* initializer_list overload. Which may be surprising to users.
|
|
*
|
|
* If however this tag was used to disambiguate such construction it would be
|
|
* easy for users to see which construction overload their code was referring
|
|
* to. For example
|
|
*
|
|
* class Something {
|
|
* public:
|
|
* explicit Something(int);
|
|
* Something(folly::initlist_construct_t, std::initializer_list<int>);
|
|
*
|
|
* operator int();
|
|
* };
|
|
*
|
|
* ...
|
|
* Something something_one{1}; // not the initializer_list overload
|
|
* Something something_two{folly::initlist_construct, {1}}; // correct
|
|
*/
|
|
struct initlist_construct_t {};
|
|
constexpr initlist_construct_t initlist_construct{};
|
|
|
|
// sorted_unique_t, sorted_unique
|
|
//
|
|
// A generic tag type and value to indicate that some constructor or method
|
|
// accepts a container in which the values are sorted and unique.
|
|
//
|
|
// Example:
|
|
//
|
|
// void takes_numbers(folly::sorted_unique_t, std::vector<int> alist) {
|
|
// assert(std::is_sorted(alist.begin(), alist.end()));
|
|
// assert(std::unique(alist.begin(), alist.end()) == alist.end());
|
|
// for (i : alist) {
|
|
// // some behavior which safe only when alist is sorted and unique
|
|
// }
|
|
// }
|
|
// void takes_numbers(std::vector<int> alist) {
|
|
// std::sort(alist.begin(), alist.end());
|
|
// alist.erase(std::unique(alist.begin(), alist.end()), alist.end());
|
|
// takes_numbers(folly::sorted_unique, alist);
|
|
// }
|
|
//
|
|
// mimic: std::sorted_unique_t, std::sorted_unique, p0429r6
|
|
struct sorted_unique_t {};
|
|
constexpr sorted_unique_t sorted_unique{};
|
|
|
|
// sorted_equivalent_t, sorted_equivalent
|
|
//
|
|
// A generic tag type and value to indicate that some constructor or method
|
|
// accepts a container in which the values are sorted but not necessarily
|
|
// unique.
|
|
//
|
|
// Example:
|
|
//
|
|
// void takes_numbers(folly::sorted_equivalent_t, std::vector<int> alist) {
|
|
// assert(std::is_sorted(alist.begin(), alist.end()));
|
|
// for (i : alist) {
|
|
// // some behavior which safe only when alist is sorted
|
|
// }
|
|
// }
|
|
// void takes_numbers(std::vector<int> alist) {
|
|
// std::sort(alist.begin(), alist.end());
|
|
// takes_numbers(folly::sorted_equivalent, alist);
|
|
// }
|
|
//
|
|
// mimic: std::sorted_equivalent_t, std::sorted_equivalent, p0429r6
|
|
struct sorted_equivalent_t {};
|
|
constexpr sorted_equivalent_t sorted_equivalent{};
|
|
|
|
template <typename T>
|
|
struct transparent : T {
|
|
using is_transparent = void;
|
|
using T::T;
|
|
};
|
|
|
|
/**
|
|
* A simple function object that passes its argument through unchanged.
|
|
*
|
|
* Example:
|
|
*
|
|
* int i = 42;
|
|
* int &j = identity(i);
|
|
* assert(&i == &j);
|
|
*
|
|
* Warning: passing a prvalue through identity turns it into an xvalue,
|
|
* which can effect whether lifetime extension occurs or not. For instance:
|
|
*
|
|
* auto&& x = std::make_unique<int>(42);
|
|
* cout << *x ; // OK, x refers to a valid unique_ptr.
|
|
*
|
|
* auto&& y = identity(std::make_unique<int>(42));
|
|
* cout << *y ; // ERROR: y did not lifetime-extend the unique_ptr. It
|
|
* // is no longer valid
|
|
*/
|
|
struct identity_fn {
|
|
template <class T>
|
|
constexpr T&& operator()(T&& x) const noexcept {
|
|
return static_cast<T&&>(x);
|
|
}
|
|
};
|
|
using Identity = identity_fn;
|
|
inline constexpr identity_fn identity{};
|
|
|
|
/// literal_string
|
|
///
|
|
/// A structural type representing a literal string. A structural type may be
|
|
/// a non-type template argument.
|
|
///
|
|
/// May at times be useful since language-level literal strings are not allowed
|
|
/// as non-type template arguments.
|
|
///
|
|
/// This may typically be used with vtag for passing the literal string as a
|
|
/// constant-expression via a non-type template argument.
|
|
///
|
|
/// Example:
|
|
///
|
|
/// template <size_t N, literal_string<char, N> Str>
|
|
/// void do_something_with_literal_string(vtag_t<Str>);
|
|
///
|
|
/// void do_something() {
|
|
/// do_something_with_literal_string(vtag<literal_string{"foobar"}>);
|
|
/// }
|
|
template <typename C, std::size_t N>
|
|
struct literal_string {
|
|
C buffer[N] = {};
|
|
|
|
FOLLY_CONSTEVAL /* implicit */ literal_string(C const (&buf)[N]) noexcept {
|
|
for (std::size_t i = 0; i < N; ++i) {
|
|
buffer[i] = buf[i];
|
|
}
|
|
}
|
|
|
|
constexpr std::size_t size() const noexcept { return N - 1; }
|
|
constexpr C const* data() const noexcept { return buffer; }
|
|
constexpr C const* c_str() const noexcept { return buffer; }
|
|
|
|
template <
|
|
typename String,
|
|
decltype((void(String(FOLLY_DECLVAL(C const*), N - 1)), 0)) = 0>
|
|
constexpr explicit operator String() const //
|
|
noexcept(noexcept(String(FOLLY_DECLVAL(C const*), N - 1))) {
|
|
return String(data(), N - 1);
|
|
}
|
|
};
|
|
|
|
inline namespace literals {
|
|
inline namespace string_literals {
|
|
|
|
#if FOLLY_CPLUSPLUS >= 202002 && !defined(__NVCC__)
|
|
template <literal_string Str>
|
|
FOLLY_CONSTEVAL decltype(Str) operator""_lit() noexcept {
|
|
return Str;
|
|
}
|
|
template <literal_string Str>
|
|
FOLLY_CONSTEVAL vtag_t<Str> operator""_litv() noexcept {
|
|
return vtag<Str>;
|
|
}
|
|
#endif
|
|
|
|
} // namespace string_literals
|
|
} // namespace literals
|
|
|
|
namespace detail {
|
|
|
|
template <typename T>
|
|
struct inheritable_inherit_ : T {
|
|
using T::T;
|
|
template <
|
|
typename... A,
|
|
std::enable_if_t<std::is_constructible<T, A...>::value, int> = 0>
|
|
/* implicit */ FOLLY_ERASE inheritable_inherit_(A&&... a) noexcept(
|
|
noexcept(T(static_cast<A&&>(a)...)))
|
|
: T(static_cast<A&&>(a)...) {}
|
|
};
|
|
|
|
template <typename T>
|
|
struct inheritable_contain_ {
|
|
T v;
|
|
template <
|
|
typename... A,
|
|
std::enable_if_t<std::is_constructible<T, A...>::value, int> = 0>
|
|
/* implicit */ FOLLY_ERASE inheritable_contain_(A&&... a) noexcept(
|
|
noexcept(T(static_cast<A&&>(a)...)))
|
|
: v(static_cast<A&&>(a)...) {}
|
|
FOLLY_ERASE operator T&() & noexcept { return v; }
|
|
FOLLY_ERASE operator T&&() && noexcept { return static_cast<T&&>(v); }
|
|
FOLLY_ERASE operator T const&() const& noexcept { return v; }
|
|
FOLLY_ERASE operator T const&&() const&& noexcept {
|
|
return static_cast<T const&&>(v);
|
|
}
|
|
};
|
|
|
|
template <bool>
|
|
struct inheritable_;
|
|
template <>
|
|
struct inheritable_<false> {
|
|
template <typename T>
|
|
using apply = inheritable_inherit_<T>;
|
|
};
|
|
template <>
|
|
struct inheritable_<true> {
|
|
template <typename T>
|
|
using apply = inheritable_contain_<T>;
|
|
};
|
|
|
|
// inheritable
|
|
//
|
|
// A class wrapping an arbitrary type T which is always inheritable, and which
|
|
// enables empty-base-optimization when possible.
|
|
template <typename T>
|
|
using inheritable =
|
|
typename inheritable_<std::is_final<T>::value>::template apply<T>;
|
|
|
|
} // namespace detail
|
|
|
|
// Prevent child classes from finding anything in folly:: by ADL.
|
|
namespace moveonly_ {
|
|
|
|
/**
|
|
* Disallow copy but not move in derived types. This is essentially
|
|
* boost::noncopyable (the implementation is almost identical), except:
|
|
* 1) It doesn't delete move constructor and move assignment.
|
|
* 2) It has public methods, enabling aggregate initialization.
|
|
*/
|
|
struct MoveOnly {
|
|
constexpr MoveOnly() noexcept = default;
|
|
~MoveOnly() noexcept = default;
|
|
|
|
MoveOnly(MoveOnly&&) noexcept = default;
|
|
MoveOnly& operator=(MoveOnly&&) noexcept = default;
|
|
MoveOnly(const MoveOnly&) = delete;
|
|
MoveOnly& operator=(const MoveOnly&) = delete;
|
|
};
|
|
|
|
/**
|
|
* Disallow copy and move for derived types. This is essentially
|
|
* boost::noncopyable (the implementation is almost identical), except it has
|
|
* public methods, enabling aggregate initialization.
|
|
*/
|
|
struct NonCopyableNonMovable {
|
|
constexpr NonCopyableNonMovable() noexcept = default;
|
|
~NonCopyableNonMovable() noexcept = default;
|
|
|
|
NonCopyableNonMovable(NonCopyableNonMovable&&) = delete;
|
|
NonCopyableNonMovable& operator=(NonCopyableNonMovable&&) = delete;
|
|
NonCopyableNonMovable(const NonCopyableNonMovable&) = delete;
|
|
NonCopyableNonMovable& operator=(const NonCopyableNonMovable&) = delete;
|
|
};
|
|
|
|
struct Default {};
|
|
|
|
template <bool Copy, bool Move>
|
|
using EnableCopyMove = std::conditional_t<
|
|
Copy,
|
|
Default,
|
|
std::conditional_t<Move, MoveOnly, NonCopyableNonMovable>>;
|
|
|
|
} // namespace moveonly_
|
|
|
|
using moveonly_::MoveOnly;
|
|
using moveonly_::NonCopyableNonMovable;
|
|
|
|
/// variadic_noop
|
|
/// variadic_noop_fn
|
|
///
|
|
/// An invocable object and type that has no side-effects - that does nothing
|
|
/// when invoked regardless of the arguments with which it is invoked - and that
|
|
/// returns void.
|
|
///
|
|
/// May be invoked with any arguments. Returns void.
|
|
struct variadic_noop_fn {
|
|
template <typename... A>
|
|
constexpr void operator()(A&&...) const noexcept {}
|
|
};
|
|
inline constexpr variadic_noop_fn variadic_noop;
|
|
|
|
/// variadic_constant_of
|
|
/// variadic_constant_of_fn
|
|
///
|
|
/// An invocable object and type that has no side-effects - that does nothing
|
|
/// when invoked regardless of the arguments with which it is invoked - and that
|
|
/// returns a constant value.
|
|
template <auto Value>
|
|
struct variadic_constant_of_fn {
|
|
using value_type = decltype(Value);
|
|
static inline constexpr value_type value = Value;
|
|
template <typename... A>
|
|
constexpr value_type operator()(A&&...) const noexcept {
|
|
return value;
|
|
}
|
|
};
|
|
template <auto Value>
|
|
inline constexpr variadic_constant_of_fn<Value> variadic_constant_of;
|
|
|
|
// unsafe_default_initialized
|
|
// unsafe_default_initialized_cv
|
|
//
|
|
// An object which is explicitly convertible to any default-constructible type
|
|
// and which, upon conversion, yields a default-initialized value of that type.
|
|
//
|
|
// https://en.cppreference.com/w/cpp/language/default_initialization
|
|
//
|
|
// For fundamental types, a default-initialized instance may have indeterminate
|
|
// value. Reading an indeterminate value is undefined behavior but may offer a
|
|
// performance optimization. When using an indeterminate value as a performance
|
|
// optimization, it is best to be explicit.
|
|
//
|
|
// Useful as an escape hatch when enabling warnings or errors:
|
|
// * gcc:
|
|
// * uninitialized
|
|
// * maybe-uninitialized
|
|
// * clang:
|
|
// * uninitialized
|
|
// * conditional-uninitialized
|
|
// * sometimes-uninitialized
|
|
// * uninitialized-const-reference
|
|
// * msvc:
|
|
// * C4701: potentially uninitialized local variable used
|
|
// * C4703: potentially uninitialized local pointer variable used
|
|
//
|
|
// Example:
|
|
//
|
|
// int local = folly::unsafe_default_initialized;
|
|
// store_value_into_int_ptr(&value); // suppresses possible warning
|
|
// use_value(value); // suppresses possible warning
|
|
struct unsafe_default_initialized_cv {
|
|
FOLLY_PUSH_WARNING
|
|
// MSVC requires warning disables to be outside of function definition
|
|
// Uninitialized local variable 'uninit' used
|
|
FOLLY_MSVC_DISABLE_WARNING(4700)
|
|
// Potentially uninitialized local variable 'uninit' used
|
|
FOLLY_MSVC_DISABLE_WARNING(4701)
|
|
// Potentially uninitialized local pointer variable 'uninit' used
|
|
FOLLY_MSVC_DISABLE_WARNING(4703)
|
|
FOLLY_GNU_DISABLE_WARNING("-Wuninitialized")
|
|
// Clang doesn't implement -Wmaybe-uninitialized and warns about it
|
|
FOLLY_GCC_DISABLE_WARNING("-Wmaybe-uninitialized")
|
|
template <typename T>
|
|
FOLLY_ERASE constexpr /* implicit */ operator T() const noexcept {
|
|
#if defined(__cpp_lib_is_constant_evaluated)
|
|
#if __cpp_lib_is_constant_evaluated >= 201811L
|
|
#if (defined(_MSC_VER) && !defined(__MSVC_RUNTIME_CHECKS)) || \
|
|
(defined(__clang__) && !defined(__GNUC__))
|
|
if (!std::is_constant_evaluated()) {
|
|
T uninit;
|
|
return uninit;
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
return T();
|
|
}
|
|
FOLLY_POP_WARNING
|
|
};
|
|
inline constexpr unsafe_default_initialized_cv unsafe_default_initialized{};
|
|
|
|
/// to_bool
|
|
/// to_bool_fn
|
|
///
|
|
/// Constructs a boolean from the argument.
|
|
///
|
|
/// Particularly useful for testing sometimes-weak function declarations. They
|
|
/// may be declared weak on some platforms but not on others. GCC likes to warn
|
|
/// about them but the warning is unhelpful.
|
|
struct to_bool_fn {
|
|
template <typename..., typename T>
|
|
FOLLY_ERASE constexpr auto operator()(T const& t) const noexcept
|
|
-> decltype(static_cast<bool>(t)) {
|
|
FOLLY_PUSH_WARNING
|
|
FOLLY_GCC_DISABLE_WARNING("-Waddress")
|
|
FOLLY_GCC_DISABLE_WARNING("-Wnonnull-compare")
|
|
return static_cast<bool>(t);
|
|
FOLLY_POP_WARNING
|
|
}
|
|
};
|
|
inline constexpr to_bool_fn to_bool{};
|
|
|
|
struct to_signed_fn {
|
|
template <typename..., typename T>
|
|
constexpr auto operator()(T const& t) const noexcept ->
|
|
typename std::make_signed<T>::type {
|
|
using S = typename std::make_signed<T>::type;
|
|
// note: static_cast<S>(t) would be more straightforward, but it would also
|
|
// be implementation-defined behavior and that is typically to be avoided;
|
|
// the following code optimized into the same thing, though
|
|
constexpr auto m = static_cast<T>(std::numeric_limits<S>::max());
|
|
return m < t ? -static_cast<S>(~t) + S{-1} : static_cast<S>(t);
|
|
}
|
|
};
|
|
inline constexpr to_signed_fn to_signed{};
|
|
|
|
struct to_unsigned_fn {
|
|
template <typename..., typename T>
|
|
constexpr auto operator()(T const& t) const noexcept ->
|
|
typename std::make_unsigned<T>::type {
|
|
using U = typename std::make_unsigned<T>::type;
|
|
return static_cast<U>(t);
|
|
}
|
|
};
|
|
inline constexpr to_unsigned_fn to_unsigned{};
|
|
|
|
namespace detail {
|
|
template <typename Src, typename Dst>
|
|
inline constexpr bool is_to_narrow_convertible_v =
|
|
(std::is_integral<Dst>::value) &&
|
|
(std::is_signed<Dst>::value == std::is_signed<Src>::value);
|
|
}
|
|
|
|
template <typename Src>
|
|
class to_narrow_convertible {
|
|
static_assert(std::is_integral<Src>::value, "not an integer");
|
|
|
|
template <typename Dst>
|
|
struct to_
|
|
: std::bool_constant<detail::is_to_narrow_convertible_v<Src, Dst>> {};
|
|
|
|
public:
|
|
explicit constexpr to_narrow_convertible(Src const& value) noexcept
|
|
: value_(value) {}
|
|
explicit to_narrow_convertible(to_narrow_convertible const&) = default;
|
|
explicit to_narrow_convertible(to_narrow_convertible&&) = default;
|
|
to_narrow_convertible& operator=(to_narrow_convertible const&) = default;
|
|
to_narrow_convertible& operator=(to_narrow_convertible&&) = default;
|
|
|
|
template <typename Dst, std::enable_if_t<to_<Dst>::value, int> = 0>
|
|
/* implicit */ constexpr operator Dst() const noexcept {
|
|
FOLLY_PUSH_WARNING
|
|
FOLLY_MSVC_DISABLE_WARNING(4244) // lossy conversion: arguments
|
|
FOLLY_MSVC_DISABLE_WARNING(4267) // lossy conversion: variables
|
|
FOLLY_GNU_DISABLE_WARNING("-Wconversion")
|
|
return value_;
|
|
FOLLY_POP_WARNING
|
|
}
|
|
|
|
private:
|
|
Src value_;
|
|
};
|
|
|
|
// to_narrow
|
|
//
|
|
// A utility for performing explicit possibly-narrowing integral conversion
|
|
// without specifying the destination type. Does not permit changing signs.
|
|
// Sometimes preferable to static_cast<Dst>(src) to document the intended
|
|
// semantics of the cast.
|
|
//
|
|
// Models explicit conversion with an elided destination type. Sits in between
|
|
// a stricter explicit conversion with a named destination type and a more
|
|
// lenient implicit conversion. Implemented with implicit conversion in order
|
|
// to take advantage of the undefined-behavior sanitizer's inspection of all
|
|
// implicit conversions - it checks for truncation, with suppressions in place
|
|
// for warnings which guard against narrowing implicit conversions.
|
|
struct to_narrow_fn {
|
|
template <typename..., typename Src>
|
|
constexpr auto operator()(Src const& src) const noexcept
|
|
-> to_narrow_convertible<Src> {
|
|
return to_narrow_convertible<Src>{src};
|
|
}
|
|
};
|
|
inline constexpr to_narrow_fn to_narrow{};
|
|
|
|
template <typename Src>
|
|
class to_integral_convertible {
|
|
static_assert(std::is_floating_point<Src>::value, "not a floating-point");
|
|
|
|
template <typename Dst>
|
|
static constexpr bool to_ = std::is_integral<Dst>::value;
|
|
|
|
public:
|
|
explicit constexpr to_integral_convertible(Src const& value) noexcept
|
|
: value_(value) {}
|
|
|
|
explicit to_integral_convertible(to_integral_convertible const&) = default;
|
|
explicit to_integral_convertible(to_integral_convertible&&) = default;
|
|
to_integral_convertible& operator=(to_integral_convertible const&) = default;
|
|
to_integral_convertible& operator=(to_integral_convertible&&) = default;
|
|
|
|
template <typename Dst, std::enable_if_t<to_<Dst>, int> = 0>
|
|
/* implicit */ constexpr operator Dst() const noexcept {
|
|
FOLLY_PUSH_WARNING
|
|
FOLLY_MSVC_DISABLE_WARNING(4244) // lossy conversion: arguments
|
|
FOLLY_MSVC_DISABLE_WARNING(4267) // lossy conversion: variables
|
|
FOLLY_GNU_DISABLE_WARNING("-Wconversion")
|
|
return value_;
|
|
FOLLY_POP_WARNING
|
|
}
|
|
|
|
private:
|
|
Src value_;
|
|
};
|
|
|
|
// to_integral
|
|
//
|
|
// A utility for performing explicit floating-point-to-integral conversion
|
|
// without specifying the destination type. Sometimes preferable to
|
|
// static_cast<Dst>(src) to document the intended semantics of the cast.
|
|
//
|
|
// Models explicit conversion with an elided destination type. Sits in between
|
|
// a stricter explicit conversion with a named destination type and a more
|
|
// lenient implicit conversion. Implemented with implicit conversion in order
|
|
// to take advantage of the undefined-behavior sanitizer's inspection of all
|
|
// implicit conversions.
|
|
struct to_integral_fn {
|
|
template <typename..., typename Src>
|
|
constexpr auto operator()(Src const& src) const noexcept
|
|
-> to_integral_convertible<Src> {
|
|
return to_integral_convertible<Src>{src};
|
|
}
|
|
};
|
|
inline constexpr to_integral_fn to_integral{};
|
|
|
|
template <typename Src>
|
|
class to_floating_point_convertible {
|
|
static_assert(std::is_integral<Src>::value, "not a floating-point");
|
|
|
|
template <typename Dst>
|
|
static constexpr bool to_ = std::is_floating_point<Dst>::value;
|
|
|
|
public:
|
|
explicit constexpr to_floating_point_convertible(Src const& value) noexcept
|
|
: value_(value) {}
|
|
|
|
explicit to_floating_point_convertible(to_floating_point_convertible const&) =
|
|
default;
|
|
explicit to_floating_point_convertible(to_floating_point_convertible&&) =
|
|
default;
|
|
to_floating_point_convertible& operator=(
|
|
to_floating_point_convertible const&) = default;
|
|
to_floating_point_convertible& operator=(to_floating_point_convertible&&) =
|
|
default;
|
|
|
|
template <typename Dst, std::enable_if_t<to_<Dst>, int> = 0>
|
|
/* implicit */ constexpr operator Dst() const noexcept {
|
|
FOLLY_PUSH_WARNING
|
|
FOLLY_GNU_DISABLE_WARNING("-Wconversion")
|
|
return value_;
|
|
FOLLY_POP_WARNING
|
|
}
|
|
|
|
private:
|
|
Src value_;
|
|
};
|
|
|
|
// to_floating_point
|
|
//
|
|
// A utility for performing explicit integral-to-floating-point conversion
|
|
// without specifying the destination type. Sometimes preferable to
|
|
// static_cast<Dst>(src) to document the intended semantics of the cast.
|
|
//
|
|
// Models explicit conversion with an elided destination type. Sits in between
|
|
// a stricter explicit conversion with a named destination type and a more
|
|
// lenient implicit conversion. Implemented with implicit conversion in order
|
|
// to take advantage of the undefined-behavior sanitizer's inspection of all
|
|
// implicit conversions.
|
|
struct to_floating_point_fn {
|
|
template <typename..., typename Src>
|
|
constexpr auto operator()(Src const& src) const noexcept
|
|
-> to_floating_point_convertible<Src> {
|
|
return to_floating_point_convertible<Src>{src};
|
|
}
|
|
};
|
|
inline constexpr to_floating_point_fn to_floating_point{};
|
|
|
|
struct to_underlying_fn {
|
|
template <typename..., class E>
|
|
constexpr std::underlying_type_t<E> operator()(E e) const noexcept {
|
|
static_assert(std::is_enum<E>::value, "not an enum type");
|
|
return static_cast<std::underlying_type_t<E>>(e);
|
|
}
|
|
};
|
|
inline constexpr to_underlying_fn to_underlying{};
|
|
|
|
namespace detail {
|
|
template <typename R>
|
|
using invocable_to_detect = decltype(FOLLY_DECLVAL(R)());
|
|
|
|
template <
|
|
typename F,
|
|
// MSVC 14.16.27023 does not permit these to be in the class body:
|
|
// error C2833: 'operator decltype' is not a recognized operator or type
|
|
// TODO: return these to the class body and remove the static assertions
|
|
typename TML = detected_t<invocable_to_detect, F&>,
|
|
typename TCL = detected_t<invocable_to_detect, F const&>,
|
|
typename TMR = detected_t<invocable_to_detect, F&&>,
|
|
typename TCR = detected_t<invocable_to_detect, F const&&>>
|
|
class invocable_to_convertible : private inheritable<F> {
|
|
private:
|
|
static_assert(std::is_same<F, decay_t<F>>::value, "mismatch");
|
|
|
|
template <typename R>
|
|
using result_t = detected_t<invocable_to_detect, R>;
|
|
template <typename R>
|
|
static constexpr bool detected_v = is_detected_v<invocable_to_detect, R>;
|
|
template <typename R>
|
|
using if_invocable_as_v = std::enable_if_t<detected_v<R>, int>;
|
|
template <typename R>
|
|
static constexpr bool nx_v = noexcept(FOLLY_DECLVAL(R)());
|
|
template <typename G>
|
|
static constexpr bool constructible_v = std::is_constructible<F, G&&>::value;
|
|
|
|
using FML = F&;
|
|
using FCL = F const&;
|
|
using FMR = F&&;
|
|
using FCR = F const&&;
|
|
static_assert(std::is_same<TML, result_t<FML>>::value, "mismatch");
|
|
static_assert(std::is_same<TCL, result_t<FCL>>::value, "mismatch");
|
|
static_assert(std::is_same<TMR, result_t<FMR>>::value, "mismatch");
|
|
static_assert(std::is_same<TCR, result_t<FCR>>::value, "mismatch");
|
|
|
|
public:
|
|
template <typename G, std::enable_if_t<constructible_v<G&&>, int> = 0>
|
|
FOLLY_ERASE explicit constexpr invocable_to_convertible(G&& g) noexcept(
|
|
noexcept(F(static_cast<G&&>(g))))
|
|
: inheritable<F>(static_cast<G&&>(g)) {}
|
|
|
|
template <typename..., typename R = FML, if_invocable_as_v<R> = 0>
|
|
FOLLY_ERASE constexpr operator TML() & noexcept(nx_v<R>) {
|
|
return static_cast<FML>(*this)();
|
|
}
|
|
template <typename..., typename R = FCL, if_invocable_as_v<R> = 0>
|
|
FOLLY_ERASE constexpr operator TCL() const& noexcept(nx_v<R>) {
|
|
return static_cast<FCL>(*this)();
|
|
}
|
|
template <typename..., typename R = FMR, if_invocable_as_v<R> = 0>
|
|
FOLLY_ERASE constexpr operator TMR() && noexcept(nx_v<R>) {
|
|
return static_cast<FMR>(*this)();
|
|
}
|
|
template <typename..., typename R = FCR, if_invocable_as_v<R> = 0>
|
|
FOLLY_ERASE constexpr operator TCR() const&& noexcept(nx_v<R>) {
|
|
return static_cast<FCR>(*this)();
|
|
}
|
|
};
|
|
} // namespace detail
|
|
|
|
// invocable_to
|
|
// invocable_to_fn
|
|
//
|
|
// Given an invocable, returns an object which is implicitly convertible to the
|
|
// type which the invocable returns when invoked with no arguments. Conversion
|
|
// invokes the invocables and returns the value.
|
|
//
|
|
// The return object has unspecified type with the following semantics:
|
|
// * It stores a decay-copy of the passed invocable.
|
|
// * It defines four-way conversion operators. Each conversion operator purely
|
|
// forwards to the invocable as forwarded-like the convertible, and has the
|
|
// same exception specification and the same participation in overload
|
|
// resolution as invocation of the invocable.
|
|
//
|
|
// Example:
|
|
//
|
|
// Given a setup:
|
|
//
|
|
// struct stable {
|
|
// int value = 0;
|
|
// stable() = default;
|
|
// stable(stable const&); // expensive!
|
|
// };
|
|
// std::list<stable const> list;
|
|
//
|
|
// The goal is to insert a stable with a value of 7 to the back of the list.
|
|
//
|
|
// The obvious ways are expensive:
|
|
//
|
|
// stable obj;
|
|
// obj.value = 7;
|
|
// list.push_back(obj); // or variations with emplace_back or std::move
|
|
//
|
|
// With a lambda and copy elision optimization (NRVO), the expense remains:
|
|
//
|
|
// list.push_back(std::invoke([] {
|
|
// stable obj;
|
|
// obj.value = 7;
|
|
// return obj;
|
|
// }));
|
|
//
|
|
// But conversion, as done with this utility, makes this goal achievable.
|
|
//
|
|
// list.emplace_back(folly::invocable_to([] {
|
|
// stable obj;
|
|
// obj.value = 7;
|
|
// return obj;
|
|
// }));
|
|
struct invocable_to_fn {
|
|
template <
|
|
typename F,
|
|
typename...,
|
|
typename D = detail::decay_t<F>,
|
|
typename R = detail::invocable_to_convertible<D>,
|
|
std::enable_if_t<std::is_constructible<D, F&&>::value, int> = 0>
|
|
FOLLY_ERASE constexpr R operator()(F&& f) const
|
|
noexcept(noexcept(R(static_cast<F&&>(f)))) {
|
|
return R(static_cast<F&&>(f));
|
|
}
|
|
};
|
|
inline constexpr invocable_to_fn invocable_to{};
|
|
|
|
#define FOLLY_DETAIL_FORWARD_BODY(...) \
|
|
noexcept(noexcept(__VA_ARGS__))->decltype(__VA_ARGS__) { \
|
|
return __VA_ARGS__; \
|
|
}
|
|
|
|
/// FOLLY_FOR_EACH_THIS_OVERLOAD_IN_CLASS_BODY_DELEGATE
|
|
///
|
|
/// Helper macro to add 4 delegated, qualifier-overloaded methods to a class
|
|
///
|
|
/// Example:
|
|
///
|
|
/// template <typename T>
|
|
/// class optional {
|
|
/// public:
|
|
/// bool has_value() const;
|
|
///
|
|
/// T& value() & {
|
|
/// if (!has_value()) { throw std::bad_optional_access(); }
|
|
/// return m_value;
|
|
/// }
|
|
///
|
|
/// const T& value() const& {
|
|
/// if (!has_value()) { throw std::bad_optional_access(); }
|
|
/// return m_value;
|
|
/// }
|
|
///
|
|
/// T&& value() && {
|
|
/// if (!has_value()) { throw std::bad_optional_access(); }
|
|
/// return std::move(m_value);
|
|
/// }
|
|
///
|
|
/// const T&& value() const&& {
|
|
/// if (!has_value()) { throw std::bad_optional_access(); }
|
|
/// return std::move(m_value);
|
|
/// }
|
|
/// };
|
|
///
|
|
/// This is equivalent to
|
|
///
|
|
/// template <typename T>
|
|
/// class optional {
|
|
/// template <typename Self>
|
|
/// decltype(auto) value_impl(Self&& self) {
|
|
/// if (!self.has_value()) {
|
|
/// throw std::bad_optional_access();
|
|
/// }
|
|
/// return std::forward<Self>(self).m_value;
|
|
/// }
|
|
/// // ...
|
|
///
|
|
/// public:
|
|
/// bool has_value() const;
|
|
///
|
|
/// FOLLY_FOR_EACH_THIS_OVERLOAD_IN_CLASS_BODY_DELEGATE(value,
|
|
/// value_impl);
|
|
/// };
|
|
///
|
|
/// Note: This can be migrated to C++23's deducing this:
|
|
/// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html
|
|
///
|
|
// clang-format off
|
|
#define FOLLY_FOR_EACH_THIS_OVERLOAD_IN_CLASS_BODY_DELEGATE(MEMBER, DELEGATE) \
|
|
template <class... Args> \
|
|
[[maybe_unused]] FOLLY_ERASE_HACK_GCC \
|
|
constexpr auto MEMBER(Args&&... args) & FOLLY_DETAIL_FORWARD_BODY( \
|
|
::folly::remove_cvref_t<decltype(*this)>::DELEGATE( \
|
|
*this, static_cast<Args&&>(args)...)) \
|
|
template <class... Args> \
|
|
[[maybe_unused]] FOLLY_ERASE_HACK_GCC \
|
|
constexpr auto MEMBER(Args&&... args) const& FOLLY_DETAIL_FORWARD_BODY( \
|
|
::folly::remove_cvref_t<decltype(*this)>::DELEGATE( \
|
|
*this, static_cast<Args&&>(args)...)) \
|
|
template <class... Args> \
|
|
[[maybe_unused]] FOLLY_ERASE_HACK_GCC \
|
|
constexpr auto MEMBER(Args&&... args) && FOLLY_DETAIL_FORWARD_BODY( \
|
|
::folly::remove_cvref_t<decltype(*this)>::DELEGATE( \
|
|
std::move(*this), static_cast<Args&&>(args)...)) \
|
|
template <class... Args> \
|
|
[[maybe_unused]] FOLLY_ERASE_HACK_GCC \
|
|
constexpr auto MEMBER(Args&&... args) const&& FOLLY_DETAIL_FORWARD_BODY( \
|
|
::folly::remove_cvref_t<decltype(*this)>::DELEGATE( \
|
|
std::move(*this), static_cast<Args&&>(args)...)) \
|
|
/* enforce semicolon after macro */ static_assert(true)
|
|
// clang-format on
|
|
} // namespace folly
|