//--------------------------------------
// utils/traits: Additional type traits
//--------------------------------------
//
//          Copyright kennytm (auraHT Ltd.) 2011.
// 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)

/**

``<utils/traits.hpp>`` --- Additional type traits
=================================================

This module provides additional type traits and related functions, missing from
the standard library.

*/

#ifndef TRAITS_HPP_9ALQFEFX7TO
#define TRAITS_HPP_9ALQFEFX7TO 1

#include <cstdlib>
#include <functional>
#include <tuple>
#include <type_traits>

#include <fplus/internal/meta.hpp>

namespace fplus {

// source: https://github.com/kennytm/utils
namespace utils {

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#endif

/**
.. macro:: DECLARE_HAS_TYPE_MEMBER(member_name)

    This macro declares a template ``has_member_name`` which will check whether
    a type member ``member_name`` exists in a particular type.

    Example::

        DECLARE_HAS_TYPE_MEMBER(result_type)

        ...

        printf("%d\n", has_result_type< std::plus<int> >::value);
        // ^ prints '1' (true)
        printf("%d\n", has_result_type< double(*)() >::value);
        // ^ prints '0' (false)
*/
#define DECLARE_HAS_TYPE_MEMBER(member_name)                                                              \
    template <typename, typename = void>                                                                  \
    struct has_##member_name {                                                                            \
        enum { value = false };                                                                           \
    };                                                                                                    \
    template <typename T>                                                                                 \
    struct has_##member_name<T, typename std::enable_if<sizeof(typename T::member_name) || true>::type> { \
        enum { value = true };                                                                            \
    };

    /**
.. type:: struct utils::function_traits<F>

    Obtain compile-time information about a function object *F*.

    This template currently supports the following types:

    * Normal function types (``R(T...)``), function pointers (``R(*)(T...)``)
      and function references (``R(&)(T...)`` and ``R(&&)(T...)``).
    * Member functions (``R(C::*)(T...)``)
    * ``std::function<F>``
    * Type of lambda functions, and any other types that has a unique
      ``operator()``.
    * Type of ``std::mem_fn`` (only for GCC's libstdc++ and LLVM's libc++).
      Following the C++ spec, the first argument will be a raw pointer.
*/
    template <typename T>
    struct function_traits
        : public function_traits<decltype(&T::operator())> {
    };

    namespace xx_impl {
        template <typename C, typename R, typename... A>
        struct memfn_type {
            typedef typename std::conditional<
                std::is_const<C>::value,
                typename std::conditional<
                    std::is_volatile<C>::value,
                    R (C::*)(A...) const volatile,
                    R (C::*)(A...) const>::type,
                typename std::conditional<
                    std::is_volatile<C>::value,
                    R (C::*)(A...) volatile,
                    R (C::*)(A...)>::type>::type type;
        };
    }

    template <typename ReturnType, typename... Args>
    struct function_traits<ReturnType(Args...)> {
        /**
    .. type:: type result_type

        The type returned by calling an instance of the function object type *F*.
    */
        typedef ReturnType result_type;

        /**
    .. type:: type function_type

        The function type (``R(T...)``).
    */
        typedef ReturnType function_type(Args...);

        /**
    .. type:: type member_function_type<OwnerType>

        The member function type for an *OwnerType* (``R(OwnerType::*)(T...)``).
    */
        template <typename OwnerType>
        using member_function_type = typename xx_impl::memfn_type<
            typename std::remove_pointer<typename std::remove_reference<OwnerType>::type>::type,
            ReturnType, Args...>::type;

        /**
    .. data:: static const size_t arity

        Number of arguments the function object will take.
    */
        enum { arity = sizeof...(Args) };

        /**
    .. type:: type arg<n>::type

        The type of the *n*-th argument.
    */
        template <size_t i>
        struct arg {
            typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
        };
    };

#if __cplusplus > 201510L

    template <typename ReturnType, typename... Args>
    struct function_traits<ReturnType(Args...) noexcept>
        : public function_traits<ReturnType(Args...)> {
    };

#endif

    template <typename ReturnType, typename... Args>
    struct function_traits<ReturnType (*)(Args...)>
        : public function_traits<ReturnType(Args...)> {
    };

    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits<ReturnType (ClassType::*)(Args...)>
        : public function_traits<ReturnType(Args...)> {
        typedef ClassType& owner_type;
    };

    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits<ReturnType (ClassType::*)(Args...) const>
        : public function_traits<ReturnType(Args...)> {
        typedef const ClassType& owner_type;
    };

    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits<ReturnType (ClassType::*)(Args...) volatile>
        : public function_traits<ReturnType(Args...)> {
        typedef volatile ClassType& owner_type;
    };

    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits<ReturnType (ClassType::*)(Args...) const volatile>
        : public function_traits<ReturnType(Args...)> {
        typedef const volatile ClassType& owner_type;
    };

