// 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 #include #include #include // 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 static std::true_type is_refwrap_test(const std::reference_wrapper&); template static std::false_type is_refwrap_test(const U&); template struct is_reference_wrapper : decltype(is_refwrap_test(std::declval())) { }; template ::type> struct unwrap_reference_wrapper { using type = T; }; template struct unwrap_reference_wrapper> { using type = U&; }; template using unwrap_reference_wrapper_t = typename unwrap_reference_wrapper::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 struct is_const_member_function : std::false_type { }; // decay doesn't add pointer to abominable functions, don't bother writing them template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_const_member_function : std::true_type { }; template struct is_volatile_member_function : std::false_type { }; // decay doesn't add pointer to abominable functions, don't bother writing them template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template struct is_volatile_member_function : std::true_type { }; template 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::type, Object>::value || ((is_volatile_member_function::value == std::is_volatile::value) && (is_const_member_function::value == std::is_const::value)); }; // pointer to member function - reference to object template < typename Base, typename T, typename Derived, typename... Args, typename Unwrapped = unwrap_reference_wrapper_t, typename std::enable_if< is_function::value && has_correct_cv::type, T>::value && std::is_base_of::type>::value, int>::type = 0> inline auto invoke_impl(T Base::*pmf, Derived&& ref, Args&&... args) FPLUS_INVOKE_RETURN((std::forward(ref).* pmf)(std::forward(args)...)) // pointer to member function - pointer to object template < typename Base, typename T, typename Pointer, typename... Args, typename std::enable_if< is_function::value && has_correct_cv::type>::type, T>::value && !std::is_base_of::type>::value, int>::type = 0> inline auto invoke_impl(T Base::*pmf, Pointer&& ptr, Args&&... args) FPLUS_INVOKE_RETURN(((*std::forward(ptr)).* pmf)(std::forward(args)...)) // pointer to non-static data member - reference to object template < typename Base, typename T, typename Derived, typename Unwrapped = unwrap_reference_wrapper_t, typename std::enable_if< !is_function::value && std::is_base_of::type>::value, int>::type = 0> inline auto invoke_impl(T Base::*pmd, Derived&& ref) FPLUS_INVOKE_RETURN((std::forward(ref).*pmd)) // pointer to non-static data member - pointer to object template < typename Base, typename T, typename Pointer, typename std::enable_if< !is_function::value && !std::is_base_of::type>::value, int>::type = 0> inline auto invoke_impl(T Base::*pmd, Pointer&& ptr) FPLUS_INVOKE_RETURN((*std::forward(ptr)).*pmd) // normal case - functions, lambdas, function objects template ::type>::value, int>::type = 0> inline auto invoke_impl(F&& f, Args&&... args) FPLUS_INVOKE_RETURN((std::forward(f)(std::forward(args)...))) template struct invoke_result_impl { }; template struct invoke_result_impl(), std::declval()...))), F, Args...> { using type = decltype(invoke_impl(std::declval(), std::declval()...)); }; template struct invoke_result : invoke_result_impl { }; template using invoke_result_t = typename invoke_result::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 invoke_result_t invoke(F&& f, ArgTypes&&... args) { return invoke_impl(std::forward(f), std::forward(args)...); } // Invoke useful traits (libstdc++ 7.1.0's implementation, ugly-case removed) template struct is_invocable_impl : std::false_type { }; template struct is_invocable_impl> : disjunction, std::is_convertible>::type { }; template struct is_invocable : is_invocable_impl, void>::type { }; template struct is_invocable_r : is_invocable_impl, ReturnType>::type { }; } } #undef FPLUS_INVOKE_RETURN