// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___SGX_INVOKE
#define _LIBCPP___SGX_INVOKE

// __invoke

#if !defined(_LIBCPP_SGX_CONFIG)

// bullets 1 and 2


template <class _Fp, class _A0, class ..._Args,
    class = typename enable_if
    <
    is_member_function_pointer<typename remove_reference<_Fp>::type>::value &&
    is_base_of<typename remove_reference<typename __member_pointer_traits<typename remove_reference<_Fp>::type>::_ClassType>::type,
    typename remove_reference<_A0>::type>::value
    >::type
>
inline _LIBCPP_INLINE_VISIBILITY
auto
__invoke(_Fp&& __f, _A0&& __a0, _Args&& ...__args)
-> decltype((_VSTD::forward<_A0>(__a0).*__f)(_VSTD::forward<_Args>(__args)...))
{
    return (_VSTD::forward<_A0>(__a0).*__f)(_VSTD::forward<_Args>(__args)...);
}

template <class _Fp, class _A0, class ..._Args,
    class = typename enable_if
    <
    is_member_function_pointer<typename remove_reference<_Fp>::type>::value &&
    !is_base_of<typename remove_reference<typename __member_pointer_traits<typename remove_reference<_Fp>::type>::_ClassType>::type,
    typename remove_reference<_A0>::type>::value
    >::type
>
inline _LIBCPP_INLINE_VISIBILITY
auto
__invoke(_Fp&& __f, _A0&& __a0, _Args&& ...__args)
-> decltype(((*_VSTD::forward<_A0>(__a0)).*__f)(_VSTD::forward<_Args>(__args)...))
{
    return ((*_VSTD::forward<_A0>(__a0)).*__f)(_VSTD::forward<_Args>(__args)...);
}


// bullets 3 and 4

template <class _Fp, class _A0,
    class = typename enable_if
    <
    is_member_object_pointer<typename remove_reference<_Fp>::type>::value &&
    is_base_of<typename __member_pointer_traits<typename remove_reference<_Fp>::type>::_ClassType,
    typename remove_reference<_A0>::type>::value
    >::type
>
inline _LIBCPP_INLINE_VISIBILITY
auto
__invoke(_Fp&& __f, _A0&& __a0)
-> decltype(_VSTD::forward<_A0>(__a0).*__f)
{
    return _VSTD::forward<_A0>(__a0).*__f;
}

template <class _Fp, class _A0,
    class = typename enable_if
    <
    is_member_object_pointer<typename remove_reference<_Fp>::type>::value &&
    !is_base_of<typename __member_pointer_traits<typename remove_reference<_Fp>::type>::_ClassType,
    typename remove_reference<_A0>::type>::value
    >::type
>
inline _LIBCPP_INLINE_VISIBILITY
auto
__invoke(_Fp&& __f, _A0&& __a0)
-> decltype((*_VSTD::forward<_A0>(__a0)).*__f)
{
    return (*_VSTD::forward<_A0>(__a0)).*__f;
}

// bullet 5

template <class _Fp, class ..._Args>
inline _LIBCPP_INLINE_VISIBILITY
auto
__invoke(_Fp&& __f, _Args&& ...__args)
-> decltype(_VSTD::forward<_Fp>(__f)(_VSTD::forward<_Args>(__args)...))
{
    return _VSTD::forward<_Fp>(__f)(_VSTD::forward<_Args>(__args)...);
}
#else  //_LIBCPP_SGX_CONFIG

struct __invoke_member_function_object_impl
{
    template<class _Fp, class _A0, class... _Args>
    static inline _LIBCPP_INLINE_VISIBILITY
    auto
    __invoke(_Fp&& __f, _A0&& __a0, _Args&& ...__args)
    -> decltype((_VSTD::forward<_A0>(__a0).*__f)(_VSTD::forward<_Args>(__args)...))
    {
        return (_VSTD::forward<_A0>(__a0).*__f)(_VSTD::forward<_Args>(__args)...);
    }
};

struct __invoke_member_function_pointer_impl
{
    template<class _Fp, class _A0, class... _Args>
    static inline _LIBCPP_INLINE_VISIBILITY
    auto
    __invoke(_Fp&& __f, _A0&& __a0, _Args&& ...__args)
    -> decltype(((*_VSTD::forward<_A0>(__a0)).*__f)(_VSTD::forward<_Args>(__args)...))
    {
        return ((*_VSTD::forward<_A0>(__a0)).*__f)(_VSTD::forward<_Args>(__args)...);
    }
};

