// // Copyright (c) 2017-2021 Martin Moene // // https://github.com/martinmoene/invoke-lite // // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef NONSTD_INVOKE_LITE_HPP #define NONSTD_INVOKE_LITE_HPP #define invoke_lite_MAJOR 0 #define invoke_lite_MINOR 0 #define invoke_lite_PATCH 0 #define invoke_lite_VERSION invoke_STRINGIFY(invoke_lite_MAJOR) "." invoke_STRINGIFY(invoke_lite_MINOR) "." invoke_STRINGIFY(invoke_lite_PATCH) #define invoke_STRINGIFY( x ) invoke_STRINGIFY_( x ) #define invoke_STRINGIFY_( x ) #x // invoke-lite configuration: #define invoke_INVOKE_DEFAULT 0 #define invoke_INVOKE_NONSTD 1 #define invoke_INVOKE_STD 2 // tweak header support: #ifdef __has_include # if __has_include() # include # endif #define invoke_HAVE_TWEAK_HEADER 1 #else #define invoke_HAVE_TWEAK_HEADER 0 //# pragma message("invoke.hpp: Note: Tweak header not supported.") #endif // invoke selection and configuration: #ifndef invoke_CONFIG_SELECT_INVOKE # define invoke_CONFIG_SELECT_INVOKE invoke_INVOKE_DEFAULT #endif // Control presence of exception handling (try and auto discover): #ifndef invoke_CONFIG_NO_EXCEPTIONS # if defined(_MSC_VER) # include // for _HAS_EXCEPTIONS # endif # if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) # define invoke_CONFIG_NO_EXCEPTIONS 0 # else # define invoke_CONFIG_NO_EXCEPTIONS 1 # endif #endif // C++ language version detection (C++23 is speculative): // Note: VC14.0/1900 (VS2015) lacks too much from C++14. #ifndef invoke_CPLUSPLUS # if defined(_MSVC_LANG ) && !defined(__clang__) # define invoke_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) # else # define invoke_CPLUSPLUS __cplusplus # endif #endif #define invoke_CPP98_OR_GREATER ( invoke_CPLUSPLUS >= 199711L ) #define invoke_CPP11_OR_GREATER ( invoke_CPLUSPLUS >= 201103L ) #define invoke_CPP14_OR_GREATER ( invoke_CPLUSPLUS >= 201402L ) #define invoke_CPP17_OR_GREATER ( invoke_CPLUSPLUS >= 201703L ) #define invoke_CPP20_OR_GREATER ( invoke_CPLUSPLUS >= 202002L ) // Use C++17 std::invoke if available and requested: #if invoke_CPP17_OR_GREATER # define invoke_HAVE_STD_INVOKE 1 #else # define invoke_HAVE_STD_INVOKE 0 #endif #define invoke_USES_STD_INVOKE ( (invoke_CONFIG_SELECT_INVOKE == invoke_INVOKE_STD) || ((invoke_CONFIG_SELECT_INVOKE == invoke_INVOKE_DEFAULT) && invoke_HAVE_STD_INVOKE) ) // Allow for #pragma message variation: #ifdef _MSC_VER # define invoke_lpar ( # define invoke_rpar ) #else # define invoke_lpar # define invoke_rpar #endif // // Use standard C++17 version: // #if invoke_USES_STD_INVOKE #pragma message invoke_lpar "*** Using std::invoke" invoke_rpar #include namespace nonstd { using std::invoke; } #else // invoke_USES_STD_INVOKE #pragma message invoke_lpar "*** Using nonstd::invoke - C++98/C++11" invoke_rpar // // C++11, code taken from http://en.cppreference.com/w/cpp/types/result_of: // // Compiler versions: // // MSVC++ 6.0 _MSC_VER == 1200 invoke_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0) // MSVC++ 7.0 _MSC_VER == 1300 invoke_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002) // MSVC++ 7.1 _MSC_VER == 1310 invoke_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003) // MSVC++ 8.0 _MSC_VER == 1400 invoke_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005) // MSVC++ 9.0 _MSC_VER == 1500 invoke_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008) // MSVC++ 10.0 _MSC_VER == 1600 invoke_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010) // MSVC++ 11.0 _MSC_VER == 1700 invoke_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012) // MSVC++ 12.0 _MSC_VER == 1800 invoke_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013) // MSVC++ 14.0 _MSC_VER == 1900 invoke_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) // MSVC++ 14.1 _MSC_VER >= 1910 invoke_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) // MSVC++ 14.2 _MSC_VER >= 1920 invoke_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) #if defined(_MSC_VER ) && !defined(__clang__) # define invoke_COMPILER_MSVC_VER (_MSC_VER ) # define invoke_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) #else # define invoke_COMPILER_MSVC_VER 0 # define invoke_COMPILER_MSVC_VERSION 0 #endif #define invoke_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) #if defined(__clang__) # define invoke_COMPILER_CLANG_VERSION invoke_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) #else # define invoke_COMPILER_CLANG_VERSION 0 #endif #if defined(__GNUC__) && !defined(__clang__) # define invoke_COMPILER_GNUC_VERSION invoke_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #else # define invoke_COMPILER_GNUC_VERSION 0 #endif // half-open range [lo..hi): #define invoke_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) // Presence of language and library features: #define invoke_HAVE( feature ) ( invoke_HAVE_##feature ) #ifdef _HAS_CPP0X # define invoke_HAS_CPP0X _HAS_CPP0X #else # define invoke_HAS_CPP0X 0 #endif #define invoke_CPP11_90 (invoke_CPP11_OR_GREATER || invoke_COMPILER_MSVC_VER >= 1500) #define invoke_CPP11_100 (invoke_CPP11_OR_GREATER || invoke_COMPILER_MSVC_VER >= 1600) #define invoke_CPP11_120 (invoke_CPP11_OR_GREATER || invoke_COMPILER_MSVC_VER >= 1800) #define invoke_CPP11_140 (invoke_CPP11_OR_GREATER || invoke_COMPILER_MSVC_VER >= 1900) #define invoke_CPP11_000 (invoke_CPP11_OR_GREATER) #define invoke_CPP14_000 (invoke_CPP14_OR_GREATER) #define invoke_CPP17_000 (invoke_CPP17_OR_GREATER) #define invoke_CPP20_000 (invoke_CPP20_OR_GREATER) // Presence of C++11 language features: #define invoke_HAVE_CONSTEXPR_11 invoke_CPP11_140 #define invoke_HAVE_NOEXCEPT invoke_CPP11_140 #define invoke_HAVE_TYPE_TRAITS invoke_CPP11_90 #define invoke_HAVE_TR1_TYPE_TRAITS (!! invoke_COMPILER_GNUC_VERSION ) // Presence of C++14 language features: #define invoke_HAVE_CONSTEXPR_14 invoke_CPP14_000 // Presence of C++17 language features: #define invoke_HAVE_NODISCARD invoke_CPP17_000 // Presence of C++ language features: #if invoke_HAVE_CONSTEXPR_11 # define invoke_constexpr constexpr #else # define invoke_constexpr /*constexpr*/ #endif #if invoke_HAVE_CONSTEXPR_14 # define invoke_constexpr14 constexpr #else # define invoke_constexpr14 /*constexpr*/ #endif #if invoke_HAVE_NOEXCEPT && !invoke_CONFIG_NO_EXCEPTIONS # define invoke_noexcept noexcept # define invoke_noexcept_op(expr) noexcept(expr) #else # define invoke_noexcept /*noexcept*/ # define invoke_noexcept_op(expr) /*noexcept(expr)*/ #endif #if invoke_HAVE_NODISCARD # define invoke_nodiscard [[nodiscard]] #else # define invoke_nodiscard /*[[nodiscard]]*/ #endif // additional includes: #if invoke_CONFIG_NO_EXCEPTIONS # include #else # include #endif #if invoke_HAVE_TYPE_TRAITS # include #elif invoke_HAVE_TR1_TYPE_TRAITS # include #endif #include // std::mem_fn() #include // std::forward() #if invoke_CPP11_OR_GREATER #pragma message invoke_lpar "*** Using nonstd::invoke - C++11" invoke_rpar namespace nonstd { namespace invoke_lite { namespace std17 { #if invoke_CPP17_000 using std::invoke_result; #else template< class F, class... Args> class invoke_result : public std::result_of {}; #endif } // namespace std17 namespace std20 { // Note: std::mem_fn is constexpr since C++20 #if invoke_CPP20_000 using std::mem_fn; #else template< class R, class C > struct mem_fn_fun { typedef R C::*F; F f; explicit constexpr mem_fn_fun( F f_ ) : f( f_) {} template constexpr typename std17::invoke_result::type operator()( C & c, Args&&... args ) const #if invoke_CPP17_000 noexcept( std::is_nothrow_invocable_v ) #endif { return (c.*f)( std::forward(args)...); } template constexpr typename std17::invoke_result::type operator()( C const & c, Args&&... args ) const #if invoke_CPP17_000 noexcept( std::is_nothrow_invocable_v ) #endif { return (c.*f)( std::forward(args)...); } }; template< class T, class C > struct mem_fn_obj { typedef T C::*D; D d; explicit constexpr mem_fn_obj( D d_ ) : d( d_) {} constexpr T & operator()( C & c ) const noexcept { return c.*d; } constexpr T const & operator()( C const & c ) const noexcept { return c.*d; } constexpr T & operator()( C * c ) const noexcept { return c->*d; } constexpr T const & operator()( C const * c ) const noexcept { return c->*d; } }; template< class R, class C > constexpr auto mem_fn( R C::*m ) noexcept -> typename std::enable_if< std::is_member_function_pointer::value , mem_fn_fun >::type { return mem_fn_fun( m ); } template< class R, class C > constexpr auto mem_fn( R C::*m ) noexcept -> typename std::enable_if< std::is_member_object_pointer::value , mem_fn_obj >::type { return mem_fn_obj( m ); } #endif // invoke_CPP20_000 } // namespace std20 namespace detail { // C++11 implementation contributed by Peter Featherstone, @pfeatherstone template< typename F, typename ... Args > constexpr auto INVOKE( F&& fn, Args&& ... args ) -> typename std::enable_if< std::is_member_pointer::type>::value , decltype( std20::mem_fn(fn)( std::forward(args)...) ) >::type { return std20::mem_fn( fn )( std::forward(args)...); } template< typename F, typename ... Args > constexpr auto INVOKE( F&& fn, Args&& ... args ) -> typename std::enable_if< ! std::is_member_pointer::type>::value , decltype( std::forward(fn)( std::forward(args)...) ) >::type { return std::forward( fn )( std::forward(args)...); } // conforming C++14 implementation (is also a valid C++11 implementation): template< typename AlwaysVoid, typename, typename...> struct invoke_result {}; template< typename F, typename... Args > struct invoke_result< decltype( void(detail::INVOKE(std::declval(), std::declval()...)) ), F, Args...> { using type = decltype( detail::INVOKE(std::declval(), std::declval()...) ); }; //template< typename Fn, typename... ArgTypes > //struct is_nothrow_invocable : std::false_type{};; } // namespace detail // template< typename > struct result_of; // template< typename F, typename... ArgTypes > // struct result_of< F(ArgTypes...) > : detail::invoke_result< void, F, ArgTypes...> {}; template< typename F, typename... ArgTypes > struct invoke_result : detail::invoke_result< void, F, ArgTypes...> {}; template< typename F, typename... Args > constexpr typename invoke_result< F, Args...>::type invoke( F && f, Args &&... args ) // noexcept( detail::is_nothrow_invocable::value ) { return detail::INVOKE( std::forward( f ), std::forward( args )...); } }} // namespace nonstd::invoke_lite namespace nonstd { using invoke_lite::invoke; } // // namespace #else // not C++17, not C++11 - invoke() #pragma message invoke_lpar "*** Using nonstd::invoke - C++98" invoke_rpar // // C++98, code based on proposal n1454. // // N1454. A uniform method for computing function object return types (revision 1), // by Douglas Gregor, http://wg21.link/n1454 namespace nonstd { namespace std98 { // type traits: template< typename T, T v > struct integral_constant { typedef integral_constant< T, v > type; typedef T value_type; static const T value = v; }; typedef integral_constant< bool, true > true_type; typedef integral_constant< bool, false > false_type; //template< typename T > struct remove_reference { typedef T type; }; //template< typename T > struct remove_reference { typedef T type; }; ////template< typename T > struct remove_reference {typedef T type;}; // //template< typename T > struct add_lvalue_reference { typedef T& type; }; //template< typename T > struct add_lvalue_reference { typedef T& type; }; template< typename T > struct remove_const { typedef T type; }; template< typename T > struct remove_const { typedef T type; }; template< typename T > struct remove_volatile { typedef T type; }; template< typename T > struct remove_volatile { typedef T type; }; template< typename T > struct remove_cv { typedef typename remove_volatile::type>::type type; }; template< typename T > struct is_const : false_type {}; template< typename T > struct is_const : true_type {}; template< typename T > struct is_pointer_helper : false_type {}; template< typename T > struct is_pointer_helper : true_type {}; template< typename T > struct is_pointer : is_pointer_helper::type> {}; template< typename T > struct is_member_pointer_helper : false_type {}; template< typename T , typename U > struct is_member_pointer_helper : true_type {}; template< typename T > struct is_member_pointer : is_member_pointer_helper::type> {}; template< bool B, typename T = void > struct enable_if {}; template< typename T > struct enable_if< true, T > { typedef T type; }; // result type deduction: namespace detail { template< typename F > struct forwardN; } // namespace detail template< typename F > struct result_of; template< typename F > struct result_of { typedef typename detail::forwardN::template result::type type; }; template< typename F, typename T1 > struct result_of { typedef typename detail::forwardN::template result::type type; }; template< typename F, typename T1, typename T2 > struct result_of { typedef typename detail::forwardN::template result::type type; }; namespace detail { template< typename F > struct forwardN { F f; template< typename T> struct result { typedef T type; }; template< typename R> struct result { typedef R type; }; // template< typename R, typename C > struct result< R (C::*)() > { typedef R type; }; template< typename T, typename T1 > struct result< T(T1) > { typedef typename result_of< F( T1 ) >::type type; }; template< typename T, typename T1 > struct result< const T(T1) > { typedef typename result_of< const F( T1 ) >::type type; }; template< typename T, typename T1, typename T2 > struct result< T(T1, T2) > { typedef typename result_of< F( T1, T2 ) >::type type; }; template< typename T, typename T1, typename T2 > struct result< const T(T1, T2) > { typedef typename result_of< const F( T1, T2 ) >::type type; }; template< typename T1> typename result< forwardN(T1) >::type operator()( T1 & t1 ) { return f( t1 ); } template< typename T1> typename result< const forwardN(T1) >::type operator()( T1 & t1 ) const { return f( t1 ); } template< typename T1, typename T2> typename result< forwardN(T1, T2) >::type operator()( T1 & t1, T2 & t2 ) { return f( t1, t2 ); } template< typename T1, typename T2> typename result< const forwardN(T1, T2) >::type operator()( T1 & t1, T2 & t2 ) const { return f( t1, t2 ); } }; } // namespace detail // limited set of invoke(): // - 0..2 arguments // - no mix of const and non-const reference arguments // - ... template< typename F> typename result_of::type invoke( F f ) { return f(); } template< typename F, typename T1 > typename enable_if< !is_member_pointer::value, typename result_of::type >::type invoke( F f, T1 const & t1 ) { return f( t1 ); } template< typename F, typename T1, typename T2, int > typename enable_if< !is_member_pointer::value, typename result_of::type >::type invoke( F f, T1 const & t1, T2 const & t2 ) { return f( t1, t2 ); } template< typename Base, typename T, typename Obj> typename enable_if< !is_pointer::value && !is_const::value, T& >::type invoke( T Base::* pmd, Obj & ref ) { return ref.*pmd; } template< typename Base, typename T, typename Obj> typename enable_if< !is_pointer::value && is_const::value, T >::type invoke( T const Base::* pmd, Obj & ref ) { return ref.*pmd; } template< typename Base, typename T, typename Obj> typename enable_if< !is_const::value, T& >::type invoke( T Base::* pmd, Obj * ptr ) { return (*ptr).*pmd; } template< typename Base, typename T, typename Obj> typename enable_if< is_const::value, T >::type invoke( T const Base::*pmd, Obj * ptr ) { return (*ptr).*pmd; } template< typename R, typename Base, typename Obj> R invoke( R (Base::*pmf)() const, Obj const & ref ) { return (ref.*pmf)(); } template< typename R, typename Base, typename Obj> R invoke( R (Base::*pmf)() , Obj & ref ) { return (ref.*pmf)(); } template< typename R, typename Base, typename Obj> R invoke( R (Base::*pmf)() const, Obj * ptr ) { return ((*ptr).*pmf)(); } template< typename R, typename Base, typename Obj> R invoke( R (Base::*pmf)() , Obj * ptr ) { return ((*ptr).*pmf)(); } template< typename R, typename Base, typename Obj, typename T1 > R invoke( R (Base::*pmf)(T1) const, Obj const & ref, T1 const & t1 ) { return (ref.*pmf)( t1 ); } template< typename R, typename Base, typename Obj, typename T1 > R invoke( R (Base::*pmf)(T1) , Obj & ref, T1 const & t1 ) { return (ref.*pmf)( t1 ); } template< typename R, typename Base, typename Obj, typename A1 , typename T1 > R invoke( R (Base::*pmf)(A1) , Obj & ref, T1 & t1 ) { return (ref.*pmf)( t1 ); } template< typename R, typename Base, typename Obj, typename T1 > R invoke( R (Base::*pmf)(T1) const, Obj const * ptr, T1 const & t1 ) { return ((*ptr).*pmf)( t1 ); } template< typename R, typename Base, typename Obj, typename T1 > R invoke( R (Base::*pmf)(T1) , Obj * ptr, T1 const & t1 ) { return ((*ptr).*pmf)( t1 ); } template< typename R, typename Base, typename Obj, typename A1 , typename T1 > R invoke( R (Base::*pmf)(A1) , Obj * ptr, T1 & t1 ) { return ((*ptr).*pmf)( t1 ); } template< typename R, typename Base, typename Obj, typename T1, typename T2 > R invoke( R (Base::*pmf)(T1,T2) const, Obj const & ref, T1 const & t1 , T2 const & t2 ) { return (ref.*pmf)( t1, t2 ); } template< typename R, typename Base, typename Obj, typename T1, typename T2 > R invoke( R (Base::*pmf)(T1,T2) , Obj & ref, T1 const & t1 , T2 const & t2 ) { return (ref.*pmf)( t1, t2 ); } template< typename R, typename Base, typename Obj, typename A1, typename A2 , typename T1, typename T2 > R invoke( R (Base::*pmf)(A1,A2) , Obj & ref, T2 & t1 , T2 & t2 ) { return (ref.*pmf)( t1, t2 ); } template< typename R, typename Base, typename Obj, typename T1, typename T2 > R invoke( R (Base::*pmf)(T1,T2) const, Obj const * ptr, T1 const & t1 , T2 const & t2 ) { return ((*ptr).*pmf)( t1, t2 ); } template< typename R, typename Base, typename Obj, typename T1, typename T2 > R invoke( R (Base::*pmf)(T1,T2) , Obj * ptr, T1 const & t1 , T2 const & t2 ) { return ((*ptr).*pmf)( t1, t2 ); } template< typename R, typename Base, typename Obj, typename A1, typename A2 , typename T1, typename T2 > R invoke( R (Base::*pmf)(A1,A2) , Obj * ptr, T2 & t1 , T2 & t2 ) { return ((*ptr).*pmf)( t1, t2 ); } }} // namespace nonstd::std98 // Bring C++98 invoke() into scope nonstd: namespace nonstd { using std98::invoke; } #endif // invoke_CPP17_OR_GREATER, invoke_CPP11_OR_GREATER - invoke() #endif // invoke_USES_STD_INVOKE - invoke() // // nonstd::apply(): // // C++11 implementation contributed by Peter Featherstone, @pfeatherstone. #if invoke_USES_STD_INVOKE #pragma message invoke_lpar "*** Using std::apply." invoke_rpar #include using std::apply; namespace nonstd { using std::apply; } #elif invoke_CPP11_OR_GREATER #pragma message invoke_lpar "*** Using nonstd::invoke - C++11" invoke_rpar #include #include namespace nonstd { namespace apply_lite { #if invoke_CPLUSPLUS < 201402L #pragma message invoke_lpar "*** Defining index_sequence etc." invoke_rpar template< std::size_t... Ints > struct index_sequence { using type = index_sequence; using value_type = std::size_t; static constexpr std::size_t size() noexcept { return sizeof...(Ints); } }; template< class Sequence1, class Sequence2 > struct merge_and_renumber; template< std::size_t... I1, std::size_t... I2 > struct merge_and_renumber< index_sequence, index_sequence> : index_sequence {}; template struct make_index_sequence : merge_and_renumber< typename make_index_sequence < N / 2 >::type, typename make_index_sequence < N - N / 2 >::type > {}; template<> struct make_index_sequence<0> : index_sequence<> {}; template<> struct make_index_sequence<1> : index_sequence<0> {}; template using index_sequence_for = make_index_sequence; #else // invoke_CPLUSPLUS < 201402L using std::index_sequence; using std::make_index_sequence; using std::index_sequence_for; #endif // invoke_CPLUSPLUS < 201402L #pragma message invoke_lpar "*** Using nonstd::apply - C++11." invoke_rpar namespace detail { template< typename F, typename Tuple, std::size_t... I > constexpr auto apply_impl( F&& fn, Tuple && tpl, index_sequence ) -> decltype( invoke( std::forward(fn), std::get(std::forward(tpl) )...) ) { return invoke( std::forward(fn), std::get( std::forward(tpl) )...); } } // namespace detail template< typename F, typename Tuple > constexpr auto apply( F&& fn, Tuple && tpl ) -> decltype( detail::apply_impl( std::forward( fn ) , std::forward( tpl ) , make_index_sequence::type>::value>{} ) ) { return detail::apply_impl( std::forward(fn) , std::forward(tpl) , make_index_sequence::type >::value>{} ); } }} // namespace nonstd::apply_lite // Bring apply() into scope nonstd: namespace nonstd { using apply_lite::apply; } #else // invoke_CPP11_OR_GREATER - apply() #pragma message invoke_lpar "*** nonstd::apply(): No implementation for C++98." invoke_rpar #endif // invoke_USES_STD_INVOKE - apply() #endif // NONSTD_INVOKE_LITE_HPP