rec/fplus/function_traits.hpp
2020-03-18 14:42:46 +08:00

451 lines
13 KiB
C++
Executable File

//--------------------------------------
// 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 <tuple>
#include <functional>
#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;
};
};
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;
};
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...)>
{};
#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
{
};
}
}
#endif