#if __cplusplus > 201510L

    template <typename ReturnType, typename... Args>
    struct function_traits<ReturnType (*)(Args...) noexcept>
        : public function_traits<ReturnType(Args...)> {
    };

    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits<ReturnType (ClassType::*)(Args...) noexcept>
        : public function_traits<ReturnType(Args...)> {
        typedef ClassType& owner_type;
    };

    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits<ReturnType (ClassType::*)(Args...) const noexcept>
        : public function_traits<ReturnType(Args...)> {
        typedef const ClassType& owner_type;
    };

    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits<ReturnType (ClassType::*)(Args...) volatile noexcept>
        : public function_traits<ReturnType(Args...)> {
        typedef volatile ClassType& owner_type;
    };

    template <typename ClassType, typename ReturnType, typename... Args>
    struct function_traits<ReturnType (ClassType::*)(Args...) const volatile noexcept>
        : public function_traits<ReturnType(Args...)> {
        typedef const volatile ClassType& owner_type;
    };

#endif

    template <typename FunctionType>
    struct function_traits<std::function<FunctionType>>
        : public function_traits<FunctionType> {
    };

#if defined(_GLIBCXX_FUNCTIONAL)
#define MEM_FN_SYMBOL_XX0SL7G4Z0J std::_Mem_fn
#elif defined(_LIBCPP_FUNCTIONAL)
#define MEM_FN_SYMBOL_XX0SL7G4Z0J std::__mem_fn
#endif

#ifdef MEM_FN_SYMBOL_XX0SL7G4Z0J

    template <typename R, typename C>
    struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R (C::*)()>>
        : public function_traits<R(C*)> {
    };
    template <typename R, typename C, typename... A>
    struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R (C::*)(A...)>>
        : public function_traits<R(C*, A...)> {
    };
    template <typename R, typename C, typename... A>
    struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R (C::*)(A...) const>>
        : public function_traits<R(const C*, A...)> {
    };
    template <typename R, typename C, typename... A>
    struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R (C::*)(A...) volatile>>
        : public function_traits<R(volatile C*, A...)> {
    };
    template <typename R, typename C, typename... A>
    struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R (C::*)(A...) const volatile>>
        : public function_traits<R(const volatile C*, A...)> {
    };
#if __cplusplus > 201510L
    template <typename R, typename C>
    struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R (C::*)() noexcept>>
        : public function_traits<R(C*)> {
    };
    template <typename R, typename C, typename... A>
    struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R (C::*)(A...) noexcept>>
        : public function_traits<R(C*, A...)> {
    };
    template <typename R, typename C, typename... A>
    struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R (C::*)(A...) const noexcept>>
        : public function_traits<R(const C*, A...)> {
    };
    template <typename R, typename C, typename... A>
    struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R (C::*)(A...) volatile noexcept>>
        : public function_traits<R(volatile C*, A...)> {
    };
    template <typename R, typename C, typename... A>
    struct function_traits<MEM_FN_SYMBOL_XX0SL7G4Z0J<R (C::*)(A...) const volatile noexcept>>
        : public function_traits<R(const volatile C*, A...)> {
    };
#endif

#undef MEM_FN_SYMBOL_XX0SL7G4Z0J
#endif

    template <typename T>
    struct function_traits<T&> : public function_traits<T> {
    };
    template <typename T>
    struct function_traits<const T&> : public function_traits<T> {
    };
    template <typename T>
    struct function_traits<volatile T&> : public function_traits<T> {
    };
    template <typename T>
    struct function_traits<const volatile T&> : public function_traits<T> {
    };
    template <typename T>
    struct function_traits<T&&> : public function_traits<T> {
    };
    template <typename T>
    struct function_traits<const T&&> : public function_traits<T> {
    };
    template <typename T>
    struct function_traits<volatile T&&> : public function_traits<T> {
    };
    template <typename T>
    struct function_traits<const volatile T&&> : public function_traits<T> {
    };

