aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/util.cpp64
-rw-r--r--lib/util.h68
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
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 <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)
{