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