sigproxy: Modernize RPC calls (remote signals)
[quassel.git] / src / common / funchelpers.h
1 /***************************************************************************
2  *   Copyright (C) 2005-2018 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
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.                                           *
9  *                                                                         *
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.                          *
14  *                                                                         *
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  ***************************************************************************/
20
21 #pragma once
22
23 #include <array>
24 #include <functional>
25 #include <tuple>
26 #include <type_traits>
27 #include <utility>
28
29 #include <QDebug>
30 #include <QVariantList>
31
32 // ---- Function traits --------------------------------------------------------------------------------------------------------------------
33
34 namespace detail {
35
36 /// @cond DOXYGEN_CANNOT_PARSE_THIS
37
38 // Primary template
39 template<typename Func>
40 struct FuncHelper : public FuncHelper<decltype(&Func::operator())>
41 {};
42
43 // Overload for free function
44 template<typename R, typename... Args>
45 struct FuncHelper<R(*)(Args...)>
46 {
47     using FunctionType = std::function<R(Args...)>;
48     using ReturnType = R;
49     using ArgsTuple = std::tuple<Args...>;
50 };
51
52 // Overload for member function with non-const call operator
53 template<typename C, typename R, typename... Args>
54 struct FuncHelper<R(C::*)(Args...)> : public FuncHelper<R(*)(Args...)>
55 {
56     using ClassType = C;
57 };
58
59 // Overload for member function with const call operator
60 template<typename C, typename R, typename... Args>
61 struct FuncHelper<R(C::*)(Args...) const> : public FuncHelper<R(C::*)(Args...)>
62 {};
63
64 /// @endcond
65
66 }  // namespace detail
67
68 /**
69  * Provides traits for the given callable.
70  */
71 template<typename Callable>
72 using FunctionTraits = detail::FuncHelper<Callable>;
73
74 // ---- Invoke function with argument list -------------------------------------------------------------------------------------------------
75
76 namespace detail {
77
78 // Helper for unpacking the argument list via an index sequence
79 template<typename Callable, std::size_t ...Is, typename ArgsTuple = typename FunctionTraits<Callable>::ArgsTuple>
80 bool invokeWithArgsList(const Callable& c, const QVariantList& args, std::index_sequence<Is...>)
81 {
82     // Sanity check that all types can be converted
83     std::array<bool, std::tuple_size<ArgsTuple>::value> convertible{{args[Is].canConvert<std::decay_t<std::tuple_element_t<Is, ArgsTuple>>>()...}};
84     for (size_t i = 0; i < convertible.size(); ++i) {
85         if (!convertible[i]) {
86             qWarning() << "Cannot convert parameter" << i << "from type" << args[static_cast<int>(i)].typeName() << "to expected argument type";
87             return false;
88         }
89     }
90
91     // Invoke callable
92     c(args[Is].value<std::decay_t<std::tuple_element_t<Is, ArgsTuple>>>()...);
93     return true;
94 }
95
96 }  // detail
97
98 /**
99  * Invokes the given callable with the arguments contained in the given variant list.
100  *
101  * The types contained in the given QVariantList are converted to the types expected by the callable.
102  * If the conversion fails, or if the argument count does not match, this function returns false and
103  * the callable is not invoked.
104  *
105  * @param c    Callable
106  * @param args Arguments to be given to the callable
107  * @returns true if the callable could be invoked with the given list of arguments
108  */
109 template<typename Callable>
110 bool invokeWithArgsList(const Callable& c, const QVariantList& args)
111 {
112     using ArgsTuple = typename FunctionTraits<Callable>::ArgsTuple;
113     constexpr auto tupleSize = std::tuple_size<ArgsTuple>::value;
114
115     if (tupleSize != args.size()) {
116         qWarning().nospace() << "Argument count mismatch! Expected: " << tupleSize << ", actual: " << args.size();
117         return false;
118     }
119     return detail::invokeWithArgsList(c, args, std::make_index_sequence<tupleSize>{});
120 }