1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
|
// SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net>
// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include <functional>
namespace Quotient {
namespace _impl {
template <typename>
struct fn_traits {};
}
/// Determine traits of an arbitrary function/lambda/functor
/*!
* Doesn't work with generic lambdas and function objects that have
* operator() overloaded.
* \sa
* https://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda#7943765
*/
template <typename T>
struct function_traits
: public _impl::fn_traits<std::remove_reference_t<T>> {};
// Specialisation for a function
template <typename ReturnT, typename... ArgTs>
struct function_traits<ReturnT(ArgTs...)> {
using return_type = ReturnT;
using arg_types = std::tuple<ArgTs...>;
};
namespace _impl {
template <typename>
struct fn_object_traits;
// Specialisation for a lambda function
template <typename ReturnT, typename ClassT, typename... ArgTs>
struct fn_object_traits<ReturnT (ClassT::*)(ArgTs...)>
: function_traits<ReturnT(ArgTs...)> {};
// Specialisation for a const lambda function
template <typename ReturnT, typename ClassT, typename... ArgTs>
struct fn_object_traits<ReturnT (ClassT::*)(ArgTs...) const>
: function_traits<ReturnT(ArgTs...)> {};
// Specialisation for function objects with (non-overloaded) operator()
// (this includes non-generic lambdas)
template <typename T>
requires requires { &T::operator(); }
struct fn_traits<T>
: public fn_object_traits<decltype(&T::operator())> {};
// Specialisation for a member function in a non-functor class
template <typename ReturnT, typename ClassT, typename... ArgTs>
struct fn_traits<ReturnT (ClassT::*)(ArgTs...)>
: function_traits<ReturnT(ClassT, ArgTs...)> {};
// Specialisation for a const member function
template <typename ReturnT, typename ClassT, typename... ArgTs>
struct fn_traits<ReturnT (ClassT::*)(ArgTs...) const>
: function_traits<ReturnT(const ClassT&, ArgTs...)> {};
// Specialisation for a constref member function
template <typename ReturnT, typename ClassT, typename... ArgTs>
struct fn_traits<ReturnT (ClassT::*)(ArgTs...) const&>
: function_traits<ReturnT(const ClassT&, ArgTs...)> {};
// Specialisation for a prvalue member function
template <typename ReturnT, typename ClassT, typename... ArgTs>
struct fn_traits<ReturnT (ClassT::*)(ArgTs...) &&>
: function_traits<ReturnT(ClassT&&, ArgTs...)> {};
// Specialisation for a pointer-to-member
template <typename ReturnT, typename ClassT>
struct fn_traits<ReturnT ClassT::*>
: function_traits<ReturnT&(ClassT)> {};
// Specialisation for a const pointer-to-member
template <typename ReturnT, typename ClassT>
struct fn_traits<const ReturnT ClassT::*>
: function_traits<const ReturnT&(ClassT)> {};
} // namespace _impl
template <typename FnT, int ArgN = 0>
using fn_arg_t =
std::tuple_element_t<ArgN, typename function_traits<FnT>::arg_types>;
template <typename FnT>
constexpr auto fn_arg_count_v =
std::tuple_size_v<typename function_traits<FnT>::arg_types>;
} // namespace Quotient
|