rec/fplus/internal/invoke.hpp
2020-03-18 14:42:46 +08:00

276 lines
8.3 KiB
C++
Executable File

// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
// https://github.com/Dobiasd/FunctionalPlus
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#pragma once
#include <functional>
#include <type_traits>
#include <utility>
#include <fplus/internal/meta.hpp>
// borrowed to libc++
#define FPLUS_INVOKE_RETURN(...) \
->decltype(__VA_ARGS__) \
{ \
return __VA_ARGS__; \
}
namespace fplus
{
namespace internal
{
// We need std::invoke to detect callable objects
//
// source:
// http://en.cppreference.com/mwiki/index.php?title=cpp/utility/functional/invoke&oldid=82514
template <typename U>
static std::true_type is_refwrap_test(const std::reference_wrapper<U>&);
template <typename U>
static std::false_type is_refwrap_test(const U&);
template <typename T>
struct is_reference_wrapper : decltype(is_refwrap_test(std::declval<T>()))
{
};
template <typename T, typename U = typename std::decay<T>::type>
struct unwrap_reference_wrapper
{
using type = T;
};
template <typename T, typename U>
struct unwrap_reference_wrapper<T, std::reference_wrapper<U>>
{
using type = U&;
};
template <typename T>
using unwrap_reference_wrapper_t = typename unwrap_reference_wrapper<T>::type;
// note: clang only triggers the second static_assert
// - static_assert(is_invocable<&base_class::non_const_method, const derived_class&>::value, "");
// - static_assert(is_invocable<&base_class::non_const_method, const base_class&>::value, "");
// GCC triggers both. To workaround this clang bug, we have to manage cv correctness ourselves
template <typename T>
struct is_const_member_function : std::false_type
{
};
// decay doesn't add pointer to abominable functions, don't bother writing them
template <typename R, typename... Args>
struct is_const_member_function<R(Args...) const> : std::true_type
{
};
template <typename R, typename... Args>
struct is_const_member_function<R(Args...) const&> : std::true_type
{
};
template <typename R, typename... Args>
struct is_const_member_function<R(Args...) const&&> : std::true_type
{
};
template <typename R, typename... Args>
struct is_const_member_function<R(Args...) const volatile> : std::true_type
{
};
template <typename R, typename... Args>
struct is_const_member_function<R(Args...) const volatile&> : std::true_type
{
};
template <typename R, typename... Args>
struct is_const_member_function<R(Args...) const volatile&&> : std::true_type
{
};
template <typename T>
struct is_volatile_member_function : std::false_type
{
};
// decay doesn't add pointer to abominable functions, don't bother writing them
template <typename R, typename... Args>
struct is_volatile_member_function<R(Args...) volatile> : std::true_type
{
};
template <typename R, typename... Args>
struct is_volatile_member_function<R(Args...) volatile&> : std::true_type
{
};
template <typename R, typename... Args>
struct is_volatile_member_function<R(Args...) volatile&&> : std::true_type
{
};
template <typename R, typename... Args>
struct is_volatile_member_function<R(Args...) const volatile> : std::true_type
{
};
template <typename R, typename... Args>
struct is_volatile_member_function<R(Args...) const volatile&> : std::true_type
{
};
template <typename R, typename... Args>
struct is_volatile_member_function<R(Args...) const volatile&&> : std::true_type
{
};
template <typename Object, typename Signature>
struct has_correct_cv
{
// if object has no cv, every method can be called
// else the method must have the same cv than the object
static constexpr bool value =
std::is_same<typename std::remove_cv<Object>::type, Object>::value ||
((is_volatile_member_function<Signature>::value ==
std::is_volatile<Object>::value) &&
(is_const_member_function<Signature>::value ==
std::is_const<Object>::value));
};
// pointer to member function - reference to object
template <
typename Base,
typename T,
typename Derived,
typename... Args,
typename Unwrapped = unwrap_reference_wrapper_t<Derived>,
typename std::enable_if<
is_function<T>::value &&
has_correct_cv<typename std::remove_reference<Unwrapped>::type, T>::value &&
std::is_base_of<Base, typename std::decay<Unwrapped>::type>::value,
int>::type = 0>
inline auto invoke_impl(T Base::*pmf, Derived&& ref, Args&&... args)
FPLUS_INVOKE_RETURN((std::forward<Unwrapped>(ref).*
pmf)(std::forward<Args>(args)...))
// pointer to member function - pointer to object
template <
typename Base,
typename T,
typename Pointer,
typename... Args,
typename std::enable_if<
is_function<T>::value &&
has_correct_cv<typename std::remove_pointer<
typename std::decay<Pointer>::type>::type,
T>::value &&
!std::is_base_of<Base, typename std::decay<Pointer>::type>::value,
int>::type = 0>
inline auto invoke_impl(T Base::*pmf, Pointer&& ptr, Args&&... args)
FPLUS_INVOKE_RETURN(((*std::forward<Pointer>(ptr)).*
pmf)(std::forward<Args>(args)...))
// pointer to non-static data member - reference to object
template <
typename Base,
typename T,
typename Derived,
typename Unwrapped = unwrap_reference_wrapper_t<Derived>,
typename std::enable_if<
!is_function<T>::value &&
std::is_base_of<Base, typename std::decay<Unwrapped>::type>::value,
int>::type = 0>
inline auto invoke_impl(T Base::*pmd, Derived&& ref)
FPLUS_INVOKE_RETURN((std::forward<Unwrapped>(ref).*pmd))
// pointer to non-static data member - pointer to object
template <
typename Base,
typename T,
typename Pointer,
typename std::enable_if<
!is_function<T>::value &&
!std::is_base_of<Base, typename std::decay<Pointer>::type>::value,
int>::type = 0>
inline auto invoke_impl(T Base::*pmd, Pointer&& ptr)
FPLUS_INVOKE_RETURN((*std::forward<Pointer>(ptr)).*pmd)
// normal case - functions, lambdas, function objects
template <typename F,
typename... Args,
typename std::enable_if<
!std::is_member_pointer<typename std::decay<F>::type>::value,
int>::type = 0>
inline auto invoke_impl(F&& f, Args&&... args)
FPLUS_INVOKE_RETURN((std::forward<F>(f)(std::forward<Args>(args)...)))
template <typename AlwaysVoid, typename, typename...>
struct invoke_result_impl
{
};
template <typename F, typename... Args>
struct invoke_result_impl<decltype(void(invoke_impl(std::declval<F>(),
std::declval<Args>()...))),
F,
Args...>
{
using type =
decltype(invoke_impl(std::declval<F>(), std::declval<Args>()...));
};
template <typename F, typename... ArgTypes>
struct invoke_result : invoke_result_impl<void, F, ArgTypes...>
{
};
template <typename F, typename... Args>
using invoke_result_t = typename invoke_result<F, Args...>::type;
// noexcept omitted on purpose, cannot be implemented without C++17.
// GCC 7.1 works with libstdc++, but clang fails, even with latest build,
// on both libstdc++/libc++, I suspect an internal compiler trait is at
// play to make GCC work.
//
// We could detect if C++17 is used and use std::invoke directly.
template <typename F, typename... ArgTypes>
invoke_result_t<F, ArgTypes...> invoke(F&& f, ArgTypes&&... args)
{
return invoke_impl(std::forward<F>(f), std::forward<ArgTypes>(args)...);
}
// Invoke useful traits (libstdc++ 7.1.0's implementation, ugly-case removed)
template <typename Result, typename ReturnType, typename = void>
struct is_invocable_impl : std::false_type
{
};
template <typename Result, typename ReturnType>
struct is_invocable_impl<Result, ReturnType, void_t<typename Result::type>>
: disjunction<std::is_void<ReturnType>,
std::is_convertible<typename Result::type, ReturnType>>::type
{
};
template <typename F, typename... ArgTypes>
struct is_invocable
: is_invocable_impl<invoke_result<F, ArgTypes...>, void>::type
{
};
template <typename ReturnType, typename F, typename... ArgTypes>
struct is_invocable_r
: is_invocable_impl<invoke_result<F, ArgTypes...>, ReturnType>::type
{
};
}
}
#undef FPLUS_INVOKE_RETURN