1 /***************************************************************************
2 * Copyright (C) 2005-2020 by the Quassel Project *
3 * devel@quassel-irc.org *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) version 3. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
26 #include <type_traits>
29 #include <boost/optional.hpp>
33 #include <QVariantList>
35 // ---- Function traits --------------------------------------------------------------------------------------------------------------------
39 /// @cond DOXYGEN_CANNOT_PARSE_THIS
42 template<typename Func>
43 struct FuncHelper : public FuncHelper<decltype(&Func::operator())>
46 // Overload for free function
47 template<typename R, typename... Args>
48 struct FuncHelper<R(*)(Args...)>
50 using FunctionType = std::function<R(Args...)>;
52 using ArgsTuple = std::tuple<Args...>;
55 // Overload for member function with non-const call operator
56 template<typename C, typename R, typename... Args>
57 struct FuncHelper<R(C::*)(Args...)> : public FuncHelper<R(*)(Args...)>
62 // Overload for member function with const call operator
63 template<typename C, typename R, typename... Args>
64 struct FuncHelper<R(C::*)(Args...) const> : public FuncHelper<R(C::*)(Args...)>
72 * Provides traits for the given callable.
74 template<typename Callable>
75 using FunctionTraits = detail::FuncHelper<Callable>;
77 // ---- Invoke function with argument list -------------------------------------------------------------------------------------------------
81 // Helper for invoking the callable, wrapping its return value in a QVariant (default-constructed if callable returns void).
82 // The correct overload is selected via SFINAE.
83 template<typename Callable, typename ...Args>
84 auto invokeWithArgs(const Callable& c, Args&&... args)
85 -> std::enable_if_t<std::is_void<typename FunctionTraits<Callable>::ReturnType>::value, QVariant>
87 c(std::forward<Args>(args)...);
91 template<typename Callable, typename ...Args>
92 auto invokeWithArgs(const Callable& c, Args&&... args)
93 -> std::enable_if_t<!std::is_void<typename FunctionTraits<Callable>::ReturnType>::value, QVariant>
95 return QVariant::fromValue(c(std::forward<Args>(args)...));
98 // Helper for unpacking the argument list via an index sequence
99 template<typename Callable, std::size_t ...Is, typename ArgsTuple = typename FunctionTraits<Callable>::ArgsTuple>
100 boost::optional<QVariant> invokeWithArgsList(const Callable& c, const QVariantList& args, std::index_sequence<Is...>)
102 // Sanity check that all types can be converted
103 std::array<bool, std::tuple_size<ArgsTuple>::value> convertible{{args[Is].canConvert<std::decay_t<std::tuple_element_t<Is, ArgsTuple>>>()...}};
104 for (size_t i = 0; i < convertible.size(); ++i) {
105 if (!convertible[i]) {
106 qWarning() << "Cannot convert parameter" << i << "from type" << args[static_cast<int>(i)].typeName() << "to expected argument type";
111 // Invoke callable with unmarshalled arguments
112 return invokeWithArgs(c, args[Is].value<std::decay_t<std::tuple_element_t<Is, ArgsTuple>>>()...);
118 * Invokes the given callable with the arguments contained in the given variant list.
120 * The types contained in the given QVariantList are converted to the types expected by the callable.
121 * If the invocation is successful, the returned optional contains a QVariant with the return value,
122 * or an invalid QVariant if the callable returns void.
123 * If the conversion fails, or if the argument count does not match, this function returns boost::none
124 * and the callable is not invoked.
127 * @param args Arguments to be given to the callable
128 * @returns An optional containing a QVariant with the return value if the callable could be invoked with
129 * the given list of arguments; otherwise boost::none
131 template<typename Callable>
132 boost::optional<QVariant> invokeWithArgsList(const Callable& c, const QVariantList& args)
134 using ArgsTuple = typename FunctionTraits<Callable>::ArgsTuple;
135 constexpr auto tupleSize = std::tuple_size<ArgsTuple>::value;
137 if (tupleSize != args.size()) {
138 qWarning().nospace() << "Argument count mismatch! Expected: " << tupleSize << ", actual: " << args.size();
141 return detail::invokeWithArgsList(c, args, std::make_index_sequence<tupleSize>{});
145 * Invokes the given member function pointer on the given object with the arguments contained in the given variant list.
147 * The types contained in the given QVariantList are converted to the types expected by the member function.
148 * If the invocation is successful, the returned optional contains a QVariant with the return value,
149 * or an invalid QVariant if the member function returns void.
150 * If the conversion fails, or if the argument count does not match, this function returns boost::none
151 * and the member function is not invoked.
154 * @param args Arguments to be given to the member function
155 * @returns An optional containing a QVariant with the return value if the member function could be invoked with
156 * the given list of arguments; otherwise boost::none
158 template<typename R, typename C, typename ...Args>
159 boost::optional<QVariant> invokeWithArgsList(C* object, R(C::*func)(Args...), const QVariantList& args)
161 if (sizeof...(Args) != args.size()) {
162 qWarning().nospace() << "Argument count mismatch! Expected: " << sizeof...(Args) << ", actual: " << args.size();
165 return detail::invokeWithArgsList([object, func](Args&&... args) {
166 return (object->*func)(std::forward<decltype(args)>(args)...);
167 }, args, std::make_index_sequence<sizeof...(Args)>{});