276 lines
8.3 KiB
C++
Executable File
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
|