aboutsummaryrefslogtreecommitdiff
path: root/lib/function_traits.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/function_traits.h')
-rw-r--r--lib/function_traits.h93
1 files changed, 93 insertions, 0 deletions
diff --git a/lib/function_traits.h b/lib/function_traits.h
new file mode 100644
index 00000000..143ed162
--- /dev/null
+++ b/lib/function_traits.h
@@ -0,0 +1,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