//////////////////////////////////////////////////////////////////////////////
//
// (C) Copyright Ion Gaztanaga 2011-2014. 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)
//
// See http://www.boost.org/libs/intrusive for documentation.
//
//////////////////////////////////////////////////////////////////////////////

// sample.h

#if !defined(BOOST_PP_IS_ITERATING)

   #ifndef BOOST_INTRUSIVE_DETAIL_HAS_MEMBER_FUNCTION_CALLABLE_WITH_DETAILS_INCLUDED
   #define BOOST_INTRUSIVE_DETAIL_HAS_MEMBER_FUNCTION_CALLABLE_WITH_DETAILS_INCLUDED

      #include <boost/intrusive/detail/preprocessor.hpp>
      #include <boost/intrusive/detail/mpl.hpp>
      #include <boost/move/utility_core.hpp>


      //Mark that we don't support 0 arg calls due to compiler ICE in GCC 3.4/4.0/4.1 and
      //wrong SFINAE for GCC 4.2/4.3
      #if defined(__GNUC__) && !defined(__clang__) && ((__GNUC__*100 + __GNUC_MINOR__*10) >= 340) && ((__GNUC__*100 + __GNUC_MINOR__*10) <= 430)
      #define BOOST_INTRUSIVE_DETAIL_HAS_MEMBER_FUNCTION_CALLABLE_WITH_0_ARGS_UNSUPPORTED
      #elif defined(BOOST_INTEL) && (BOOST_INTEL < 1200 )
      #define BOOST_INTRUSIVE_DETAIL_HAS_MEMBER_FUNCTION_CALLABLE_WITH_0_ARGS_UNSUPPORTED
      #endif

      namespace boost_intrusive_has_member_function_callable_with {

      struct dont_care
      {
         dont_care(...);
      };

      template<class T>
      struct make_dontcare
      {
         typedef boost_intrusive_has_member_function_callable_with::dont_care type;
      };

      struct private_type
      {
         static private_type p;
         private_type const &operator,(int) const;
      };

      typedef char yes_type;            // sizeof(yes_type) == 1
      struct no_type{ char dummy[2]; }; // sizeof(no_type)  == 2

      template<typename T>
      no_type is_private_type(T const &);
      yes_type is_private_type(private_type const &);

      }  //boost_intrusive_has_member_function_callable_with

   #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
   #define BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_SINGLE_ITERATION
   #endif

   #endif   //BOOST_INTRUSIVE_DETAIL_HAS_MEMBER_FUNCTION_CALLABLE_WITH_DETAILS_INCLUDED