#define FORWARD_RES_8QR485JMSBT             \
    typename std::conditional<              \
        std::is_lvalue_reference<R>::value, \
        T&,                                 \
        typename std::remove_reference<T>::type&&>::type

    /**
.. function:: auto utils::forward_like<Like, T>(T&& t) noexcept

    Forward the reference *t* like the type of *Like*. That means, if *Like* is
    an lvalue (reference), this function will return an lvalue reference of *t*.
    Otherwise, if *Like* is an rvalue, this function will return an rvalue
    reference of *t*.

    This is mainly used to propagate the expression category (lvalue/rvalue) of
    a member of *Like*, generalizing ``std::forward``.
*/
    template <typename R, typename T>
    FORWARD_RES_8QR485JMSBT forward_like(T&& input) noexcept
    {
        return static_cast<FORWARD_RES_8QR485JMSBT>(input);
    }

#undef FORWARD_RES_8QR485JMSBT

    /**
.. type:: struct utils::copy_cv<From, To>

    Copy the CV qualifier between the two types. For example,
    ``utils::copy_cv<const int, double>::type`` will become ``const double``.
*/
    template <typename From, typename To>
    struct copy_cv {
    private:
        typedef typename std::remove_cv<To>::type raw_To;
        typedef typename std::conditional<std::is_const<From>::value,
            const raw_To, raw_To>::type const_raw_To;

    public:
        /**
    .. type:: type type

        Result of cv-copying.
    */
        typedef typename std::conditional<std::is_volatile<From>::value,
            volatile const_raw_To, const_raw_To>::type type;
    };

    /**
.. type:: struct utils::pointee<T>

    Returns the type by derefering an instance of *T*. This is a generalization
    of ``std::remove_pointer``, that it also works with iterators.
*/
    template <typename T>
    struct pointee {
        /**
    .. type:: type type

        Result of dereferencing.
    */
        typedef typename std::remove_reference<decltype(*std::declval<T>())>::type type;
    };

    /**
.. function:: std::add_rvalue_reference<T>::type utils::rt_val<T>() noexcept

    Returns a value of type *T*. It is guaranteed to do nothing and will not
    throw a compile-time error, but using the returned result will cause
    undefined behavior.
*/
    template <typename T>
    typename std::add_rvalue_reference<T>::type rt_val() noexcept
    {
        return std::move(*static_cast<T*>(nullptr));
    }

#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

}
}

namespace fplus {
namespace internal {
    template <typename>
    struct is_std_function : std::false_type {
    };

    template <typename T>
    struct is_std_function<std::function<T>> : std::true_type {
    };

    // Those traits are needed to not perform arity checks on a generic-lambd
    // or a templated/overloaded operator()
    template <typename T, typename = void>
    struct has_function_traits : std::false_type {
    };

    // There is a bug with GCC 7 when a std::function is passed as T.
    // It produces an ambiguous call between this one and the std::function overload
    // It's related to our void_t implementation, the C++14 compatible version does not
    // work, whereas the C++17 one does...
    //
    // So, help GCC a bit with is_std_function
    template <typename T>
    struct has_function_traits<T,
        std::enable_if_t<!is_std_function<T>::value,
            void_t<decltype(&T::operator())>>>
        : std::true_type {
    };

    template <typename ReturnType, typename... Args>
    struct has_function_traits<ReturnType(Args...)> : std::true_type {
    };

    template <typename ReturnType, typename... Args>
    struct has_function_traits<ReturnType (*)(Args...)> : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...)> : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) const>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) volatile>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) const volatile>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...)&> : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) const&>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) volatile&>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) const volatile&>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) &&>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) const&&>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) volatile&&>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) const volatile&&>
        : std::true_type {
    };

    template <typename FunctionType>
    struct has_function_traits<std::function<FunctionType>> : std::true_type {
    };

#if __cplusplus > 201510L

    template <typename ReturnType, typename... Args>
    struct has_function_traits<ReturnType(Args...) noexcept> : std::true_type {
    };

    template <typename ReturnType, typename... Args>
    struct has_function_traits<ReturnType (*)(Args...) noexcept> : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) noexcept> : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) const noexcept>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) volatile noexcept>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) const volatile noexcept>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) & noexcept> : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) const & noexcept>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) volatile & noexcept>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) const volatile & noexcept>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) && noexcept>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) const && noexcept>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) volatile && noexcept>
        : std::true_type {
    };

    template <typename ReturnType, typename ClassType, typename... Args>
    struct has_function_traits<ReturnType (ClassType::*)(Args...) const volatile && noexcept>
        : std::true_type {
    };

#endif
}
}

#endif
