diff options
author | Kitsune Ral <Kitsune-Ral@users.sf.net> | 2018-12-06 21:23:07 +0900 |
---|---|---|
committer | Kitsune Ral <Kitsune-Ral@users.sf.net> | 2018-12-08 16:25:21 +0900 |
commit | be4a16cd4188ebeeba60768deadd88de5cc5be7b (patch) | |
tree | e2917b1351e936077f8222c291a760b621f04e59 | |
parent | c665883be52016be51f5b0a902e43885b024a8ac (diff) | |
download | libquotient-be4a16cd4188ebeeba60768deadd88de5cc5be7b.tar.gz libquotient-be4a16cd4188ebeeba60768deadd88de5cc5be7b.zip |
function_traits<>: support any arity; add compile-time tests
-rw-r--r-- | lib/util.cpp | 64 | ||||
-rw-r--r-- | 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<fn_return_t<decltype(f)>, int>::value, + "Test fn_return_t<>"); + +void f1(int); +static_assert(function_traits<decltype(f1)>::arg_number == 1, + "Test fn_arg_number"); + +void f2(int, QString); +static_assert(std::is_same<fn_arg_t<decltype(f2), 1>, QString>::value, + "Test fn_arg_t<>"); + +struct S { int mf(); }; +static_assert(is_callable_v<decltype(&S::mf)>, "Test member function"); +static_assert(returns<int, decltype(&S::mf)>(), "Test returns<> with member function"); + +struct Fo { void operator()(int); }; +static_assert(function_traits<Fo>::arg_number == 1, "Test function object 1"); +static_assert(is_callable_v<Fo>, "Test is_callable<>"); +static_assert(std::is_same<fn_arg_t<Fo>, int>(), + "Test fn_arg_t defaulting to first argument"); + +static auto l = [] { return 1; }; +static_assert(is_callable_v<decltype(l)>, "Test is_callable_v<> with lambda"); +static_assert(std::is_same<fn_return_t<decltype(l)>, int>::value, + "Test fn_return_t<> with lambda"); + +template <typename T> +struct fn_object +{ + static int smf(double) { return 0; } +}; +template <> +struct fn_object<QString> +{ + void operator()(QString); +}; +static_assert(is_callable_v<fn_object<QString>>, "Test function object"); +static_assert(returns<void, fn_object<QString>>(), + "Test returns<> with function object"); +static_assert(!is_callable_v<fn_object<int>>, "Test non-function object"); +// FIXME: These two don't work +//static_assert(is_callable_v<decltype(&fn_object<int>::smf)>, +// "Test static member function"); +//static_assert(returns<int, decltype(&fn_object<int>::smf)>(), +// "Test returns<> with static member function"); + +template <typename T> +QString ft(T&&); +static_assert(std::is_same<fn_arg_t<decltype(ft<QString>)>, QString&&>(), + "Test function templates"); + +#ifdef Q_CC_CLANG +#pragma clang diagnostic pop +#endif @@ -119,41 +119,69 @@ namespace QMatrixClient bool _omitted = false; }; + namespace _impl { + template <typename AlwaysVoid, typename> 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 <typename T> - struct function_traits : public function_traits<decltype(&T::operator())> - { }; // A generic function object that has (non-overloaded) operator() + struct function_traits : public _impl::fn_traits<void, T> {}; // Specialisation for a function - template <typename ReturnT, typename ArgT> - struct function_traits<ReturnT(ArgT)> + template <typename ReturnT, typename... ArgTs> + struct function_traits<ReturnT(ArgTs...)> { + static constexpr auto is_callable = true; using return_type = ReturnT; - using arg_type = ArgT; + using arg_types = std::tuple<ArgTs..., void>; + static constexpr auto arg_number = std::tuple_size<arg_types>::value - 1; }; - // Specialisation for a member function - template <typename ReturnT, typename ClassT, typename ArgT> - struct function_traits<ReturnT(ClassT::*)(ArgT)> - : function_traits<ReturnT(ArgT)> - { }; + namespace _impl { + template <typename AlwaysVoid, typename T> + struct fn_traits + { + static constexpr auto is_callable = false; + }; + + template <typename T> + struct fn_traits<decltype(void(T::operator())), T> + : public fn_traits<void, decltype(T::operator())> + { }; // A generic function object that has (non-overloaded) operator() - // Specialisation for a const member function - template <typename ReturnT, typename ClassT, typename ArgT> - struct function_traits<ReturnT(ClassT::*)(ArgT) const> - : function_traits<ReturnT(ArgT)> - { }; + // 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> - using fn_arg_t = typename function_traits<FnT>::arg_type; + template <typename FnT, int ArgN = 0> + using fn_arg_t = + std::tuple_element_t<ArgN, typename function_traits<FnT>::arg_types>; + + template <typename R, typename FnT> + constexpr bool returns() + { + return std::is_same<fn_return_t<FnT>, R>::value; + } + + // Poor-man's is_invokable + template <typename T> + constexpr auto is_callable_v = function_traits<T>::is_callable; inline auto operator"" _ls(const char* s, std::size_t size) { |