From be4a16cd4188ebeeba60768deadd88de5cc5be7b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 6 Dec 2018 21:23:07 +0900 Subject: function_traits<>: support any arity; add compile-time tests --- lib/util.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/util.h | 68 ++++++++++++++++++++++++++++++++++++++++++------------------ 2 files changed, 112 insertions(+), 20 deletions(-) diff --git a/lib/util.cpp b/lib/util.cpp index af06013c..4c176fc7 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -75,3 +75,67 @@ QString QMatrixClient::cacheLocation(const QString& dirName) dir.mkpath(cachePath); return cachePath; } + +// Tests for function_traits<> + +#ifdef Q_CC_CLANG +#pragma clang diagnostic push +#pragma ide diagnostic ignored "OCSimplifyInspection" +#endif +using namespace QMatrixClient; + +int f(); +static_assert(std::is_same, int>::value, + "Test fn_return_t<>"); + +void f1(int); +static_assert(function_traits::arg_number == 1, + "Test fn_arg_number"); + +void f2(int, QString); +static_assert(std::is_same, QString>::value, + "Test fn_arg_t<>"); + +struct S { int mf(); }; +static_assert(is_callable_v, "Test member function"); +static_assert(returns(), "Test returns<> with member function"); + +struct Fo { void operator()(int); }; +static_assert(function_traits::arg_number == 1, "Test function object 1"); +static_assert(is_callable_v, "Test is_callable<>"); +static_assert(std::is_same, int>(), + "Test fn_arg_t defaulting to first argument"); + +static auto l = [] { return 1; }; +static_assert(is_callable_v, "Test is_callable_v<> with lambda"); +static_assert(std::is_same, int>::value, + "Test fn_return_t<> with lambda"); + +template +struct fn_object +{ + static int smf(double) { return 0; } +}; +template <> +struct fn_object +{ + void operator()(QString); +}; +static_assert(is_callable_v>, "Test function object"); +static_assert(returns>(), + "Test returns<> with function object"); +static_assert(!is_callable_v>, "Test non-function object"); +// FIXME: These two don't work +//static_assert(is_callable_v::smf)>, +// "Test static member function"); +//static_assert(returns::smf)>(), +// "Test returns<> with static member function"); + +template +QString ft(T&&); +static_assert(std::is_same)>, QString&&>(), + "Test function templates"); + +#ifdef Q_CC_CLANG +#pragma clang diagnostic pop +#endif diff --git a/lib/util.h b/lib/util.h index 88c756a1..3f5bcb5f 100644 --- a/lib/util.h +++ b/lib/util.h @@ -119,41 +119,69 @@ namespace QMatrixClient bool _omitted = false; }; + namespace _impl { + template struct fn_traits; + } + /** Determine traits of an arbitrary function/lambda/functor - * This only works with arity of 1 (1-argument) for now but is extendable - * to other cases. Also, doesn't work with generic lambdas and function - * objects that have operator() overloaded + * 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 - struct function_traits : public function_traits - { }; // A generic function object that has (non-overloaded) operator() + struct function_traits : public _impl::fn_traits {}; // Specialisation for a function - template - struct function_traits + template + struct function_traits { + static constexpr auto is_callable = true; using return_type = ReturnT; - using arg_type = ArgT; + using arg_types = std::tuple; + static constexpr auto arg_number = std::tuple_size::value - 1; }; - // Specialisation for a member function - template - struct function_traits - : function_traits - { }; + namespace _impl { + template + struct fn_traits + { + static constexpr auto is_callable = false; + }; + + template + struct fn_traits + : public fn_traits + { }; // A generic function object that has (non-overloaded) operator() - // Specialisation for a const member function - template - struct function_traits - : function_traits - { }; + // Specialisation for a member function + template + struct fn_traits + : function_traits + { }; + + // Specialisation for a const member function + template + struct fn_traits + : function_traits + { }; + } // namespace _impl template using fn_return_t = typename function_traits::return_type; - template - using fn_arg_t = typename function_traits::arg_type; + template + using fn_arg_t = + std::tuple_element_t::arg_types>; + + template + constexpr bool returns() + { + return std::is_same, R>::value; + } + + // Poor-man's is_invokable + template + constexpr auto is_callable_v = function_traits::is_callable; inline auto operator"" _ls(const char* s, std::size_t size) { -- cgit v1.2.3