utl/include/utl/utility/invoke.h

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__ */