diff options
author | Alexey Rusakov <Kitsune-Ral@users.sf.net> | 2021-12-02 15:24:44 +0100 |
---|---|---|
committer | Alexey Rusakov <Kitsune-Ral@users.sf.net> | 2021-12-02 15:24:44 +0100 |
commit | dc08fb9dfd474023084de9ce86f29f177ca52fdc (patch) | |
tree | 29cb5ab8a133bc46c4ccbee1cd369cd665525949 /lib | |
parent | ae0ad49f36e8ba5983839581302ed16ddbd75d5f (diff) | |
download | libquotient-dc08fb9dfd474023084de9ce86f29f177ca52fdc.tar.gz libquotient-dc08fb9dfd474023084de9ce86f29f177ca52fdc.zip |
Improve function_traits<>; split out from util.*
Quotient::function_traits<> did not support member functions in a proper
way (i.e. the way std::invoke_result<> treats them, with the function's
owning class represented as the first parameter). Now that I gained
the skill and understanding in function_traits<> somewhat wicked
machinery, I could properly support member functions. Overloads and
generic lambdas are not supported but maybe we'll get to those one day.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/events/event.h | 1 | ||||
-rw-r--r-- | lib/function_traits.cpp | 53 | ||||
-rw-r--r-- | lib/function_traits.h | 93 | ||||
-rw-r--r-- | lib/qt_connection_util.h | 2 | ||||
-rw-r--r-- | lib/util.cpp | 32 | ||||
-rw-r--r-- | lib/util.h | 52 |
6 files changed, 148 insertions, 85 deletions
diff --git a/lib/events/event.h b/lib/events/event.h index ce737280..4d4bb16b 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -5,6 +5,7 @@ #include "converters.h" #include "logging.h" +#include "function_traits.h" namespace Quotient { // === event_ptr_tt<> and type casting facilities === diff --git a/lib/function_traits.cpp b/lib/function_traits.cpp new file mode 100644 index 00000000..4ff427e4 --- /dev/null +++ b/lib/function_traits.cpp @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net> +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "function_traits.h" + +// Tests for function_traits<> + +using namespace Quotient; + +int f_(); +static_assert(std::is_same<fn_return_t<decltype(f_)>, int>::value, + "Test fn_return_t<>"); + +void f1_(int, float); +static_assert(std::is_same<fn_arg_t<decltype(f1_), 1>, float>::value, + "Test fn_arg_t<>"); + +struct Fo { + int operator()(); + static constexpr auto l = [] { return 0.0f; }; + bool memFn(); + void constMemFn() const&; + double field; + const double field2; +}; +static_assert(std::is_same_v<fn_return_t<Fo>, int>, + "Test return type of function object"); +static_assert(std::is_same_v<fn_return_t<decltype(Fo::l)>, float>, + "Test return type of lambda"); +static_assert(std::is_same_v<fn_arg_t<decltype(&Fo::memFn)>, Fo>, + "Test first argument type of member function"); +static_assert(std::is_same_v<fn_return_t<decltype(&Fo::memFn)>, bool>, + "Test return type of member function"); +static_assert(std::is_same_v<fn_arg_t<decltype(&Fo::constMemFn)>, const Fo&>, + "Test first argument type of const member function"); +static_assert(std::is_void_v<fn_return_t<decltype(&Fo::constMemFn)>>, + "Test return type of const member function"); +static_assert(std::is_same_v<fn_return_t<decltype(&Fo::field)>, double&>, + "Test return type of a class member"); +static_assert(std::is_same_v<fn_return_t<decltype(&Fo::field2)>, const double&>, + "Test return type of a const class member"); + +struct Fo1 { + void operator()(int); +}; +static_assert(std::is_same<fn_arg_t<Fo1>, int>(), + "Test fn_arg_t defaulting to first argument"); + +template <typename T> +static void ft(const std::vector<T>&); +static_assert( + std::is_same<fn_arg_t<decltype(ft<double>)>, const std::vector<double>&>(), + "Test function templates"); diff --git a/lib/function_traits.h b/lib/function_traits.h new file mode 100644 index 00000000..83b8e425 --- /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 AlwaysVoid, 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<void, 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...>; + // See also the comment for wrap_in_function() in qt_connection_util.h + using function_type = std::function<ReturnT(ArgTs...)>; +}; + +namespace _impl { + template <typename AlwaysVoid, typename> + struct fn_object_traits; + + // Specialisation for a lambda function + template <typename ReturnT, typename ClassT, typename... ArgTs> + struct fn_object_traits<void, ReturnT (ClassT::*)(ArgTs...)> + : function_traits<ReturnT(ArgTs...)> {}; + + // Specialisation for a const lambda function + template <typename ReturnT, typename ClassT, typename... ArgTs> + struct fn_object_traits<void, ReturnT (ClassT::*)(ArgTs...) const> + : function_traits<ReturnT(ArgTs...)> {}; + + // Specialisation for function objects with (non-overloaded) operator() + // (this includes non-generic lambdas) + template <typename T> + struct fn_traits<decltype(void(&T::operator())), T> + : public fn_object_traits<void, decltype(&T::operator())> {}; + + // Specialisation for a member function in a non-functor class + template <typename ReturnT, typename ClassT, typename... ArgTs> + struct fn_traits<void, ReturnT (ClassT::*)(ArgTs...)> + : function_traits<ReturnT(ClassT, ArgTs...)> {}; + + // Specialisation for a const member function + template <typename ReturnT, typename ClassT, typename... ArgTs> + struct fn_traits<void, 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<void, 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<void, ReturnT (ClassT::*)(ArgTs...) &&> + : function_traits<ReturnT(ClassT&&, ArgTs...)> {}; + + // Specialisation for a pointer-to-member + template <typename ReturnT, typename ClassT> + struct fn_traits<void, ReturnT ClassT::*> + : function_traits<ReturnT&(ClassT)> {}; + + // Specialisation for a const pointer-to-member + template <typename ReturnT, typename ClassT> + struct fn_traits<void, const ReturnT ClassT::*> + : function_traits<const ReturnT&(ClassT)> {}; +} // namespace _impl + +template <typename FnT> +using fn_return_t = typename function_traits<FnT>::return_type; + +template <typename FnT, int ArgN = 0> +using fn_arg_t = + std::tuple_element_t<ArgN, typename function_traits<FnT>::arg_types>; + +} // namespace Quotient diff --git a/lib/qt_connection_util.h b/lib/qt_connection_util.h index 46294499..86593cc8 100644 --- a/lib/qt_connection_util.h +++ b/lib/qt_connection_util.h @@ -3,7 +3,7 @@ #pragma once -#include "util.h" +#include "function_traits.h" #include <QtCore/QPointer> diff --git a/lib/util.cpp b/lib/util.cpp index 2dfb09a6..03ebf325 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -135,35 +135,3 @@ int Quotient::patchVersion() { return Quotient_VERSION_PATCH; } - -// Tests for function_traits<> - -using namespace Quotient; - -int f_(); -static_assert(std::is_same<fn_return_t<decltype(f_)>, int>::value, - "Test fn_return_t<>"); - -void f1_(int, QString); -static_assert(std::is_same<fn_arg_t<decltype(f1_), 1>, QString>::value, - "Test fn_arg_t<>"); - -struct Fo { - int operator()(); - static constexpr auto l = [] { return 0.0f; }; -}; -static_assert(std::is_same<fn_return_t<Fo>, int>::value, - "Test return type of function object"); -static_assert(std::is_same<fn_return_t<decltype(Fo::l)>, float>::value, - "Test return type of lambda"); - -struct Fo1 { - void operator()(int); -}; -static_assert(std::is_same<fn_arg_t<Fo1>, int>(), - "Test fn_arg_t defaulting to first argument"); - -template <typename T> -static QString ft(T&&); -static_assert(std::is_same<fn_arg_t<decltype(ft<QString>)>, QString&&>(), - "Test function templates"); @@ -189,58 +189,6 @@ inline auto merge(T1& lhs, const Omittable<T2>& rhs) return true; } -namespace _impl { - template <typename AlwaysVoid, 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<void, 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...>; - // Doesn't (and there's no plan to make it) work for "classic" - // member functions (i.e. outside of functors). - // See also the comment for wrap_in_function() below - using function_type = std::function<ReturnT(ArgTs...)>; -}; - -namespace _impl { - // Specialisation for function objects with (non-overloaded) operator() - // (this includes non-generic lambdas) - template <typename T> - struct fn_traits<decltype(void(&T::operator())), T> - : public fn_traits<void, decltype(&T::operator())> {}; - - // Specialisation for a member function - template <typename ReturnT, typename ClassT, typename... ArgTs> - struct fn_traits<void, ReturnT (ClassT::*)(ArgTs...)> - : function_traits<ReturnT(ArgTs...)> {}; - - // Specialisation for a const member function - template <typename ReturnT, typename ClassT, typename... ArgTs> - struct fn_traits<void, ReturnT (ClassT::*)(ArgTs...) const> - : function_traits<ReturnT(ArgTs...)> {}; -} // namespace _impl - -template <typename FnT> -using fn_return_t = typename function_traits<FnT>::return_type; - -template <typename FnT, int ArgN = 0> -using fn_arg_t = - std::tuple_element_t<ArgN, typename function_traits<FnT>::arg_types>; - inline constexpr auto operator"" _ls(const char* s, std::size_t size) { return QLatin1String(s, int(size)); |