228 lines
7.5 KiB
C++
228 lines
7.5 KiB
C++
/*!
|
|
* \file utl/utility/invoke.h
|
|
* \brief invoke() and invoke traits implementation
|
|
*/
|
|
#ifndef __utl_utility_invoke_h__
|
|
#define __utl_utility_invoke_h__
|
|
|
|
#include <utl/core/impl.h>
|
|
#include <utl/meta/meta.h>
|
|
|
|
#include <type_traits>
|
|
#include <functional>
|
|
#include <utility>
|
|
|
|
//! \defgroup utility Utility
|
|
|
|
/*!
|
|
* \ingroup utility
|
|
* \defgroup util_invoke Invoke
|
|
*/
|
|
//! @{
|
|
namespace utl {
|
|
|
|
#if !defined __cpp_lib_is_invocable
|
|
namespace detail {
|
|
|
|
template <class T>
|
|
struct is_ref_wrapper : meta::false_ {};
|
|
template <class U>
|
|
struct is_ref_wrapper<std::reference_wrapper<U>> : meta::true_ {};
|
|
|
|
// 1
|
|
template <class T, class Type, class T1, class... Args,
|
|
meta::enable_if_t<
|
|
std::is_member_function_pointer<std::decay_t<Type T::*>>::value &&
|
|
std::is_base_of<T, std::decay_t<T1>>::value,
|
|
int> =0
|
|
>
|
|
decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) {
|
|
return (std::forward<T1>(t1).*f)(std::forward<Args>(args)...);
|
|
}
|
|
|
|
// 2
|
|
template <class T, class Type, class T1, class... Args,
|
|
meta::enable_if_t<
|
|
std::is_member_function_pointer<std::decay_t<Type T::*>>::value &&
|
|
is_ref_wrapper<std::decay_t<T1>>::value,
|
|
int> =0
|
|
>
|
|
decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) {
|
|
return (t1.get().*f)(std::forward<Args>(args)...);
|
|
}
|
|
|
|
// 3
|
|
template <class T, class Type, class T1, class... Args,
|
|
meta::enable_if_t<
|
|
std::is_member_function_pointer<std::decay_t<Type T::*>>::value &&
|
|
!std::is_base_of<T, std::decay_t<T1>>::value &&
|
|
!is_ref_wrapper<std::decay_t<T1>>::value,
|
|
int> =0
|
|
>
|
|
decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) {
|
|
return ((*std::forward<T1>(t1)).*f)(std::forward<Args>(args)...);
|
|
}
|
|
|
|
// 4
|
|
template <class T, class Type, class T1, class... Args,
|
|
meta::enable_if_t<
|
|
std::is_member_object_pointer<std::decay_t<Type T::*>>::value &&
|
|
std::is_base_of<T, std::decay_t<T1>>::value,
|
|
int> =0
|
|
>
|
|
decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) {
|
|
return std::forward<T1>(t1).*f;
|
|
}
|
|
|
|
// 5
|
|
template <class T, class Type, class T1, class... Args,
|
|
meta::enable_if_t<
|
|
std::is_member_object_pointer<std::decay_t<Type T::*>>::value &&
|
|
is_ref_wrapper<std::decay_t<T1>>::value,
|
|
int> =0
|
|
>
|
|
decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) {
|
|
return t1.get().*f;
|
|
}
|
|
|
|
// 6
|
|
template <class T, class Type, class T1, class... Args,
|
|
meta::enable_if_t<
|
|
std::is_member_object_pointer<std::decay_t<Type T::*>>::value &&
|
|
!std::is_base_of<T, std::decay_t<T1>>::value &&
|
|
!is_ref_wrapper<std::decay_t<T1>>::value,
|
|
int> =0
|
|
>
|
|
decltype(auto) invoke_impl_(Type T::* f, T1&& t1, Args&&... args) {
|
|
return (*std::forward<T1>(t1)).*f;
|
|
}
|
|
|
|
template <class F, class... Args>
|
|
decltype(auto) invoke_impl_(F&& f, Args&&... args) {
|
|
return std::forward<F>(f)(std::forward<Args>(args)...);
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
//! Invoke the Callable object \c fn with the parameters args.
|
|
//! As by INVOKE(std::forward<F>(f), std::forward<Args>(args)...).
|
|
//!
|
|
//! \note
|
|
//! This implementation fills the lack of an invoke() utility for builds
|
|
//! pre-c++17.
|
|
//!
|
|
//! \param fn Callable object to be invoked
|
|
//! \param args Arguments to pass to \c fn
|
|
//! \return The return of the Callable underling functionality
|
|
//!
|
|
template<typename Callable, typename... Args>
|
|
inline decltype(auto) invoke(Callable&& fn, Args&&... args) {
|
|
return detail::invoke_impl_(
|
|
std::forward<Callable>(fn), std::forward<Args>(args)...
|
|
);
|
|
}
|
|
|
|
//!
|
|
//! \brief
|
|
//! Determines whether \c F can be invoked with the arguments \c Args....
|
|
//!
|
|
//! Formally, determines whether invoke(declval<Fn>(), declval<ArgTypes>()...)
|
|
//! is well formed when treated as an unevaluated operand,
|
|
//! where invoke is \c Callable.
|
|
//!
|
|
//! \note
|
|
//! This implementation fills the lack of an invoke() utility for builds
|
|
//! pre-c++17.
|
|
//!
|
|
//! \tparam F The candidate type to check if its invocable
|
|
//! \tparam Args The arguments for the call
|
|
//! \return If \c F is invocable
|
|
//! \arg true Is invocable
|
|
//! \arg false Is not invocable
|
|
template <typename F, typename... Args>
|
|
struct is_invocable :
|
|
std::is_constructible<
|
|
std::function<void(Args ...)>,
|
|
std::reference_wrapper<typename std::remove_reference<F>::type>
|
|
> { };
|
|
|
|
//! \brief
|
|
//! Determines whether \c F can be invoked with the arguments \c Args...
|
|
//! to yield a result that is convertible to \c R.
|
|
//!
|
|
//! Formally, determines whether invoke(declval<Fn>(), declval<ArgTypes>()...)
|
|
//! is well formed when treated as an unevaluated operand, where invoke is \c Callable.
|
|
//!
|
|
//! \tparam R The return type of invocable functionality
|
|
//! \tparam F The candidate type to check if its invocable
|
|
//! \tparam Args The arguments to pass to \c F
|
|
//! \return If \c F is invocable
|
|
//! \arg true Is invocable
|
|
//! \arg false Is not invocable
|
|
template <typename R, typename F, typename... Args>
|
|
struct is_invocable_r :
|
|
std::is_constructible<
|
|
std::function<R(Args ...)>,
|
|
std::reference_wrapper<typename std::remove_reference<F>::type>
|
|
> { };
|
|
|
|
/*!
|
|
* invoke_result (SFINAE friendly)
|
|
*/
|
|
//! @{
|
|
namespace detail {
|
|
template<typename Callable, typename... Args>
|
|
struct try_invoke {
|
|
using type = decltype (
|
|
detail::invoke_impl_(std::declval<Callable&&>(), std::declval<Args&&>()...)
|
|
);
|
|
};
|
|
|
|
template<bool B, typename Callable, typename... Args>
|
|
struct invoke_result_ {
|
|
using type = meta::nil_;
|
|
};
|
|
|
|
template <typename Callable, typename... Args>
|
|
struct invoke_result_ <true, Callable, Args...> {
|
|
using type = meta::invoke_t<
|
|
meta::quote<try_invoke>, Callable, Args...
|
|
>;
|
|
};
|
|
}
|
|
//! trait that deduces the return type of an INVOKE expression at compile time.
|
|
//!
|
|
//! \tparam Callable The candidate type to check if its invocable
|
|
//! \tparam Args The arguments to pass to \c F
|
|
//!
|
|
//! \b member \n
|
|
//! \::type The return type of the \c Callable type if invoked with the arguments Args....
|
|
template <typename Callable, typename... Args>
|
|
using invoke_result = detail::invoke_result_<
|
|
is_invocable<Callable, Args...>::value,
|
|
Callable,
|
|
Args...
|
|
>;
|
|
|
|
//! trait that deduces the return type of an INVOKE expression at compile time.
|
|
//!
|
|
//! \tparam Callable The candidate type to check if its invocable
|
|
//! \tparam Args The arguments to pass to \c F
|
|
//!
|
|
//! \return The type of the \c Callable type if invoked with the arguments Args....
|
|
template<typename Callable, typename... Args>
|
|
using invoke_result_t = meta::eval <
|
|
invoke_result<Callable, Args...>
|
|
>;
|
|
//! @}
|
|
|
|
#else
|
|
|
|
#endif
|
|
}
|
|
|
|
//! @}
|
|
|
|
|
|
#endif /* __utl_utility_invoke_h__ */
|