diff options
Diffstat (limited to 'lib/function_traits.h')
-rw-r--r-- | lib/function_traits.h | 93 |
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 |