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 {