// Copyright (C) 2021 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_FUNCTIONAL_Hh_ #define DLIB_FUNCTIONAL_Hh_ #include <functional> #include "type_traits.h" #include "utility.h" namespace dlib { // ---------------------------------------------------------------------------------------- namespace detail { template< typename T > struct is_reference_wrapper : std::false_type {}; template< typename U > struct is_reference_wrapper<std::reference_wrapper<U>> : std::true_type {}; template < typename Base, typename T, typename Derived, typename... Args > constexpr auto invoke_( T Base::*pmf, //pointer to member function Derived&& ref, Args&&... args ) noexcept(noexcept((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...))) -> typename std::enable_if< std::is_function<T>::value && std::is_base_of<Base, typename std::decay<Derived>::type>::value, decltype((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...)) >::type { return (std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...); } template< typename Base, typename T, typename RefWrap, typename... Args > constexpr auto invoke_( T Base::*pmf, //pointer to member function RefWrap&& ref, Args&&... args ) noexcept(noexcept((ref.get().*pmf)(std::forward<Args>(args)...))) -> typename std::enable_if< std::is_function<T>::value && is_reference_wrapper<typename std::decay<RefWrap>::type>::value, decltype((ref.get().*pmf)(std::forward<Args>(args)...))>::type { return (ref.get().*pmf)(std::forward<Args>(args)...); } template< typename Base, typename T, typename Ptr, typename... Args > constexpr auto invoke_( T Base::*pmf, //pointer to member function Ptr&& ptr, Args&&... args ) noexcept(noexcept(((*std::forward<Ptr>(ptr)).*pmf)( std::forward<Args>( args )...))) -> typename std::enable_if< std::is_function<T>::value && !is_reference_wrapper<typename std::decay<Ptr>::type>::value && !std::is_base_of<Base, typename std::decay<Ptr>::type>::value, decltype(((*std::forward<Ptr>(ptr)).*pmf)(std::forward<Args>(args)...))>::type { return ((*std::forward<Ptr>(ptr)).*pmf)( std::forward<Args>( args )...); } template< typename Base, typename T, typename Derived > constexpr auto invoke_( T Base::*pmd, //pointer to member data Derived&& ref ) noexcept(noexcept(std::forward<Derived>(ref).*pmd)) -> typename std::enable_if< std::is_object<T>::value && std::is_base_of<Base, typename std::decay<Derived>::type>::value, decltype(std::forward<Derived>(ref).*pmd)>::type { return std::forward<Derived>(ref).*pmd; } template< typename Base, typename T, typename RefWrap > constexpr auto invoke_( T Base::*pmd, //pointer to member data RefWrap&& ref ) noexcept(noexcept(ref.get().*pmd)) -> typename std::enable_if< std::is_object<T>::value && is_reference_wrapper<typename std::decay<RefWrap>::type>::value, decltype(ref.get().*pmd)>::type { return ref.get().*pmd; } template< typename Base, typename T, typename Ptr > constexpr auto invoke_( T Base::*pmd, //pointer to member data Ptr&& ptr ) noexcept(noexcept((*std::forward<Ptr>(ptr)).*pmd)) -> typename std::enable_if< std::is_object<T>::value && !is_reference_wrapper<typename std::decay<Ptr>::type>::value && !std::is_base_of<Base, typename std::decay<Ptr>::type>::value, decltype((*std::forward<Ptr>(ptr)).*pmd)>::type { return (*std::forward<Ptr>(ptr)).*pmd; } template< typename F, typename... Args > constexpr auto invoke_( F && f, Args&&... args ) noexcept(noexcept(std::forward<F>( f )( std::forward<Args>( args )...))) -> typename std::enable_if< !std::is_member_pointer<typename std::decay<F>::type>::value, decltype(std::forward<F>(f)(std::forward<Args>(args)...))>::type { return std::forward<F>( f )( std::forward<Args>( args )...); } } // end namespace detail // ---------------------------------------------------------------------------------------- template< typename F, typename... Args> constexpr auto invoke(F && f, Args &&... args) /*! ensures - identical to std::invoke(std::forward<F>(f), std::forward<Args>(args)...) - works with C++11 onwards !*/ noexcept(noexcept(detail::invoke_(std::forward<F>( f ), std::forward<Args>( args )...))) -> decltype(detail::invoke_(std::forward<F>( f ), std::forward<Args>( args )...)) { return detail::invoke_(std::forward<F>( f ), std::forward<Args>( args )...); } // ---------------------------------------------------------------------------------------- namespace detail { template<class AlwaysVoid, class F, class... Args> struct invoke_traits { static constexpr bool value{false}; template<class R> using is_convertible = std::false_type; }; template<class F, class... Args> struct invoke_traits<void_t<decltype(dlib::invoke(std::declval<F>(), std::declval<Args>()...))>, F, Args...> { static constexpr bool value{true}; using type = decltype(dlib::invoke(std::declval<F>(), std::declval<Args>()...)); template<class R> using is_convertible = std::is_convertible<type, R>; }; } // end namespace detail // ---------------------------------------------------------------------------------------- template< typename F, typename... Args > struct invoke_result : detail::invoke_traits< void, F, Args...> {}; /*! ensures - identical to std::invoke_result<F, Args..> - works with C++11 onwards !*/ template< typename F, typename... Args > using invoke_result_t = typename invoke_result<F, Args...>::type; /*! ensures - identical to std::invoke_result_t<F, Args..> - works with C++11 onwards !*/ // ---------------------------------------------------------------------------------------- template< typename F, typename... Args > struct is_invocable : std::integral_constant<bool, detail::invoke_traits< void, F, Args...>::value> {}; /*! ensures - identical to std::is_invocable<F, Args..> - works with C++11 onwards !*/ // ---------------------------------------------------------------------------------------- template <typename R, typename F, typename... Args> struct is_invocable_r : std::integral_constant<bool, detail::invoke_traits< void, F, Args...>::template is_convertible<R>::value> {}; /*! ensures - identical to std::is_invocable_r<R, F, Args..> - works with C++11 onwards !*/ // ---------------------------------------------------------------------------------------- template< typename R, typename F, typename... Args> constexpr typename std::enable_if<dlib::is_invocable_r<R, F, Args...>::value, R>::type invoke_r(F && f, Args &&... args) /*! ensures - identical to std::invoke_r<R>(std::forward<F>(f), std::forward<Args>(args)...) - works with C++11 onwards !*/ noexcept(noexcept(dlib::invoke(std::forward<F>( f ), std::forward<Args>( args )...))) { return dlib::invoke(std::forward<F>( f ), std::forward<Args>( args )...); } // ---------------------------------------------------------------------------------------- namespace detail { template<typename F, typename Tuple, std::size_t... I> constexpr auto apply_impl(F&& fn, Tuple&& tpl, index_sequence<I...>) noexcept(noexcept(dlib::invoke(std::forward<F>(fn), std::get<I>(std::forward<Tuple>(tpl))...))) -> decltype(dlib::invoke(std::forward<F>(fn), std::get<I>(std::forward<Tuple>(tpl))...)) { return dlib::invoke(std::forward<F>(fn), std::get<I>(std::forward<Tuple>(tpl))...); } } // end namespace detail // ---------------------------------------------------------------------------------------- template<typename F, typename Tuple> constexpr auto apply(F&& fn, Tuple&& tpl) /*! ensures - identical to std::apply(std::forward<F>(f), std::forward<Tuple>(tpl)) - works with C++11 onwards !*/ noexcept(noexcept(detail::apply_impl(std::forward<F>(fn), std::forward<Tuple>(tpl), make_index_sequence<std::tuple_size<typename std::remove_reference<Tuple>::type >::value>{}))) -> decltype(detail::apply_impl(std::forward<F>(fn), std::forward<Tuple>(tpl), make_index_sequence<std::tuple_size<typename std::remove_reference<Tuple>::type >::value>{})) { return detail::apply_impl(std::forward<F>(fn), std::forward<Tuple>(tpl), make_index_sequence<std::tuple_size<typename std::remove_reference<Tuple>::type >::value>{}); } // ---------------------------------------------------------------------------------------- namespace detail { template <class T, class Tuple, std::size_t... I> constexpr T make_from_tuple_impl( Tuple&& t, index_sequence<I...> ) { return T(std::get<I>(std::forward<Tuple>(t))...); } } // end namespace detail // ---------------------------------------------------------------------------------------- template <class T, class Tuple> constexpr T make_from_tuple( Tuple&& t ) /*! ensures - identical to std::make_from_tuple<T>(std::forward<Tuple>(t)) - works with C++11 onwards !*/ { return detail::make_from_tuple_impl<T>(std::forward<Tuple>(t), make_index_sequence<std::tuple_size<typename std::remove_reference<Tuple>::type >::value>{}); } // ---------------------------------------------------------------------------------------- namespace detail { template<class Binder, std::size_t ...I, class ...Rest> constexpr static auto binder_run( Binder&& self, std::integral_constant<bool, false>, index_sequence<I...>, Rest&&... rest ) noexcept(noexcept(dlib::invoke(std::forward<Binder>(self).func, std::get<I>(std::forward<Binder>(self).args)..., std::forward<Rest>(rest)...))) -> decltype(dlib::invoke(std::forward<Binder>(self).func, std::get<I>(std::forward<Binder>(self).args)..., std::forward<Rest>(rest)...)) { return dlib::invoke(std::forward<Binder>(self).func, std::get<I>(std::forward<Binder>(self).args)..., std::forward<Rest>(rest)...); } template<class Binder, std::size_t ...I, class ...Rest> constexpr static auto binder_run( Binder&& self, std::integral_constant<bool, true>, index_sequence<I...>, Rest&&... rest ) noexcept(noexcept(dlib::invoke(std::forward<Binder>(self).func, std::forward<Rest>(rest)..., std::get<I>(std::forward<Binder>(self).args)...))) -> decltype(dlib::invoke(std::forward<Binder>(self).func, std::forward<Rest>(rest)..., std::get<I>(std::forward<Binder>(self).args)...)) { return dlib::invoke(std::forward<Binder>(self).func, std::forward<Rest>(rest)..., std::get<I>(std::forward<Binder>(self).args)...); } template<bool Back, class F, class ...Args> struct binder_wrapper { F func; std::tuple<Args...> args; template<class ...Rest> constexpr auto operator()(Rest&&... rest) noexcept(noexcept(binder_run(std::declval<binder_wrapper&>(), std::integral_constant<bool, Back>{}, index_sequence_for<Args...>{}, std::forward<Rest>(rest)...))) -> decltype(binder_run(std::declval<binder_wrapper&>(), std::integral_constant<bool, Back>{}, index_sequence_for<Args...>{}, std::forward<Rest>(rest)...)) { return binder_run(*this, std::integral_constant<bool, Back>{}, index_sequence_for<Args...>{}, std::forward<Rest>(rest)...); } }; template<bool Back, class F, class... Args> constexpr auto make_binder( F&& func, Args&&... args ) { return binder_wrapper<Back, std::decay_t<F>, std::decay_t<Args>...>{std::forward<F>(func), std::make_tuple(std::forward<Args>(args)...)}; } } template<class F, class ...Args> constexpr auto bind_front( F&& func, Args&&... args ) noexcept(noexcept(detail::make_binder<false>(std::forward<F>(func), std::forward<Args>(args)...))) /*! ensures - identical to std::bind_front() - works with C++11 onwards !*/ { return detail::make_binder<false>(std::forward<F>(func), std::forward<Args>(args)...); } template<class F, class ...Args> constexpr auto bind_back( F&& func, Args&&... args ) noexcept(noexcept(detail::make_binder<true>(std::forward<F>(func), std::forward<Args>(args)...))) /*! ensures - identical to std::bind_back() - works with C++11 onwards !*/ { return detail::make_binder<true>(std::forward<F>(func), std::forward<Args>(args)...); } // ---------------------------------------------------------------------------------------- } #endif //DLIB_FUNCTIONAL_Hh_