0

bind_internal.h cleanup: Use concepts and constexprs more.

Bug: none
Change-Id: I7c840dbe6b405eefc5fc32a72cbaf1d3c7a61e09
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5113260
Code-Coverage: findit-for-me@appspot.gserviceaccount.com <findit-for-me@appspot.gserviceaccount.com>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Peter Kasting <pkasting@chromium.org>
Owners-Override: Lei Zhang <thestig@chromium.org>
Auto-Submit: Peter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1238401}
This commit is contained in:
Peter Kasting
2023-12-16 08:40:00 +00:00
committed by Chromium LUCI CQ
parent f1ed67946e
commit d077bb2075
4 changed files with 195 additions and 289 deletions

@ -7,6 +7,7 @@
#include <stddef.h>
#include <concepts>
#include <functional>
#include <memory>
#include <tuple>
@ -123,7 +124,7 @@ class UnretainedWrapper {
// This is enforced by `static_assert`s in `ParamCanBeBound`.
using GetPtrType = std::conditional_t<
raw_ptr_traits::IsSupportedType<T>::value &&
std::is_same_v<UnretainedTrait, unretained_traits::MayDangle>,
std::same_as<UnretainedTrait, unretained_traits::MayDangle>,
DanglingRawPtrType,
T*>;
@ -151,15 +152,15 @@ class UnretainedWrapper {
private:
// `ptr_` is either a `raw_ptr` or a regular C++ pointer.
template <typename U>
requires std::same_as<T, U>
static GetPtrType GetInternal(U* ptr) {
static_assert(std::is_same_v<T, U>);
return ptr;
}
template <typename U, RawPtrTraits Traits>
requires std::same_as<T, U>
static GetPtrType GetInternal(const raw_ptr<U, Traits>& ptr) {
static_assert(std::is_same_v<T, U>);
if constexpr (std::is_same_v<UnretainedTrait,
unretained_traits::MayNotDangle>) {
if constexpr (std::same_as<UnretainedTrait,
unretained_traits::MayNotDangle>) {
ptr.ReportIfDangling();
}
return ptr;
@ -182,7 +183,7 @@ class UnretainedWrapper {
// It is allowable to convert `raw_ptr<T>` -> `T*`, but not in the other
// direction. See the comment by `GetPtrType` describing for more details.
static_assert(std::is_pointer_v<GetPtrType> ||
std::is_same_v<GetPtrType, StorageType>);
std::same_as<GetPtrType, StorageType>);
StorageType ptr_;
};
@ -225,13 +226,13 @@ class UnretainedRefWrapper {
private:
// `ref_` is either a `raw_ref` or a regular C++ reference.
template <typename U>
requires std::same_as<T, U>
static T& GetInternal(U& ref) {
static_assert(std::is_same_v<T, U>);
return ref;
}
template <typename U, RawPtrTraits Traits>
requires std::same_as<T, U>
static T& GetInternal(const raw_ref<U, Traits>& ref) {
static_assert(std::is_same_v<T, U>);
// The ultimate goal is to crash when a callback is invoked with a
// dangling pointer. This is checked here. For now, it is configured to
// either crash, DumpWithoutCrashing or be ignored. This depends on the
@ -265,6 +266,20 @@ class UnretainedRefWrapper {
StorageType ref_;
};
// Can't use `is_instantiation` to detect the unretained wrappers, since they
// have non-type template params.
template <template <typename, typename, RawPtrTraits> typename WrapperT,
typename T>
inline constexpr bool kIsUnretainedWrapper = false;
template <template <typename, typename, RawPtrTraits> typename WrapperT,
typename T,
typename UnretainedTrait,
RawPtrTraits PtrTraits>
inline constexpr bool
kIsUnretainedWrapper<WrapperT, WrapperT<T, UnretainedTrait, PtrTraits>> =
true;
// The class is used to wrap `UnretainedRefWrapper` when the latter is used as
// a method receiver (a reference on `this` argument). This is needed because
// the internal callback mechanism expects the receiver to have the type
@ -393,7 +408,7 @@ decltype(auto) Unwrap(T&& o) {
return Unwrapper<T>::Unwrap(std::forward<T>(o));
}
// IsWeakMethod is a helper that determine if we are binding a WeakPtr<> to a
// `kIsWeakMethod` is a helper that determine if we are binding a WeakPtr<> to a
// method. It is used internally by Bind() to select the correct
// InvokeHelper that will no-op itself in the event the WeakPtr<> for
// the target object is invalidated.
@ -401,10 +416,11 @@ decltype(auto) Unwrap(T&& o) {
// The first argument should be the type of the object that will be received by
// the method.
template <bool is_method, typename... Args>
struct IsWeakMethod : std::false_type {};
inline constexpr bool kIsWeakMethod = false;
template <typename T, typename... Args>
struct IsWeakMethod<true, T, Args...> : IsWeakReceiver<T> {};
inline constexpr bool kIsWeakMethod<true, T, Args...> =
IsWeakReceiver<T>::value;
// Packs a list of types to hold them in a single type.
template <typename... Types>
@ -412,46 +428,32 @@ struct TypeList {};
// Used for DropTypeListItem implementation.
template <size_t n, typename List>
struct DropTypeListItemImpl;
requires is_instantiation<TypeList, List>
struct DropTypeListItemImpl {
using Type = List;
};
// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure.
template <size_t n, typename T, typename... List>
requires(n > 0)
struct DropTypeListItemImpl<n, TypeList<T, List...>>
: DropTypeListItemImpl<n - 1, TypeList<List...>> {};
template <typename T, typename... List>
struct DropTypeListItemImpl<0, TypeList<T, List...>> {
using Type = TypeList<T, List...>;
};
template <>
struct DropTypeListItemImpl<0, TypeList<>> {
using Type = TypeList<>;
};
// A type-level function that drops |n| list item from given TypeList.
template <size_t n, typename List>
using DropTypeListItem = typename DropTypeListItemImpl<n, List>::Type;
// Used for TakeTypeListItem implementation.
template <size_t n, typename List, typename... Accum>
struct TakeTypeListItemImpl;
requires is_instantiation<TypeList, List>
struct TakeTypeListItemImpl {
using Type = TypeList<Accum...>;
};
// Do not use enable_if and SFINAE here to avoid MSVC2013 compile failure.
template <size_t n, typename T, typename... List, typename... Accum>
requires(n > 0)
struct TakeTypeListItemImpl<n, TypeList<T, List...>, Accum...>
: TakeTypeListItemImpl<n - 1, TypeList<List...>, Accum..., T> {};
template <typename T, typename... List, typename... Accum>
struct TakeTypeListItemImpl<0, TypeList<T, List...>, Accum...> {
using Type = TypeList<Accum...>;
};
template <typename... Accum>
struct TakeTypeListItemImpl<0, TypeList<>, Accum...> {
using Type = TypeList<Accum...>;
};
// A type-level function that takes first |n| list item from given TypeList.
// E.g. TakeTypeListItem<3, TypeList<A, B, C, D>> is evaluated to
// TypeList<A, B, C>.
@ -477,9 +479,7 @@ struct MakeFunctionTypeImpl;
template <typename R, typename... Args>
struct MakeFunctionTypeImpl<R, TypeList<Args...>> {
// MSVC 2013 doesn't support Type Alias of function types.
// Revisit this after we update it to newer version.
typedef R Type(Args...);
using Type = R(Args...);
};
// A type-level function that constructs a function type that has |R| as its
@ -542,29 +542,26 @@ template <typename Callable>
using ExtractCallableRunType =
typename ExtractCallableRunTypeImpl<Callable>::Type;
// IsCallableObject<Functor> is std::true_type if |Functor| has operator().
// Otherwise, it's std::false_type.
// IsCallableObject<Functor> is true if `Functor` has a non-overloaded
// `operator()`. This means it fails on some real callable objects, e.g. with
// templated or overloaded `operator()`s.
// Example:
// IsCallableObject<void(*)()>::value is false.
// IsCallableObject<void(*)()> is false.
//
// struct Foo {};
// IsCallableObject<void(Foo::*)()>::value is false.
// IsCallableObject<void(Foo::*)()> is false.
//
// int i = 0;
// auto f = [i] {};
// IsCallableObject<decltype(f)>::value is false.
// IsCallableObject<decltype(f)> is false.
template <typename Functor>
struct IsCallableObject : std::false_type {};
concept IsCallableObject = requires { &Functor::operator(); };
template <typename Callable>
requires requires { &Callable::operator(); }
struct IsCallableObject<Callable> : std::true_type {};
// HasRefCountedTypeAsRawPtr inherits from true_type when any of the |Args| is a
// raw pointer to a RefCounted type.
// `HasRefCountedTypeAsRawPtr` is true when any of the `Args` is a raw pointer
// to a `RefCounted` type.
template <typename... Ts>
struct HasRefCountedTypeAsRawPtr
: std::disjunction<NeedsScopedRefptrButGetsRawPtr<Ts>...> {};
concept HasRefCountedTypeAsRawPtr =
std::disjunction_v<NeedsScopedRefptrButGetsRawPtr<Ts>...>;
// ForceVoidReturn<>
//
@ -603,7 +600,7 @@ struct FunctorTraits;
// // No non-static member variable and no virtual functions.
// };
template <typename Functor>
requires(IsCallableObject<Functor>::value)
requires IsCallableObject<Functor>
struct FunctorTraits<Functor> {
using RunType = ExtractCallableRunType<Functor>;
static constexpr bool is_method = false;
@ -633,6 +630,10 @@ struct FunctorTraits<R (*)(Args...)> {
}
};
template <typename R, typename... Args>
struct FunctorTraits<R (*)(Args...) noexcept> : FunctorTraits<R (*)(Args...)> {
};
#if BUILDFLAG(IS_WIN) && !defined(ARCH_CPU_64_BITS)
// For functions.
@ -715,6 +716,10 @@ struct FunctorTraits<R (Receiver::*)(Args...)> {
}
};
template <typename R, typename Receiver, typename... Args>
struct FunctorTraits<R (Receiver::*)(Args...) noexcept>
: FunctorTraits<R (Receiver::*)(Args...)> {};
// For const methods.
template <typename R, typename Receiver, typename... Args>
struct FunctorTraits<R (Receiver::*)(Args...) const> {
@ -732,6 +737,10 @@ struct FunctorTraits<R (Receiver::*)(Args...) const> {
}
};
template <typename R, typename Receiver, typename... Args>
struct FunctorTraits<R (Receiver::*)(Args...) const noexcept>
: FunctorTraits<R (Receiver::*)(Args...) const> {};
#if BUILDFLAG(IS_WIN) && !defined(ARCH_CPU_64_BITS)
// For __stdcall methods.
@ -746,23 +755,6 @@ struct FunctorTraits<R (__stdcall Receiver::*)(Args...) const>
#endif // BUILDFLAG(IS_WIN) && !defined(ARCH_CPU_64_BITS)
#ifdef __cpp_noexcept_function_type
// noexcept makes a distinct function type in C++17.
// I.e. `void(*)()` and `void(*)() noexcept` are same in pre-C++17, and
// different in C++17.
template <typename R, typename... Args>
struct FunctorTraits<R (*)(Args...) noexcept> : FunctorTraits<R (*)(Args...)> {
};
template <typename R, typename Receiver, typename... Args>
struct FunctorTraits<R (Receiver::*)(Args...) noexcept>
: FunctorTraits<R (Receiver::*)(Args...)> {};
template <typename R, typename Receiver, typename... Args>
struct FunctorTraits<R (Receiver::*)(Args...) const noexcept>
: FunctorTraits<R (Receiver::*)(Args...) const> {};
#endif
// For IgnoreResults.
template <typename T>
struct FunctorTraits<IgnoreResultHelper<T>> : FunctorTraits<T> {
@ -941,10 +933,6 @@ struct Invoker<StorageType, R(UnboundArgs...)> {
BoundArgsTuple&& bound,
std::index_sequence<indices...> seq,
UnboundArgs&&... unbound_args) {
static constexpr bool is_method = MakeFunctorTraits<Functor>::is_method;
using DecayedArgsTuple = std::decay_t<BoundArgsTuple>;
#if BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
RawPtrAsanBoundArgTracker raw_ptr_asan_bound_arg_tracker;
raw_ptr_asan_bound_arg_tracker.AddArgs(
@ -952,9 +940,10 @@ struct Invoker<StorageType, R(UnboundArgs...)> {
std::forward<UnboundArgs>(unbound_args)...);
#endif // BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
static constexpr bool is_weak_call =
IsWeakMethod<is_method,
std::tuple_element_t<indices, DecayedArgsTuple>...>();
using DecayedArgsTuple = std::decay_t<BoundArgsTuple>;
static constexpr bool kIsWeakCall =
kIsWeakMethod<MakeFunctorTraits<Functor>::is_method,
std::tuple_element_t<indices, DecayedArgsTuple>...>;
// Do not `Unwrap()` here, as that immediately triggers dangling pointer
// detection. Dangling pointer detection should only be triggered if the
@ -966,65 +955,56 @@ struct Invoker<StorageType, R(UnboundArgs...)> {
// a memory safety error because protecting raw pointers usage with weak
// receivers (where the weak receiver usually own the pointed objects) is a
// common and broadly used pattern in the codebase.
return InvokeHelper<is_weak_call, R, indices...>::MakeItSo(
return InvokeHelper<kIsWeakCall, R, indices...>::MakeItSo(
std::forward<Functor>(functor), std::forward<BoundArgsTuple>(bound),
std::forward<UnboundArgs>(unbound_args)...);
}
};
template <typename Functor>
requires(FunctorTraits<Functor>::is_nullable)
constexpr bool IsNull(const Functor& functor) {
return !functor;
}
// Allow binding a method call with no receiver.
// TODO(crbug.com/1511757): Remove or make safe.
template <typename... Unused>
void VerifyMethodReceiver(Unused&&...) {}
template <typename Functor>
requires(!FunctorTraits<Functor>::is_nullable)
constexpr bool IsNull(const Functor&) {
return false;
}
template <typename Receiver, typename... Unused>
void VerifyMethodReceiver(Receiver&& receiver, Unused&&...) {
// Asserts that Callback is not the first owner of a ref-counted receiver.
if constexpr (IsPointerV<std::decay_t<Receiver>> &&
IsRefCountedType<RemovePointerT<std::decay_t<Receiver>>>) {
DCHECK(receiver);
// The base case of BanUnconstructedRefCountedReceiver that checks nothing.
template <typename Functor, typename... Unused>
void BanUnconstructedRefCountedReceiver(Unused&&...) {}
// Asserts that Callback is not the first owner of a ref-counted receiver.
template <typename Functor, typename Receiver, typename... Unused>
requires(MakeFunctorTraits<Functor>::is_method &&
IsPointerV<std::decay_t<Receiver>> &&
IsRefCountedType<RemovePointerT<std::decay_t<Receiver>>>)
void BanUnconstructedRefCountedReceiver(Receiver&& receiver, Unused&&...) {
DCHECK(receiver);
// It's error prone to make the implicit first reference to ref-counted types.
// In the example below, base::BindOnce() would make the implicit first
// reference to the ref-counted Foo. If PostTask() failed or the posted task
// ran fast enough, the newly created instance could be destroyed before `oo`
// makes another reference.
// Foo::Foo() {
// base::ThreadPool::PostTask(FROM_HERE, base::BindOnce(&Foo::Bar, this));
// }
//
// scoped_refptr<Foo> oo = new Foo();
//
// Hence, base::Bind{Once,Repeating}() refuses to create the first reference
// to ref-counted objects, and DCHECK()s otherwise. As above, that typically
// happens around PostTask() in their constructor, and such objects can be
// destroyed before `new` returns if the task resolves fast enough.
//
// Instead of doing the above, please consider adding a static constructor,
// and keep the first reference alive explicitly.
// // static
// scoped_refptr<Foo> Foo::Create() {
// auto foo = base::WrapRefCounted(new Foo());
// base::ThreadPool::PostTask(FROM_HERE, base::BindOnce(&Foo::Bar, foo));
// return foo;
// }
//
// Foo::Foo() {}
//
// scoped_refptr<Foo> oo = Foo::Create();
DCHECK(receiver->HasAtLeastOneRef());
// It's error prone to make the implicit first reference to ref-counted
// types. In the example below, base::BindOnce() would make the implicit
// first reference to the ref-counted Foo. If PostTask() failed or the
// posted task ran fast enough, the newly created instance could be
// destroyed before `oo` makes another reference.
// Foo::Foo() {
// base::ThreadPool::PostTask(FROM_HERE,
// base::BindOnce(&Foo::Bar, this));
// }
//
// scoped_refptr<Foo> oo = new Foo();
//
// Hence, base::Bind{Once,Repeating}() refuses to create the first reference
// to ref-counted objects, and DCHECK()s otherwise. As above, that typically
// happens around PostTask() in their constructor, and such objects can be
// destroyed before `new` returns if the task resolves fast enough.
//
// Instead of doing the above, please consider adding a static constructor,
// and keep the first reference alive explicitly.
// // static
// scoped_refptr<Foo> Foo::Create() {
// auto foo = base::WrapRefCounted(new Foo());
// base::ThreadPool::PostTask(FROM_HERE,
// base::BindOnce(&Foo::Bar, foo));
// return foo;
// }
//
// Foo::Foo() {}
//
// scoped_refptr<Foo> oo = Foo::Create();
DCHECK(receiver->HasAtLeastOneRef());
}
}
// BindState<>
@ -1033,20 +1013,18 @@ void BanUnconstructedRefCountedReceiver(Receiver&& receiver, Unused&&...) {
template <typename Functor, typename... BoundArgs>
struct BindState final : BindStateBase {
private:
using FunctorTraits = MakeFunctorTraits<Functor>;
using BoundArgsTuple = std::tuple<BoundArgs...>;
using Traits = CallbackCancellationTraits<Functor, BoundArgsTuple>;
public:
template <typename ForwardFunctor, typename... ForwardBoundArgs>
static BindState* Create(BindStateBase::InvokeFuncStorage invoke_func,
ForwardFunctor&& functor,
ForwardBoundArgs&&... bound_args) {
// Ban ref counted receivers that were not yet fully constructed to avoid
// a common pattern of racy situation.
BanUnconstructedRefCountedReceiver<ForwardFunctor>(bound_args...);
return new BindState(std::bool_constant<Traits::is_cancellable>(),
invoke_func, std::forward<ForwardFunctor>(functor),
if constexpr (FunctorTraits::is_method) {
VerifyMethodReceiver(bound_args...);
}
return new BindState(invoke_func, std::forward<ForwardFunctor>(functor),
std::forward<ForwardBoundArgs>(bound_args)...);
}
@ -1054,41 +1032,45 @@ struct BindState final : BindStateBase {
BoundArgsTuple bound_args_;
private:
static constexpr bool kIsNestedCallback =
MakeFunctorTraits<Functor>::is_callback;
using CancellationTraits =
CallbackCancellationTraits<Functor, BoundArgsTuple>;
template <typename ForwardFunctor, typename... ForwardBoundArgs>
explicit BindState(std::true_type,
BindStateBase::InvokeFuncStorage invoke_func,
requires CancellationTraits::is_cancellable
explicit BindState(BindStateBase::InvokeFuncStorage invoke_func,
ForwardFunctor&& functor,
ForwardBoundArgs&&... bound_args)
: BindStateBase(invoke_func, &Destroy, &QueryCancellationTraits),
functor_(std::forward<ForwardFunctor>(functor)),
bound_args_(std::forward<ForwardBoundArgs>(bound_args)...) {
// We check the validity of nested callbacks (e.g., Bind(callback, ...)) in
// release builds to avoid null pointers from ending up in posted tasks,
// causing hard-to-diagnose crashes. Ideally we'd do this for all functors
// here, but that would have a large binary size impact.
if constexpr (kIsNestedCallback) {
CHECK(!IsNull(functor_));
} else {
DCHECK(!IsNull(functor_));
if constexpr (FunctorTraits::is_nullable) {
// We check the validity of nested callbacks (e.g., Bind(callback, ...))
// in release builds to avoid null pointers from ending up in posted
// tasks, causing hard-to-diagnose crashes. Ideally we'd do this for all
// functors here, but that would have a large binary size impact.
if constexpr (FunctorTraits::is_callback) {
CHECK(!!functor_);
} else {
DCHECK(!!functor_);
}
}
}
template <typename ForwardFunctor, typename... ForwardBoundArgs>
explicit BindState(std::false_type,
BindStateBase::InvokeFuncStorage invoke_func,
requires(!CancellationTraits::is_cancellable)
explicit BindState(BindStateBase::InvokeFuncStorage invoke_func,
ForwardFunctor&& functor,
ForwardBoundArgs&&... bound_args)
: BindStateBase(invoke_func, &Destroy),
functor_(std::forward<ForwardFunctor>(functor)),
bound_args_(std::forward<ForwardBoundArgs>(bound_args)...) {
// See above for CHECK/DCHECK rationale.
if constexpr (kIsNestedCallback) {
CHECK(!IsNull(functor_));
} else {
DCHECK(!IsNull(functor_));
if constexpr (FunctorTraits::is_nullable) {
// See above for CHECK/DCHECK rationale.
if constexpr (FunctorTraits::is_callback) {
CHECK(!!functor_);
} else {
DCHECK(!!functor_);
}
}
}
@ -1111,12 +1093,14 @@ struct BindState final : BindStateBase {
// Helpers to do arg tuple expansion.
template <size_t... indices>
bool IsCancelled(std::index_sequence<indices...>) const {
return Traits::IsCancelled(functor_, std::get<indices>(bound_args_)...);
return CancellationTraits::IsCancelled(functor_,
std::get<indices>(bound_args_)...);
}
template <size_t... indices>
bool MaybeValid(std::index_sequence<indices...>) const {
return Traits::MaybeValid(functor_, std::get<indices>(bound_args_)...);
return CancellationTraits::MaybeValid(functor_,
std::get<indices>(bound_args_)...);
}
};
@ -1169,8 +1153,7 @@ struct MakeBindStateTypeImpl<false, Functor, BoundArgs...> {
// condition to be checked is always used as the default value of a template
// argument, so callers can simply instantiate the struct with no template
// params to verify the condition.
template <bool v =
!HasRefCountedTypeAsRawPtr<std::decay_t<BoundArgs>...>::value>
template <bool v = !HasRefCountedTypeAsRawPtr<std::decay_t<BoundArgs>...>>
struct NoRawPtrsToRefCountedTypes {
static constexpr bool value = [] {
static_assert(
@ -1227,8 +1210,7 @@ struct MakeBindStateTypeImpl<true, Functor, Receiver, BoundArgs...> {
}();
};
template <bool v =
!HasRefCountedTypeAsRawPtr<std::decay_t<BoundArgs>...>::value>
template <bool v = !HasRefCountedTypeAsRawPtr<std::decay_t<BoundArgs>...>>
struct NoRawPtrsToRefCountedTypes {
static constexpr bool value = [] {
static_assert(
@ -1254,24 +1236,6 @@ using MakeBindStateType =
Functor,
BoundArgs...>;
// The implementation of TransformToUnwrappedType below.
template <bool is_once, typename T>
struct TransformToUnwrappedTypeImpl;
template <typename T>
struct TransformToUnwrappedTypeImpl<true, T> {
using StoredType = std::decay_t<T>;
using ForwardType = StoredType&&;
using Unwrapped = decltype(Unwrap(std::declval<ForwardType>()));
};
template <typename T>
struct TransformToUnwrappedTypeImpl<false, T> {
using StoredType = std::decay_t<T>;
using ForwardType = const StoredType&;
using Unwrapped = decltype(Unwrap(std::declval<ForwardType>()));
};
// Transform |T| into `Unwrapped` type, which is passed to the target function.
// Example:
// In is_once == true case,
@ -1282,9 +1246,13 @@ struct TransformToUnwrappedTypeImpl<false, T> {
// `int&&` -> `const int&`,
// `const int&` -> `const int&`,
// `OwnedWrapper<int>&` -> `int* const &`.
template <bool is_once, typename T>
template <bool is_once,
typename T,
typename StoredType = std::decay_t<T>,
typename ForwardedType =
std::conditional_t<is_once, StoredType&&, const StoredType&>>
using TransformToUnwrappedType =
typename TransformToUnwrappedTypeImpl<is_once, T>::Unwrapped;
decltype(Unwrap(std::declval<ForwardedType>()));
// Converts `this` arguments to underlying pointer types. E.g.
// `int*` -> `int*`
@ -1349,13 +1317,6 @@ struct MakeUnwrappedTypeList<is_once, true, Receiver, Args...> {
static constexpr bool value = UnderlyingReceiver::value;
};
// IsOnceCallback<T> is a std::true_type if |T| is a OnceCallback.
template <typename T>
struct IsOnceCallback : std::false_type {};
template <typename Signature>
struct IsOnceCallback<OnceCallback<Signature>> : std::true_type {};
// IsUnretainedMayDangle is true if StorageType is of type
// `UnretainedWrapper<T, unretained_traits::MayDangle, PtrTraits>.
// Note that it is false for unretained_traits::MayDangleUntriaged.
@ -1377,11 +1338,10 @@ template <typename T,
inline constexpr bool UnretainedAndRawPtrHaveCompatibleTraits<
UnretainedWrapper<T, unretained_traits::MayDangle, PtrTraitsInUnretained>,
raw_ptr<T, PtrTraitsInReceiver>> =
std::is_same_v<
typename UnretainedWrapper<T,
unretained_traits::MayDangle,
PtrTraitsInUnretained>::GetPtrType,
raw_ptr<T, PtrTraitsInReceiver>>;
std::same_as<typename UnretainedWrapper<T,
unretained_traits::MayDangle,
PtrTraitsInUnretained>::GetPtrType,
raw_ptr<T, PtrTraitsInReceiver>>;
// Helpers to make error messages slightly more readable.
template <int i>
@ -1419,7 +1379,7 @@ struct BindArgument {
template <typename StorageType>
struct StoredAs {
static constexpr bool kBindArgumentCanBeCaptured =
std::is_constructible_v<StorageType, BoundAsType>;
std::constructible_from<StorageType, BoundAsType>;
// Note that this intentionally drops the const qualifier from
// `BoundAsType`, to test if it *could* have been successfully bound if
@ -1622,12 +1582,6 @@ struct ParamsCanBeBound<is_method,
Params>...>;
};
template <typename T>
inline constexpr bool kBindArgIsBasePassed = false;
template <typename T>
inline constexpr bool kBindArgIsBasePassed<PassedWrapper<T>> = true;
// Core implementation of Bind variants, which checks common preconditions
// before returning an appropriate callback.
template <template <typename> class CallbackT>
@ -1708,7 +1662,8 @@ struct BindHelper {
// using CallbackType = OnceCallback<double(const std::string&)>;
// ...
// ```
static constexpr bool kIsOnce = IsOnceCallback<CallbackT<void()>>::value;
static constexpr bool kIsOnce =
is_instantiation<OnceCallback, CallbackT<void()>>;
using FunctorTraits = MakeFunctorTraits<Functor>;
using UnwrappedArgsList =
MakeUnwrappedTypeList<kIsOnce, FunctorTraits::is_method, Args&&...>;
@ -1756,27 +1711,24 @@ struct BindHelper {
// extra bound arguments. We CHECK() the validity of callback to guard against
// null pointers accidentally ending up in posted tasks, causing hard-to-debug
// crashes.
template <typename Signature>
requires(std::same_as<CallbackT<Signature>, OnceCallback<Signature>>)
static OnceCallback<Signature> Bind(OnceCallback<Signature> callback) {
// `OnceCallback` passed to `OnceCallback`, or `RepeatingCallback` passed to
// `RepeatingCallback`.
template <typename T>
requires is_instantiation<CallbackT, T>
static T Bind(T callback) {
CHECK(callback);
return callback;
}
// `RepeatingCallback` passed to `OnceCallback`. The opposite direction is
// intentionally not supported.
template <typename Signature>
requires(std::same_as<CallbackT<Signature>, OnceCallback<Signature>>)
requires is_instantiation<CallbackT, OnceCallback<Signature>>
static OnceCallback<Signature> Bind(RepeatingCallback<Signature> callback) {
CHECK(callback);
return callback;
}
template <typename Signature>
requires(std::same_as<CallbackT<Signature>, RepeatingCallback<Signature>>)
static RepeatingCallback<Signature> Bind(
RepeatingCallback<Signature> callback) {
CHECK(callback);
return callback;
}
};
// Implementation of `BindOnce()`, which checks preconditions before handing off
@ -1784,7 +1736,7 @@ struct BindHelper {
template <typename Functor, typename... Args>
struct BindOnceHelper {
private:
template <bool v = !IsOnceCallback<std::decay_t<Functor>>() ||
template <bool v = !is_instantiation<OnceCallback, std::decay_t<Functor>> ||
(std::is_rvalue_reference_v<Functor&&> &&
!std::is_const_v<std::remove_reference_t<Functor>>)>
struct OnceCallbackFunctorIsConstRvalue {
@ -1795,7 +1747,8 @@ struct BindOnceHelper {
}();
};
template <bool v = (... && !kBindArgIsBasePassed<std::decay_t<Args>>)>
template <bool v =
(... && !is_instantiation<PassedWrapper, std::decay_t<Args>>)>
struct NoBindArgIsBasePassed {
static constexpr bool value = [] {
static_assert(
@ -1820,7 +1773,7 @@ struct BindOnceHelper {
template <typename Functor, typename... Args>
struct BindRepeatingHelper {
private:
template <bool v = !IsOnceCallback<std::decay_t<Functor>>()>
template <bool v = !is_instantiation<OnceCallback, std::decay_t<Functor>>>
struct FunctorIsNotOnceCallback {
static constexpr bool value = [] {
static_assert(v,
@ -1853,14 +1806,11 @@ struct BindRepeatingHelper {
// WeakPtr<Foo> oo = nullptr;
// base::BindOnce(&Foo::bar, oo).Run();
template <typename T>
struct IsWeakReceiver : std::false_type {};
struct IsWeakReceiver : std::bool_constant<is_instantiation<WeakPtr, T>> {};
template <typename T>
struct IsWeakReceiver<std::reference_wrapper<T>> : IsWeakReceiver<T> {};
template <typename T>
struct IsWeakReceiver<WeakPtr<T>> : std::true_type {};
// An injection point to control how objects are checked for maybe validity,
// which is an optimistic thread-safe check for full validity.
template <typename>
@ -1882,39 +1832,14 @@ struct BindUnwrapTraits {
}
};
template <typename T, typename UnretainedTrait, RawPtrTraits PtrTraits>
struct BindUnwrapTraits<
internal::UnretainedWrapper<T, UnretainedTrait, PtrTraits>> {
static auto Unwrap(
const internal::UnretainedWrapper<T, UnretainedTrait, PtrTraits>& o) {
return o.get();
}
};
template <typename T, typename UnretainedTrait, RawPtrTraits PtrTraits>
struct BindUnwrapTraits<
internal::UnretainedRefWrapper<T, UnretainedTrait, PtrTraits>> {
static T& Unwrap(
const internal::UnretainedRefWrapper<T, UnretainedTrait, PtrTraits>& o) {
return o.get();
}
};
template <typename T>
struct BindUnwrapTraits<internal::RetainedRefWrapper<T>> {
static T* Unwrap(const internal::RetainedRefWrapper<T>& o) { return o.get(); }
};
template <typename T, typename Deleter>
struct BindUnwrapTraits<internal::OwnedWrapper<T, Deleter>> {
static T* Unwrap(const internal::OwnedWrapper<T, Deleter>& o) {
return o.get();
}
};
template <typename T>
struct BindUnwrapTraits<internal::OwnedRefWrapper<T>> {
static T& Unwrap(const internal::OwnedRefWrapper<T>& o) { return o.get(); }
requires internal::kIsUnretainedWrapper<internal::UnretainedWrapper, T> ||
internal::kIsUnretainedWrapper<internal::UnretainedRefWrapper, T> ||
is_instantiation<internal::RetainedRefWrapper, T> ||
is_instantiation<internal::OwnedWrapper, T> ||
is_instantiation<internal::OwnedRefWrapper, T>
struct BindUnwrapTraits<T> {
static decltype(auto) Unwrap(const T& o) { return o.get(); }
};
template <typename T>
@ -1940,8 +1865,8 @@ struct CallbackCancellationTraits {
// Specialization for method bound to weak pointer receiver.
template <typename Functor, typename... BoundArgs>
requires(internal::IsWeakMethod<internal::FunctorTraits<Functor>::is_method,
BoundArgs...>::value)
requires internal::kIsWeakMethod<internal::FunctorTraits<Functor>::is_method,
BoundArgs...>
struct CallbackCancellationTraits<Functor, std::tuple<BoundArgs...>> {
static constexpr bool is_cancellable = true;
@ -1961,33 +1886,16 @@ struct CallbackCancellationTraits<Functor, std::tuple<BoundArgs...>> {
};
// Specialization for a nested bind.
template <typename Signature, typename... BoundArgs>
struct CallbackCancellationTraits<OnceCallback<Signature>,
std::tuple<BoundArgs...>> {
template <typename Functor, typename... BoundArgs>
requires is_instantiation<OnceCallback, Functor> ||
is_instantiation<RepeatingCallback, Functor>
struct CallbackCancellationTraits<Functor, std::tuple<BoundArgs...>> {
static constexpr bool is_cancellable = true;
template <typename Functor>
static bool IsCancelled(const Functor& functor, const BoundArgs&...) {
return functor.IsCancelled();
}
template <typename Functor>
static bool MaybeValid(const Functor& functor, const BoundArgs&...) {
return MaybeValidTraits<Functor>::MaybeValid(functor);
}
};
template <typename Signature, typename... BoundArgs>
struct CallbackCancellationTraits<RepeatingCallback<Signature>,
std::tuple<BoundArgs...>> {
static constexpr bool is_cancellable = true;
template <typename Functor>
static bool IsCancelled(const Functor& functor, const BoundArgs&...) {
return functor.IsCancelled();
}
template <typename Functor>
static bool MaybeValid(const Functor& functor, const BoundArgs&...) {
return MaybeValidTraits<Functor>::MaybeValid(functor);
}

@ -1493,17 +1493,17 @@ TEST_F(BindTest, RepeatingWithoutPassed) {
}
TEST_F(BindTest, CapturelessLambda) {
EXPECT_FALSE(internal::IsCallableObject<void>::value);
EXPECT_FALSE(internal::IsCallableObject<int>::value);
EXPECT_FALSE(internal::IsCallableObject<void (*)()>::value);
EXPECT_FALSE(internal::IsCallableObject<void (NoRef::*)()>::value);
EXPECT_FALSE(internal::IsCallableObject<void>);
EXPECT_FALSE(internal::IsCallableObject<int>);
EXPECT_FALSE(internal::IsCallableObject<void (*)()>);
EXPECT_FALSE(internal::IsCallableObject<void (NoRef::*)()>);
auto f = [] {};
EXPECT_TRUE(internal::IsCallableObject<decltype(f)>::value);
EXPECT_TRUE(internal::IsCallableObject<decltype(f)>);
int i = 0;
auto g = [i] { (void)i; };
EXPECT_TRUE(internal::IsCallableObject<decltype(g)>::value);
EXPECT_TRUE(internal::IsCallableObject<decltype(g)>);
auto h = [](int, double) { return 'k'; };
EXPECT_TRUE((std::is_same_v<char(int, double),
@ -1535,9 +1535,9 @@ TEST_F(BindTest, EmptyFunctor) {
int operator()() const { return 42; }
};
EXPECT_TRUE(internal::IsCallableObject<NonEmptyFunctor>::value);
EXPECT_TRUE(internal::IsCallableObject<EmptyFunctor>::value);
EXPECT_TRUE(internal::IsCallableObject<EmptyFunctorConst>::value);
EXPECT_TRUE(internal::IsCallableObject<NonEmptyFunctor>);
EXPECT_TRUE(internal::IsCallableObject<EmptyFunctor>);
EXPECT_TRUE(internal::IsCallableObject<EmptyFunctorConst>);
EXPECT_EQ(42, BindOnce(EmptyFunctor()).Run());
EXPECT_EQ(42, BindOnce(EmptyFunctorConst()).Run());
EXPECT_EQ(42, BindRepeating(EmptyFunctorConst()).Run());

@ -854,9 +854,7 @@ of its implementation.
namespace base {
template <typename Receiver>
struct IsWeakReceiver {
static constexpr bool value = false;
};
struct IsWeakReceiver : std::false_type {};
template <typename Obj>
struct BindUnwrapTraits {

@ -124,9 +124,9 @@ auto MakeWGPUOnceCallback(CallbackType&& cb) {
}
template <typename FunctionType, typename... BoundParameters>
requires(!base::internal::IsWeakMethod<
requires(!base::internal::kIsWeakMethod<
base::internal::MakeFunctorTraits<FunctionType>::is_method,
BoundParameters...>::value)
BoundParameters...>)
auto BindWGPUOnceCallback(FunctionType&& function,
BoundParameters&&... bound_parameters) {
return MakeWGPUOnceCallback(