#else //!BOOST_PP_IS_ITERATING

   #ifndef  BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME
   #error "BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME not defined!"
   #endif

   #ifndef  BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_NS_BEGIN
   #error "BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_NS_BEGIN not defined!"
   #endif

   #ifndef  BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_NS_END
   #error "BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_NS_END not defined!"
   #endif

   #if BOOST_PP_ITERATION_START() > BOOST_PP_ITERATION_FINISH()
   #error "BOOST_PP_ITERATION_START() must be <= BOOST_PP_ITERATION_FINISH()"
   #endif

   #if BOOST_PP_ITERATION() == BOOST_PP_ITERATION_START()

      BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_NS_BEGIN

      template <typename Type>
      class BOOST_PP_CAT(has_member_function_named_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)
      {
         struct BaseMixin
         {
            void BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME();
         };

         struct Base : public ::boost::intrusive::detail::remove_cv<Type>::type, public BaseMixin { Base(); };
         template <typename T, T t> class Helper{};

         template <typename U>
         static boost_intrusive_has_member_function_callable_with::no_type  deduce
            (U*, Helper<void (BaseMixin::*)(), &U::BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME>* = 0);
         static boost_intrusive_has_member_function_callable_with::yes_type deduce(...);

         public:
         static const bool value =
            sizeof(boost_intrusive_has_member_function_callable_with::yes_type) == sizeof(deduce((Base*)(0)));
      };

      #if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

         template<typename Fun, bool HasFunc
                  BOOST_PP_ENUM_TRAILING(BOOST_PP_ITERATION_FINISH(), BOOST_INTRUSIVE_PP_TEMPLATE_PARAM_VOID_DEFAULT, _)>
         struct BOOST_PP_CAT(BOOST_PP_CAT(has_member_function_callable_with_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME), _impl);
         //!

         template<typename Fun BOOST_PP_ENUM_TRAILING_PARAMS(BOOST_PP_ITERATION_FINISH(), class P)>
         struct BOOST_PP_CAT(BOOST_PP_CAT(has_member_function_callable_with_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME), _impl)
            <Fun, false BOOST_PP_ENUM_TRAILING_PARAMS(BOOST_PP_ITERATION_FINISH(), P)>
         {
            static const bool value = false;
         };
         //!

      #else //!defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

         template<typename Fun, bool HasFunc, class ...Args>
         struct BOOST_PP_CAT(BOOST_PP_CAT(has_member_function_callable_with_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME),_impl);

         template<typename Fun, class ...Args>
         struct BOOST_PP_CAT(BOOST_PP_CAT(has_member_function_callable_with_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME),_impl)
            <Fun, false, Args...>
         {
            static const bool value = false;
         };

         #ifdef BOOST_NO_CXX11_DECLTYPE

         //Special case for 0 args
         template< class F
               , std::size_t N =
                     sizeof((boost::move_detail::declval<F>().
                        BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME (), 0))>
         struct BOOST_PP_CAT(zeroarg_checker_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)
         {
            boost_intrusive_has_member_function_callable_with::yes_type dummy;
            BOOST_PP_CAT(zeroarg_checker_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)(int);
         };

         //For buggy compilers like MSVC 7.1+ ((F*)0)->func() does not
         //SFINAE-out the zeroarg_checker_ instantiation but sizeof yields to 0.
         template<class F>
         struct BOOST_PP_CAT(zeroarg_checker_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)<F, 0>
         {
            boost_intrusive_has_member_function_callable_with::no_type dummy;
            BOOST_PP_CAT(zeroarg_checker_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)(int);
         };

         #endif   //#ifdef BOOST_NO_CXX11_DECLTYPE

         template<typename Fun>
         struct BOOST_PP_CAT(BOOST_PP_CAT(has_member_function_callable_with_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME),_impl)
            <Fun, true>
         {
            #ifndef BOOST_NO_CXX11_DECLTYPE
            template<class U, class V = decltype(boost::move_detail::declval<U>().BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME()) >
            static boost_intrusive_has_member_function_callable_with::yes_type Test(U*);
            #else
            template<class U>
               static BOOST_PP_CAT(zeroarg_checker_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)
               <U> Test(BOOST_PP_CAT(zeroarg_checker_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)<U>*);
            #endif
   
            template <class U>
            static boost_intrusive_has_member_function_callable_with::no_type Test(...);

            static const bool value = sizeof(Test< Fun >(0))
                                 == sizeof(boost_intrusive_has_member_function_callable_with::yes_type);
         };

         template<typename Fun, class ...Args>
         struct BOOST_PP_CAT( BOOST_PP_CAT(has_member_function_callable_with_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME), _impl)
            <Fun, true , Args...>
         {

            template<class ...DontCares>
            struct FunWrapTmpl : Fun
            {
               FunWrapTmpl();
               using Fun::BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME;

               boost_intrusive_has_member_function_callable_with::private_type
                  BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME
                     ( DontCares...)  const;
            };

            typedef FunWrapTmpl<typename boost_intrusive_has_member_function_callable_with::make_dontcare<Args>::type...> FunWrap;

            static bool const value = (sizeof(boost_intrusive_has_member_function_callable_with::no_type) ==
                                       sizeof(boost_intrusive_has_member_function_callable_with::is_private_type
                                                ( (::boost::move_detail::declval< FunWrap >().
                                          BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME
                                             ( ::boost::move_detail::declval<Args>()... ), 0) )
                                             )
                                       );
         };

         template<typename Fun, class ...Args>
         struct BOOST_PP_CAT( has_member_function_callable_with_
                            , BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)
            : public BOOST_PP_CAT( BOOST_PP_CAT(has_member_function_callable_with_
                                 , BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME),_impl)
               < Fun
               , BOOST_PP_CAT( has_member_function_named_
                             , BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME )<Fun>::value
               , Args... >
         {};

      #endif   //defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

      BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_NS_END

   #endif   //BOOST_PP_ITERATION() == BOOST_PP_ITERATION_START()

   #if BOOST_PP_ITERATION() == 0

      BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_NS_BEGIN

      #if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

         #if !defined(_MSC_VER) || (_MSC_VER < 1600)

            #if defined(BOOST_INTRUSIVE_DETAIL_HAS_MEMBER_FUNCTION_CALLABLE_WITH_0_ARGS_UNSUPPORTED)

            template<typename Fun>
            struct BOOST_PP_CAT(BOOST_PP_CAT(has_member_function_callable_with_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME),_impl)
               <Fun, true BOOST_PP_ENUM_TRAILING(BOOST_PP_SUB(BOOST_PP_ITERATION_FINISH(), BOOST_PP_ITERATION()), BOOST_INTRUSIVE_PP_IDENTITY, void)>
            {
               //Mark that we don't support 0 arg calls due to compiler ICE in GCC 3.4/4.0/4.1 and
               //wrong SFINAE for GCC 4.2/4.3
               static const bool value = true;
            };

            #else //defined(BOOST_INTRUSIVE_DETAIL_HAS_MEMBER_FUNCTION_CALLABLE_WITH_0_ARGS_UNSUPPORTED)

            //Special case for 0 args
            template< class F
                  , std::size_t N =
                        sizeof((boost::move_detail::declval<F>().
                           BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME (), 0))>
            struct BOOST_PP_CAT(zeroarg_checker_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)
            {
               boost_intrusive_has_member_function_callable_with::yes_type dummy;
               BOOST_PP_CAT(zeroarg_checker_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)(int);
            };

            //For buggy compilers like MSVC 7.1+ ((F*)0)->func() does not
            //SFINAE-out the zeroarg_checker_ instantiation but sizeof yields to 0.
            template<class F>
            struct BOOST_PP_CAT(zeroarg_checker_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)<F, 0>
            {
               boost_intrusive_has_member_function_callable_with::no_type dummy;
               BOOST_PP_CAT(zeroarg_checker_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)(int);
            };

            template<typename Fun>
            struct BOOST_PP_CAT(BOOST_PP_CAT(has_member_function_callable_with_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME),_impl)
               <Fun, true BOOST_PP_ENUM_TRAILING(BOOST_PP_SUB(BOOST_PP_ITERATION_FINISH(), BOOST_PP_ITERATION()), BOOST_INTRUSIVE_PP_IDENTITY, void)>
            {
               template<class U>
               static BOOST_PP_CAT(zeroarg_checker_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)<U>
                  Test(BOOST_PP_CAT(zeroarg_checker_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)<U>*);

               template <class U>
               static boost_intrusive_has_member_function_callable_with::no_type Test(...);

               static const bool value = sizeof(Test< Fun >(0))
                                    == sizeof(boost_intrusive_has_member_function_callable_with::yes_type);
            };
            #endif   //defined(BOOST_INTRUSIVE_DETAIL_HAS_MEMBER_FUNCTION_CALLABLE_WITH_0_ARGS_UNSUPPORTED)

         #else //#if !defined(_MSC_VER) || (_MSC_VER < 1600)
            template<typename Fun>
            struct BOOST_PP_CAT(BOOST_PP_CAT(has_member_function_callable_with_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME),_impl)
               <Fun, true BOOST_PP_ENUM_TRAILING(BOOST_PP_SUB(BOOST_PP_ITERATION_FINISH(), BOOST_PP_ITERATION()), BOOST_INTRUSIVE_PP_IDENTITY, void)>
            {
               template<class U>
               static decltype( boost::move_detail::declval<U>().BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME()
                              , boost_intrusive_has_member_function_callable_with::yes_type())
                  Test(U*);

               template<class U>
               static boost_intrusive_has_member_function_callable_with::no_type Test(...);

               static const bool value = sizeof(Test<Fun>(0))
                                    == sizeof(boost_intrusive_has_member_function_callable_with::yes_type);
            };
         #endif   //#if !defined(_MSC_VER) || (_MSC_VER < 1600)

      #else   //#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

      #endif   //#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

      BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_NS_END

   #else   //BOOST_PP_ITERATION() == 0

      #if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

         BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_NS_BEGIN

         template<typename Fun BOOST_PP_ENUM_TRAILING_PARAMS(BOOST_PP_ITERATION(), class P)>
         struct BOOST_PP_CAT( BOOST_PP_CAT(has_member_function_callable_with_
                            , BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME),_impl)
            <Fun, true
            BOOST_PP_ENUM_TRAILING_PARAMS(BOOST_PP_ITERATION(), P)
            BOOST_PP_ENUM_TRAILING( BOOST_PP_SUB(BOOST_PP_ITERATION_FINISH(), BOOST_PP_ITERATION())
                                  , BOOST_INTRUSIVE_PP_IDENTITY
                                  , void)>
         {
            struct FunWrap : Fun
            {
               FunWrap();

               using Fun::BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME;
               boost_intrusive_has_member_function_callable_with::private_type
                  BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME
                     ( BOOST_PP_ENUM(BOOST_PP_ITERATION()
                     , BOOST_INTRUSIVE_PP_IDENTITY
                     , boost_intrusive_has_member_function_callable_with::dont_care))  const;
            };

            static bool const value =
            (sizeof(boost_intrusive_has_member_function_callable_with::no_type) ==
               sizeof(boost_intrusive_has_member_function_callable_with::is_private_type
                        (  (boost::move_detail::declval<FunWrap>().
                              BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME
                                 ( BOOST_PP_ENUM( BOOST_PP_ITERATION(), BOOST_INTRUSIVE_PP_DECLVAL, _) ), 0
                           )
                        )
                     )
            );
         };

         BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_NS_END
      #endif   //#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

   #endif   //BOOST_PP_ITERATION() == 0

   #if BOOST_PP_ITERATION() == BOOST_PP_ITERATION_FINISH()

      #if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

         BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_NS_BEGIN

         template<typename Fun
                  BOOST_PP_ENUM_TRAILING(BOOST_PP_ITERATION_FINISH(), BOOST_INTRUSIVE_PP_TEMPLATE_PARAM_VOID_DEFAULT, _)>
         struct BOOST_PP_CAT(has_member_function_callable_with_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)
            : public BOOST_PP_CAT(BOOST_PP_CAT(has_member_function_callable_with_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME), _impl)
               <Fun, BOOST_PP_CAT(has_member_function_named_, BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME)<Fun>::value
               BOOST_PP_ENUM_TRAILING_PARAMS(BOOST_PP_ITERATION_FINISH(), P) >
         {};

         BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_NS_END

      #endif //#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)

      #undef BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_FUNCNAME
      #undef BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_NS_BEGIN
      #undef BOOST_INTRUSIVE_HAS_MEMBER_FUNCTION_CALLABLE_WITH_NS_END

   #endif   //#if BOOST_PP_ITERATION() == BOOST_PP_ITERATION_FINISH()

#endif   //!BOOST_PP_IS_ITERATING
