mirror of
https://github.com/abseil/abseil-cpp.git
synced 2026-06-04 20:14:23 +08:00
Improve absl::FunctionRef compatibility with C++26
- Add deduction guides - Fix constructor constraints - Add lifetimebound annotations - Improve support for member pointers - Rename EnableIfCallable to EnableIfCompatible in const version for consistency PiperOrigin-RevId: 810527262 Change-Id: I366b94d6b6661a6c08e7ed3e2532a6242319b239
This commit is contained in:
committed by
Copybara-Service
parent
f1428cf4e4
commit
aef36c342b
@@ -85,13 +85,12 @@ class FunctionRef;
|
||||
// callback);
|
||||
template <typename R, typename... Args>
|
||||
class FunctionRef<R(Args...)> {
|
||||
private:
|
||||
protected:
|
||||
// Used to disable constructors for objects that are not compatible with the
|
||||
// signature of this FunctionRef.
|
||||
template <typename F, typename FR = std::invoke_result_t<F, Args&&...>>
|
||||
template <typename F, typename... U>
|
||||
using EnableIfCompatible =
|
||||
std::enable_if_t<std::conditional_t<std::is_void_v<R>, std::true_type,
|
||||
std::is_invocable_r<R, FR()>>::value>;
|
||||
std::enable_if_t<std::is_invocable_r<R, F, U..., Args...>::value>;
|
||||
|
||||
public:
|
||||
// Constructs a FunctionRef from any invocable type.
|
||||
@@ -111,9 +110,8 @@ class FunctionRef<R(Args...)> {
|
||||
//
|
||||
// This overload is also used for references to functions, since references to
|
||||
// functions can decay to function pointers implicitly.
|
||||
template <
|
||||
typename F, typename = EnableIfCompatible<F*>,
|
||||
absl::functional_internal::EnableIf<absl::is_function<F>::value> = 0>
|
||||
template <typename F, typename = EnableIfCompatible<F*>,
|
||||
absl::functional_internal::EnableIf<std::is_function_v<F>> = 0>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
FunctionRef(F* f ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept
|
||||
: invoker_(&absl::functional_internal::InvokeFunction<F*, R, Args...>) {
|
||||
@@ -126,22 +124,27 @@ class FunctionRef<R(Args...)> {
|
||||
// `F` at compile time. This allows calling arbitrary functions while avoiding
|
||||
// an indirection.
|
||||
// Needs C++20 as `nontype_t` needs C++20 for `auto` template parameters.
|
||||
template <auto F>
|
||||
template <auto F, typename = EnableIfCompatible<decltype(F)>>
|
||||
FunctionRef(nontype_t<F>) noexcept // NOLINT(google-explicit-constructor)
|
||||
: invoker_(&absl::functional_internal::InvokeFunction<decltype(F), F, R,
|
||||
Args...>) {}
|
||||
|
||||
template <auto F, typename Obj>
|
||||
template <
|
||||
auto F, typename Obj,
|
||||
typename = EnableIfCompatible<decltype(F), std::remove_reference_t<Obj>&>,
|
||||
absl::functional_internal::EnableIf<!std::is_rvalue_reference_v<Obj&&>> =
|
||||
0>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
FunctionRef(nontype_t<F>, Obj&& obj) noexcept
|
||||
FunctionRef(nontype_t<F>, Obj&& obj ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept
|
||||
: invoker_(&absl::functional_internal::InvokeObject<Obj&, decltype(F), F,
|
||||
R, Args...>) {
|
||||
ptr_.obj = std::addressof(obj);
|
||||
}
|
||||
|
||||
template <auto F, typename Obj>
|
||||
template <auto F, typename Obj,
|
||||
typename = EnableIfCompatible<decltype(F), Obj*>>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
FunctionRef(nontype_t<F>, Obj* obj) noexcept
|
||||
FunctionRef(nontype_t<F>, Obj* obj ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept
|
||||
: invoker_(&absl::functional_internal::InvokePtr<Obj, decltype(F), F, R,
|
||||
Args...>) {
|
||||
ptr_.obj = obj;
|
||||
@@ -161,42 +164,72 @@ class FunctionRef<R(Args...)> {
|
||||
// Allow const qualified function signatures. Since FunctionRef requires
|
||||
// constness anyway we can just make this a no-op.
|
||||
template <typename R, typename... Args>
|
||||
class FunctionRef<R(Args...) const> : public FunctionRef<R(Args...)> {
|
||||
class FunctionRef<R(Args...) const> : private FunctionRef<R(Args...)> {
|
||||
using Base = FunctionRef<R(Args...)>;
|
||||
|
||||
template <typename F, typename T = void>
|
||||
using EnableIfCallable =
|
||||
std::enable_if_t<!std::is_same_v<FunctionRef, absl::remove_cvref_t<F>> &&
|
||||
std::is_invocable_r_v<R, F, Args...> &&
|
||||
std::is_constructible_v<Base, F>,
|
||||
T>;
|
||||
template <typename F, typename... U>
|
||||
using EnableIfCompatible =
|
||||
typename Base::template EnableIfCompatible<F, U...>;
|
||||
|
||||
public:
|
||||
template <typename F, typename = EnableIfCallable<const F&>>
|
||||
template <
|
||||
typename F,
|
||||
typename = EnableIfCompatible<std::enable_if_t<
|
||||
!std::is_same_v<FunctionRef, absl::remove_cvref_t<F>>, const F&>>>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
FunctionRef(const F& f ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept : Base(f) {}
|
||||
|
||||
template <typename F,
|
||||
typename = std::enable_if_t<std::is_constructible_v<Base, F*>>>
|
||||
template <typename F, typename = EnableIfCompatible<F*>,
|
||||
absl::functional_internal::EnableIf<std::is_function_v<F>> = 0>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
FunctionRef(F* f ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept : Base(f) {}
|
||||
|
||||
#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
|
||||
template <auto F, typename = EnableIfCallable<decltype(F)>>
|
||||
template <auto F, typename = EnableIfCompatible<decltype(F)>>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
FunctionRef(nontype_t<F> arg) noexcept : Base(arg) {}
|
||||
|
||||
template <auto F, typename Obj, typename = EnableIfCallable<decltype(F)>>
|
||||
template <auto F, typename Obj,
|
||||
typename = EnableIfCompatible<decltype(F),
|
||||
const std::remove_reference_t<Obj>&>,
|
||||
absl::functional_internal::EnableIf<
|
||||
!std::is_rvalue_reference_v<Obj&&>> = 0>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
FunctionRef(nontype_t<F> arg, Obj&& obj) noexcept
|
||||
FunctionRef(nontype_t<F> arg,
|
||||
Obj&& obj ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept
|
||||
: Base(arg, std::forward<Obj>(obj)) {}
|
||||
|
||||
template <auto F, typename Obj, typename = EnableIfCallable<decltype(F)>>
|
||||
template <auto F, typename Obj,
|
||||
typename = EnableIfCompatible<decltype(F), const Obj*>>
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
FunctionRef(nontype_t<F> arg, Obj* obj) noexcept : Base(arg, obj) {}
|
||||
FunctionRef(nontype_t<F> arg,
|
||||
const Obj* obj ABSL_ATTRIBUTE_LIFETIME_BOUND) noexcept
|
||||
: Base(arg, obj) {}
|
||||
#endif
|
||||
|
||||
using Base::operator();
|
||||
};
|
||||
|
||||
template <class F>
|
||||
FunctionRef(F*) -> FunctionRef<F>;
|
||||
|
||||
#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
|
||||
template <auto Func>
|
||||
FunctionRef(nontype_t<Func>)
|
||||
-> FunctionRef<std::remove_pointer_t<decltype(Func)>>;
|
||||
|
||||
template <class M, class T, M T::* Func, class U>
|
||||
FunctionRef(nontype_t<Func>, U&&)
|
||||
-> FunctionRef<std::enable_if_t<std::is_member_pointer_v<M T::*>, M>>;
|
||||
|
||||
template <class M, class T, M T::* Func, class U>
|
||||
FunctionRef(nontype_t<Func>, U&&) -> FunctionRef<std::enable_if_t<
|
||||
std::is_object_v<M>, std::invoke_result_t<decltype(Func), U&>()>>;
|
||||
|
||||
template <class R, class T, class... Args, R (*Func)(T, Args...), class U>
|
||||
FunctionRef(nontype_t<Func>, U&&) -> FunctionRef<R(Args...)>;
|
||||
#endif
|
||||
|
||||
ABSL_NAMESPACE_END
|
||||
} // namespace absl
|
||||
|
||||
|
||||
@@ -30,6 +30,11 @@ namespace absl {
|
||||
ABSL_NAMESPACE_BEGIN
|
||||
namespace {
|
||||
|
||||
struct Class {
|
||||
int Func() { return 42; }
|
||||
int CFunc() const { return 43; }
|
||||
};
|
||||
|
||||
int Function() { return 1337; }
|
||||
|
||||
template <typename T>
|
||||
@@ -312,6 +317,7 @@ TEST(FunctionRefTest, CorrectConstQualifiers) {
|
||||
EXPECT_EQ(42, FunctionRef<int()>(s)());
|
||||
EXPECT_EQ(1337, FunctionRef<int() const>(s)());
|
||||
EXPECT_EQ(1337, FunctionRef<int()>(std::as_const(s))());
|
||||
EXPECT_EQ(1337, FunctionRef<int() const>(std::as_const(s))());
|
||||
}
|
||||
|
||||
TEST(FunctionRefTest, Lambdas) {
|
||||
@@ -342,11 +348,46 @@ TEST(FunctionRefTest, Lambdas) {
|
||||
}
|
||||
|
||||
#if ABSL_INTERNAL_CPLUSPLUS_LANG >= 202002L
|
||||
TEST(FunctionRefTest, NonTypeParameter) {
|
||||
EXPECT_EQ(1337, FunctionRef<int()>(nontype<&Function>)());
|
||||
EXPECT_EQ(42, FunctionRef<int()>(nontype<&Copy<int>>, 42)());
|
||||
EXPECT_EQ(42, FunctionRef<int()>(nontype<&Dereference<int>>,
|
||||
&std::integral_constant<int, 42>::value)());
|
||||
static_assert(std::is_same_v<decltype(FunctionRef(nontype<&Class::Func>,
|
||||
std::declval<Class&>())),
|
||||
FunctionRef<int()>>);
|
||||
static_assert(std::is_same_v<decltype(FunctionRef(nontype<&Class::CFunc>,
|
||||
std::declval<Class&>())),
|
||||
FunctionRef<int() const>>);
|
||||
|
||||
static_assert(std::is_same_v<decltype(FunctionRef(nontype<&Class::Func>,
|
||||
std::declval<Class*>())),
|
||||
FunctionRef<int()>>);
|
||||
static_assert(std::is_same_v<decltype(FunctionRef(nontype<&Class::CFunc>,
|
||||
std::declval<Class*>())),
|
||||
FunctionRef<int() const>>);
|
||||
|
||||
TEST(FunctionRefTest, NonTypeParameterWithTemporaries) {
|
||||
static_assert(!std::is_constructible_v<FunctionRef<int()>,
|
||||
nontype_t<&Class::Func>, Class&&>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<FunctionRef<int()>, nontype_t<&Class::Func>,
|
||||
const Class&&>);
|
||||
static_assert(!std::is_constructible_v<FunctionRef<int() const>,
|
||||
nontype_t<&Class::CFunc>, Class&&>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<FunctionRef<int() const>,
|
||||
nontype_t<&Class::CFunc>, const Class&&>);
|
||||
}
|
||||
|
||||
TEST(FunctionRefTest, NonTypeParameterWithDeductionGuides) {
|
||||
EXPECT_EQ(1337, FunctionRef(nontype<&Function>)());
|
||||
EXPECT_EQ(42, FunctionRef(nontype<&Copy<int>>,
|
||||
std::integral_constant<int, 42>::value)());
|
||||
EXPECT_EQ(42, FunctionRef(nontype<&Dereference<int>>,
|
||||
&std::integral_constant<int, 42>::value)());
|
||||
|
||||
Class c;
|
||||
EXPECT_EQ(42, FunctionRef<int()>(nontype<&Class::Func>, c)());
|
||||
EXPECT_EQ(43, FunctionRef<int() const>(nontype<&Class::CFunc>, c)());
|
||||
|
||||
EXPECT_EQ(42, FunctionRef<int()>(nontype<&Class::Func>, &c)());
|
||||
EXPECT_EQ(43, FunctionRef<int() const>(nontype<&Class::CFunc>, &c)());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -81,22 +81,37 @@ R InvokeObject(VoidPtr ptr, typename ForwardT<Args>::type... args) {
|
||||
template <typename Obj, typename Fun, Fun F, typename R, typename... Args>
|
||||
R InvokeObject(VoidPtr ptr, typename ForwardT<Args>::type... args) {
|
||||
using T = std::remove_reference_t<Obj>;
|
||||
return static_cast<R>(
|
||||
F(std::forward<Obj>(*const_cast<T*>(static_cast<const T*>(ptr.obj))),
|
||||
Obj&& obj =
|
||||
std::forward<Obj>(*const_cast<T*>(static_cast<const T*>(ptr.obj)));
|
||||
// Avoid std::invoke() since the callee is a known function at compile time
|
||||
if constexpr (std::is_member_function_pointer_v<Fun>) {
|
||||
return static_cast<R>((std::forward<Obj>(obj).*F)(
|
||||
std::forward<typename ForwardT<Args>::type>(args)...));
|
||||
} else {
|
||||
return static_cast<R>(
|
||||
F(std::forward<Obj>(obj),
|
||||
std::forward<typename ForwardT<Args>::type>(args)...));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename Fun, Fun F, typename R, typename... Args>
|
||||
R InvokePtr(VoidPtr ptr, typename ForwardT<Args>::type... args) {
|
||||
return static_cast<R>(
|
||||
F(const_cast<T*>(static_cast<const T*>(ptr.obj)),
|
||||
std::forward<typename ForwardT<Args>::type>(args)...));
|
||||
T* obj = const_cast<T*>(static_cast<const T*>(ptr.obj));
|
||||
// Avoid std::invoke() since the callee is a known function at compile time
|
||||
if constexpr (std::is_member_function_pointer_v<Fun>) {
|
||||
return static_cast<R>(
|
||||
(obj->*F)(std::forward<typename ForwardT<Args>::type>(args)...));
|
||||
} else {
|
||||
return static_cast<R>(
|
||||
F(obj, std::forward<typename ForwardT<Args>::type>(args)...));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Fun, typename R, typename... Args>
|
||||
R InvokeFunction(VoidPtr ptr, typename ForwardT<Args>::type... args) {
|
||||
auto f = reinterpret_cast<Fun>(ptr.fun);
|
||||
return static_cast<R>(std::invoke(f, std::forward<Args>(args)...));
|
||||
return static_cast<R>(
|
||||
std::invoke(f, std::forward<typename ForwardT<Args>::type>(args)...));
|
||||
}
|
||||
|
||||
template <typename Fun, Fun F, typename R, typename... Args>
|
||||
|
||||
Reference in New Issue
Block a user