From a4bf327b5398e79bb675809346892c1c9dce1370 Mon Sep 17 00:00:00 2001 From: Marcin Kowalczyk Date: Thu, 24 Oct 2024 15:11:39 +0200 Subject: [PATCH] Make `InvokerType::Result` present conditionally instead of malformed depending on whether the function is callable with the given arguments, so that the presence of `Result` and `ConstResult` can be used to detect usability of the given instantiation. Implement conditional functionality by class template specializations instead of by taking a `bool is_const_callable` template parameter and supplying its value in the usage later. This feels cleaner. Add SFINAE constraints to `{,Owning}Invoker()`. PiperOrigin-RevId: 689352893 --- riegeli/base/invoker.h | 76 +++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/riegeli/base/invoker.h b/riegeli/base/invoker.h index 66c1cfa4..b5922765 100644 --- a/riegeli/base/invoker.h +++ b/riegeli/base/invoker.h @@ -56,7 +56,18 @@ decltype(std::declval()()) Invoke(Function&& function) { return std::forward(function)(); } +template +struct IsCallableImpl : std::false_type {}; + template +struct IsCallableImpl(), std::declval()...))>, + Function, Args...> : std::true_type {}; + +template +struct IsCallable : IsCallableImpl {}; + +template class InvokerBase : public ConditionallyAssignable>...>::value> { public: @@ -123,25 +134,18 @@ class InvokerBase : public ConditionallyAssignable args_; }; -template -struct IsConstCallableImpl : std::false_type {}; - -template -struct IsConstCallableImpl< - absl::void_t(), std::declval()...))>, - Function, Args...> : std::true_type {}; - +// Disable functionality when the function is not callable with the arguments. template -struct IsConstCallable : IsConstCallableImpl {}; +class InvokerBase::value>, + Function, Args...> {}; -template -class InvokerConstBase; - -template -class InvokerConstBase - : public InvokerBase { +template +class InvokerConstBase : public InvokerBase { public: + // The result of calling `const InvokerType&` with no arguments. + using ConstResult = decltype(invoker_internal::Invoke( + std::declval(), std::declval()...)); + using InvokerConstBase::InvokerBase::InvokerBase; InvokerConstBase(InvokerConstBase&& that) = default; @@ -149,11 +153,21 @@ class InvokerConstBase InvokerConstBase(const InvokerConstBase& that) = default; InvokerConstBase& operator=(const InvokerConstBase& that) = default; + + // Calls the function. + using InvokerConstBase::InvokerBase::operator typename InvokerConstBase:: + Result; + /*implicit*/ operator ConstResult() const& { + return absl::apply(this->function(), this->args()); + } }; +// Disable const functionality when the const function is not callable with the +// const arguments. template -class InvokerConstBase - : public InvokerBase { +class InvokerConstBase< + std::enable_if_t::value>, + Function, Args...> : public InvokerBase { public: using InvokerConstBase::InvokerBase::InvokerBase; @@ -162,17 +176,6 @@ class InvokerConstBase InvokerConstBase(const InvokerConstBase& that) = default; InvokerConstBase& operator=(const InvokerConstBase& that) = default; - - // The result of calling `const InvokerType&` with no arguments. - using ConstResult = decltype(invoker_internal::Invoke( - std::declval(), std::declval()...)); - - // Calls the function. - using InvokerConstBase::InvokerBase::operator typename InvokerConstBase:: - Result; - /*implicit*/ operator ConstResult() const& { - return absl::apply(this->function(), this->args()); - } }; } // namespace invoker_internal @@ -193,9 +196,7 @@ class InvokerConstBase // the target reference, reference wrapper, or pointer. template class InvokerType - : public invoker_internal::InvokerConstBase< - invoker_internal::IsConstCallable::value, Function, - Args...> { + : public invoker_internal::InvokerConstBase { public: using InvokerType::InvokerConstBase::InvokerConstBase; @@ -277,7 +278,10 @@ using InvokerResultT = typename InvokerResult::type; // storing a `InvokerType` in a variable or returning it from a function, use // `riegeli::OwningInvoker(function, args...)` or construct `InvokerType` // directly. -template +template < + typename Function, typename... Args, + std::enable_if_t::value, + int> = 0> inline InvokerType Invoker( Function&& function ABSL_ATTRIBUTE_LIFETIME_BOUND, Args&&... args ABSL_ATTRIBUTE_LIFETIME_BOUND) { @@ -287,7 +291,11 @@ inline InvokerType Invoker( // `riegeli::OwningInvoker()` is like `riegeli::Invoker()`, but the arguments // are stored by value instead of by reference. This is useful for storing the // `InvokerType` in a variable or returning it from a function. -template +template < + typename Function, typename... Args, + std::enable_if_t, + std::decay_t...>::value, + int> = 0> inline InvokerType, std::decay_t...> OwningInvoker( Function&& function, Args&&... args) { return {std::forward(function), std::forward(args)...};