Make absl::Condition work with C++23 deducing-this Closes: #1992 PiperOrigin-RevId: 857136106 Change-Id: Iae31d7c6c9a0fda16ebf2c4f68764da521d036bf
diff --git a/absl/synchronization/mutex.h b/absl/synchronization/mutex.h index 39ea0d0..ad156d4 100644 --- a/absl/synchronization/mutex.h +++ b/absl/synchronization/mutex.h
@@ -61,6 +61,7 @@ #include <atomic> #include <cstdint> #include <cstring> +#include <type_traits> #include "absl/base/attributes.h" #include "absl/base/config.h" @@ -81,6 +82,18 @@ class Condition; struct SynchWaitParams; +namespace synchronization_internal { + +template <typename T, typename = void> +struct HasConstMemberCallOperator : std::false_type {}; + +template <typename T> +struct HasConstMemberCallOperator< + T, std::void_t<decltype(static_cast<bool (T::*)() const>(&T::operator()))>> + : std::true_type {}; + +} // namespace synchronization_internal + // ----------------------------------------------------------------------------- // Mutex // ----------------------------------------------------------------------------- @@ -866,11 +879,23 @@ // Implementation note: The second template parameter ensures that this // constructor doesn't participate in overload resolution if T doesn't have // `bool operator() const`. - template <typename T, typename E = decltype(static_cast<bool (T::*)() const>( - &T::operator()))> + template <typename T, + std::enable_if_t< + synchronization_internal::HasConstMemberCallOperator<T>::value, + int> = 0> explicit Condition(const T* absl_nonnull obj) : Condition(obj, static_cast<bool (T::*)() const>(&T::operator())) {} + // Constructor for functors that do not match the `bool operator()() const` + // signature, such as those using C++23 "deducing this" or static operator(). + template < + typename T, + typename = std::enable_if_t< + !synchronization_internal::HasConstMemberCallOperator<T>::value && + sizeof(static_cast<bool (*)(const T&)>(&T::operator())) != 0>> + explicit Condition(const T* absl_nonnull obj) + : Condition(&CallByRef<T>, obj) {} + // A Condition that always returns `true`. // kTrue is only useful in a narrow set of circumstances, mostly when // it's passed conditionally. For example: @@ -932,6 +957,11 @@ template <typename T, typename ConditionMethodPtr> static bool CastAndCallMethod(const Condition* absl_nonnull c); + template <typename T> + static bool CallByRef(const T* absl_nonnull self) { + return (*self)(); + } + // Helper methods for storing, validating, and reading callback arguments. template <typename T> inline void StoreCallback(T callback) {
diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc index 793acf8..38c8691 100644 --- a/absl/synchronization/mutex_test.cc +++ b/absl/synchronization/mutex_test.cc
@@ -993,6 +993,20 @@ EXPECT_TRUE(absl::Condition(&chapman, &Constable::WotsAllThisThen).Eval()); } +#ifdef __cpp_explicit_this_parameter +struct TrueViaDeducingThis { + template <class This, class... Args> + bool operator()(this const This&, Args...) { + return true; + } +}; + +TEST(Mutex, FunctorConditionDeducingThis) { + TrueViaDeducingThis f; + EXPECT_TRUE(absl::Condition(&f).Eval()); +} +#endif + struct True { template <class... Args> bool operator()(Args...) const {