struct __invoke_member_data_object_impl
{
    template<class _Fp, class _A0>
    static inline _LIBCPP_INLINE_VISIBILITY
    auto
    __invoke(_Fp&& __f, _A0&& __a0)
    -> decltype(_VSTD::forward<_A0>(__a0).*__f)
    {
        return _VSTD::forward<_A0>(__a0).*__f;
    }
};

struct __invoke_member_data_pointer_impl
{
    template<class _Fp, class _A0>
    static inline _LIBCPP_INLINE_VISIBILITY
    auto
    __invoke(_Fp&& __f, _A0&& __a0)
    -> decltype((*_VSTD::forward<_A0>(__a0)).*__f)
    {
        return (*_VSTD::forward<_A0>(__a0)).*__f;
    }
};

struct __invoke_function_other_impl
{
    template <class _Fp, class ..._Args>
    static inline _LIBCPP_INLINE_VISIBILITY
    auto
    __invoke(_Fp&& __f, _Args&& ...__args)
    -> decltype(_VSTD::forward<_Fp>(__f)(_VSTD::forward<_Args>(__args)...))
    {
        return _VSTD::forward<_Fp>(__f)(_VSTD::forward<_Args>(__args)...);
    }
};

template<class _Fp, class _A0, class ..._Args> 
struct __invoke_member_function_pointer
    : conditional<is_base_of<typename remove_reference<typename __member_pointer_traits<typename remove_reference<_Fp>::type>::_ClassType>::type, typename remove_reference<_A0>::type>::value,
        __invoke_member_function_object_impl,
        __invoke_member_function_pointer_impl>::type {};

template<class _Fp, class _A0> 
struct __invoke_member_object_pointer
    : conditional<is_base_of<typename __member_pointer_traits<typename remove_reference<_Fp>::type>::_ClassType, typename remove_reference<_A0>::type>::value,
        __invoke_member_data_object_impl,
        __invoke_member_data_pointer_impl>::type {};

// bullets 1 and 2
template<class _Fp, class _A0, class... _Args,
class = typename enable_if<is_member_function_pointer<typename remove_reference<_Fp>::type>::value >::type>
inline  _LIBCPP_INLINE_VISIBILITY
auto
__invoke(_Fp&& __f, _A0&& __a0, _Args&&... __args)
    -> decltype(__invoke_member_function_pointer<_Fp, _A0, _Args...>::__invoke(_VSTD::forward<_Fp>(__f), _VSTD::forward<_A0>(__a0), _VSTD::forward<_Args>(__args)...))
{
    return (__invoke_member_function_pointer<_Fp, _A0, _Args...>::__invoke(_VSTD::forward<_Fp>(__f), _VSTD::forward<_A0>(__a0), _VSTD::forward<_Args>(__args)...));
}

// bullets 3 and 4
template<class _Fp, class _A0,
class = typename enable_if <is_member_object_pointer<typename remove_reference<_Fp>::type>::value>::type>
inline _LIBCPP_INLINE_VISIBILITY
auto
__invoke(_Fp&& __f, _A0&& __args)
-> decltype(__invoke_member_object_pointer<_Fp, _A0>::__invoke(_VSTD::forward<_Fp>(__f), _VSTD::forward<_A0>(__args)))
{
    return (__invoke_member_object_pointer<_Fp, _A0>::__invoke(_VSTD::forward<_Fp>(__f), _VSTD::forward<_A0>(__args)));
}

// bullet 5

template<class _Fp, class... _Args>
inline  _LIBCPP_INLINE_VISIBILITY
auto
__invoke(_Fp&& __f, _Args&&... __args)
-> decltype(__invoke_function_other_impl::__invoke(_VSTD::forward<_Fp>(__f), _VSTD::forward<_Args>(__args)...))
{
    return (__invoke_function_other_impl::__invoke(_VSTD::forward<_Fp>(__f), _VSTD::forward<_Args>(__args)...));
}

#endif // _LIBCPP_SGX_CONFIG

#endif // _LIBCPP___SGX_INVOKE