JSON RPC
JSON-RPC 2.0 library for C++
details.h
Go to the documentation of this file.
1#ifndef DE443A53_EEA9_4918_BCFB_AE76A19FB197
2#define DE443A53_EEA9_4918_BCFB_AE76A19FB197
3
4/**
5 * @file
6 * @brief Contains implementation details for the JSON RPC library.
7 *
8 * This file includes type traits for extracting function return types and argument types,
9 * as well as utilities for creating offset sequences and handling handler parameters.
10 * These details are used internally by the JSON RPC library to facilitate various operations.
11 *
12 * It includes:
13 * - Type traits for function return types and argument types.
14 * - Utilities for creating offset sequences.
15 * - Helper functions for handling handler parameters.
16 *
17 * @internal
18 */
19
20#include <functional>
21#include <tuple>
22#include <type_traits>
23#include <utility>
24#include <nlohmann/json.hpp>
25#include "exception.h"
26
27/**
28 * @brief Contains the implementation details of the JSON RPC library.
29 * @internal
30 */
31namespace wwa::json_rpc::details {
32
33/**
34 * @defgroup function_traits Function Traits
35 * @brief Type traits for extracting function return types and argument types.
36 * @internal
37 * @{
38 */
39
40/**
41 * @brief Primary template for function traits.
42 *
43 * This template is specialized for various function types to extract the return type and argument types.
44 *
45 * @tparam T The function type.
46 */
47template<typename T>
48struct function_traits;
49
50/**
51 * @brief Specialization for function pointers.
52 *
53 * @tparam R The return type of the function.
54 * @tparam Args The argument types of the function.
55 */
56template<typename R, typename... Args>
57struct function_traits<R (*)(Args...)> {
58 using return_type = R; ///< The return type of the function.
59 using args_tuple = std::tuple<Args...>; ///< A tuple of the argument types.
60};
61
62/**
63 * @brief Specialization for noexcept function pointers.
64 *
65 * @tparam R The return type of the function.
66 * @tparam Args The argument types of the function.
67 */
68template<typename R, typename... Args>
69struct function_traits<R (*)(Args...) noexcept> : function_traits<R (*)(Args...)> {};
70
71/**
72 * @brief Specialization for member function pointers.
73 *
74 * @tparam R The return type of the member function.
75 * @tparam C The class type.
76 * @tparam Args The argument types of the member function.
77 */
78template<typename R, typename C, typename... Args>
79struct function_traits<R (C::*)(Args...)> {
80 using return_type = R; ///< The return type of the member function.
81 using args_tuple = std::tuple<Args...>; ///< A tuple of the argument types.
82};
83
84/**
85 * @brief Specialization for const member function pointers.
86 *
87 * @tparam R The return type of the member function.
88 * @tparam C The class type.
89 * @tparam Args The argument types of the member function.
90 */
91template<typename R, typename C, typename... Args>
92struct function_traits<R (C::*)(Args...) const> : function_traits<R (C::*)(Args...)> {};
93
94/**
95 * @brief Specialization for volatile member function pointers.
96 *
97 * @tparam R The return type of the member function.
98 * @tparam C The class type.
99 * @tparam Args The argument types of the member function.
100 */
101template<typename R, typename C, typename... Args>
102struct function_traits<R (C::*)(Args...) volatile> : function_traits<R (C::*)(Args...)> {};
103
104/**
105 * @brief Specialization for const volatile member function pointers.
106 *
107 * @tparam R The return type of the member function.
108 * @tparam C The class type.
109 * @tparam Args The argument types of the member function.
110 */
111template<typename R, typename C, typename... Args>
112struct function_traits<R (C::*)(Args...) const volatile> : function_traits<R (C::*)(Args...)> {};
113
114/**
115 * @brief Specialization for noexcept member function pointers.
116 *
117 * @tparam R The return type of the member function.
118 * @tparam C The class type.
119 * @tparam Args The argument types of the member function.
120 */
121template<typename R, typename C, typename... Args>
122struct function_traits<R (C::*)(Args...) noexcept> : function_traits<R (C::*)(Args...)> {};
123
124/**
125 * @brief Specialization for const noexcept member function pointers.
126 *
127 * @tparam R The return type of the member function.
128 * @tparam C The class type.
129 * @tparam Args The argument types of the member function.
130 */
131template<typename R, typename C, typename... Args>
132struct function_traits<R (C::*)(Args...) const noexcept> : function_traits<R (C::*)(Args...)> {};
133
134/**
135 * @brief Specialization for volatile noexcept member function pointers.
136 *
137 * @tparam R The return type of the member function.
138 * @tparam C The class type.
139 * @tparam Args The argument types of the member function.
140 */
141template<typename R, typename C, typename... Args>
142struct function_traits<R (C::*)(Args...) volatile noexcept> : function_traits<R (C::*)(Args...)> {};
143
144/**
145 * @brief Specialization for const volatile noexcept member function pointers.
146 *
147 * @tparam R The return type of the member function.
148 * @tparam C The class type.
149 * @tparam Args The argument types of the member function.
150 */
151template<typename R, typename C, typename... Args>
152struct function_traits<R (C::*)(Args...) const volatile noexcept> : function_traits<R (C::*)(Args...)> {};
153
154/**
155 * @brief Specialization for `std::function`.
156 *
157 * @tparam R The return type of the function.
158 * @tparam Args The argument types of the function.
159 */
160template<typename R, typename... Args>
161struct function_traits<std::function<R(Args...)>> {
162 using return_type = R; ///< The return type of the function.
163 using args_tuple = std::tuple<Args...>; ///< A tuple of the argument types.
164};
165
166/**
167 * @brief Specialization for functors (objects with `operator()`).
168 *
169 * @tparam T The functor type.
170 */
171template<typename T>
173private:
174 using call_type = function_traits<decltype(&T::operator())>; ///< Type traits for the call operator.
175
176public:
177 using return_type = typename call_type::return_type; ///< The return type of the functor.
178 using args_tuple = typename call_type::args_tuple; ///< A tuple of the argument types.
179};
180/** @} */
181
182/**
183 * @defgroup offset_sequence Offset Sequence
184 * @brief Utilities for creating offset sequences.
185 * @see https://devblogs.microsoft.com/oldnewthing/20200625-00/?p=103903
186 * @internal
187 * @{
188 */
189
190/**
191 * @brief Template for creating an offset sequence.
192 *
193 * @tparam N The offset value.
194 * @tparam Seq The sequence type.
195 */
196template<std::size_t N, typename Seq>
197struct offset_sequence;
198
199/**
200 * @brief Specialization for creating an offset sequence.
201 *
202 * @tparam N The offset value.
203 * @tparam Ints The integer sequence.
204 */
205template<std::size_t N, std::size_t... Ints>
207 using type = std::index_sequence<Ints + N...>; ///< The resulting offset sequence.
208};
209
210/**
211 * @brief Alias template for creating an offset sequence.
212 *
213 * @tparam N The offset value.
214 * @tparam Seq The sequence type.
215 */
216template<std::size_t N, typename Seq>
217using offset_sequence_t = typename offset_sequence<N, Seq>::type;
218
219/** @} */
220
221/**
222 * @defgroup param_helpers Parameter Helpers
223 * @brief Utilities for handling handler parameters.
224 * @internal
225 * @{
226 */
227
228/**
229 * @brief Creates a tuple containing the instance if it is a class pointer, or an empty tuple if it is a null pointer.
230 *
231 * @tparam C The type of the instance, which should be a class pointer or a null pointer.
232 * @param inst The instance to be included in the tuple.
233 * @return A tuple containing the instance if it is a class pointer, or an empty tuple if it is a null pointer.
234 *
235 * @note This function uses `constexpr` and `if constexpr` to ensure that the tuple is created at compile time if possible.
236 * @note The function requires that the type C is either a class pointer or a null pointer.
237 */
238template<typename C>
239constexpr auto make_inst_tuple(C inst)
241{
242 if constexpr (std::is_null_pointer_v<C>) {
243 return std::make_tuple();
244 }
245 else {
246 return std::make_tuple(inst);
247 }
248}
249
250/**
251 * @brief Creates a tuple from the provided JSON object based on the type of @a Extra.
252 *
253 * @tparam Extra The type to be extracted from the JSON object.
254 * @param extra The JSON object from which the tuple is created.
255 * @return A tuple containing the extracted value(s) from the JSON object.
256 * @retval std::tuple<> If @a Extra is `void`.
257 * @retval std::tuple<nlohmann::json> If @a Extra is `nlohmann::json`.
258 * @retval std::tuple<Extra> If @a Extra is a different type.
259 *
260 * @throws wwa::json_rpc::exception If the extraction of @a Extra from the JSON object @a extra fails.
261 */
262template<typename Extra>
264{
265 if constexpr (std::is_void_v<Extra>) {
266 return std::make_tuple();
267 }
268 else if constexpr (std::is_same_v<std::decay_t<Extra>, nlohmann::json>) {
269 return std::make_tuple(extra);
270 }
271 else {
272 try {
273 return std::make_tuple(extra.get<Extra>());
274 }
275 catch (const nlohmann::json::exception& e) {
277 }
278 }
279}
280
281/**
282 * @brief Invokes a function with the provided arguments handling `void` return type.
283 *
284 * @tparam F The type of the function.
285 * @tparam Tuple The type of the arguments tuple.
286 * @param f The function.
287 * @param tuple The arguments as a tuple.
288 * @return The result of the function converted to a JSON value.
289 * @retval nlohmann::json::null_t If the function returns void.
290 *
291 * @details This helper invokes the function with the arguments passed as a tuple.
292 * It uses the `if constexpr` construct to handle the case when the handler function returns void.
293 *
294 * The `if constexpr` construct allows for determinining at compile time whether @a f returns `void`.
295 * If the return type is `void`, `invoke_function` calls @a f and returns a JSON `null` value.
296 * If the return type is not `void`, `invoke_function` calls @a f and returns the result converted to a JSON value.
297 */
298template<typename F, typename Tuple>
300{
302
303 if constexpr (std::is_void_v<ReturnType>) {
305 return nullptr;
306 }
307 else {
308 return std::apply(std::forward<F>(f), std::forward<Tuple>(tuple));
309 }
310}
311
312/**
313 * @brief Type alias for a tuple element with decay applied.
314 *
315 * This template alias retrieves the type of the @a I-th element in the tuple type @a A,
316 * and then applies `std::decay_t` to remove any references and cv-qualifiers.
317 *
318 * @tparam I The index of the element in the tuple.
319 * @tparam A The tuple type.
320 */
321template<std::size_t I, typename A>
323
324/**
325 * @brief Converts JSON parameters to a tuple of arguments based on the specified types.
326 *
327 * @tparam Extra An additional type that may be included in the conversion. If Extra is void, it is ignored.
328 * @tparam Args A tuple of argument types to which the JSON parameters will be converted.
329 * @tparam Indices A parameter pack representing the indices of the arguments.
330 * @param params The JSON object containing the parameters to be converted.
331 * @return A tuple containing the converted arguments.
332 *
333 * @throws wwa::json_rpc::exception If the conversion of any parameter from the JSON object fails.
334 *
335 * @details The function attempts to convert each parameter in the JSON object @a params to the corresponding type in @a Args and returns them as a tuple.
336 * It uses `std::index_sequence` to unpack the indices and access the corresponding parameters in @a params.
337 *
338 * We support two types of handler functions:
339 * 1. `ReturnType handler(const Extra& extra, Arguments... args);` (@a Extra is not `void`, @a Indices are [1..`sizeof...(Args)-1`])
340 * 2. `ReturnType handler(Arguments... args);` (@a Extra is `void`, @a Indices are [0..`sizeof...(Args)-1`])
341 *
342 * Because @a Args correspond to the function arguments, @a Args contains both @a Extra and @a Arguments for the first type of handler; therefore, `Args[i]` will be the type of the `params[i-1]`.
343 * For the second type of handler, @a Args contains only @a Arguments, and `Args[i]` will be the type of the `params[i]`.
344 */
345template<typename Extra, typename Args, std::size_t... Indices>
346constexpr auto convert_args(const nlohmann::json& params, std::index_sequence<Indices...>)
347{
348 constexpr std::size_t offset = std::is_void_v<Extra> ? 0 : 1;
349 try {
350 return std::make_tuple(params[Indices - offset].template get<tuple_element<Indices, Args>>()...);
351 }
352 catch (const nlohmann::json::exception& e) {
353 throw wwa::json_rpc::exception(wwa::json_rpc::exception::INVALID_PARAMS, e.what());
354 }
355}
356
357/** @} */
358
359} // namespace wwa::json_rpc::details
360
361#endif /* DE443A53_EEA9_4918_BCFB_AE76A19FB197 */
Private implementation of the JSON RPC dispatcher class.
A class that manages JSON RPC method handlers and processes JSON RPC requests.
Definition dispatcher.h:77
dispatcher & operator=(dispatcher &&rhs)=default
Move assignment operator.
dispatcher(dispatcher &&rhs)=default
Move constructor.
virtual void on_request(const nlohmann::json &extra)
Invoked when a request is received.
void add_ex(std::string_view method, F &&f, C instance)
Adds a method handler with an extra parameter and a class instance.
Definition dispatcher.h:277
std::string parse_and_process_request(const std::string &request, const nlohmann::json &extra=nlohmann::json::object())
Parses and processes a JSON RPC request.
constexpr auto create_closure(C inst, F &&f) const
Creates a closure for invoking a member function with JSON parameters.
Definition dispatcher.h:455
std::string process_request(const nlohmann::json &request, const nlohmann::json &extra=nlohmann::json::object())
Processes a JSON RPC request.
virtual void on_request_processed(const std::string &method, int code, const nlohmann::json &extra)
Invoked after the method handler is called.
void add_internal_method(std::string_view method, handler_t &&handler)
Adds a method handler for the specified method.
void add(std::string_view method, F &&f)
Adds a method handler f for the method method.
Definition dispatcher.h:192
virtual void on_method(const std::string &method, const nlohmann::json &extra)
Invoked right before the method handler is called.
void add(std::string_view method, F &&f, C instance)
Adds a method to the dispatcher with the specified instance and function.
Definition dispatcher.h:210
dispatcher()
Class constructor.
dispatcher(const dispatcher &)=delete
dispatcher & operator=(const dispatcher &)=delete
virtual ~dispatcher()
Class destructor.
void add_ex(std::string_view method, F &&f)
Adds a method handler with an extra parameter.
Definition dispatcher.h:254
std::unique_ptr< dispatcher_private > d_ptr
Pointer to the implementation (Pimpl idiom).
Definition dispatcher.h:413
#define WWA_JSONRPC_EXPORT
Macro for exporting symbols when building the library dynamically or importing symbols when using the...
Definition export.h:42
constexpr auto convert_args(const nlohmann::json &params, std::index_sequence< Indices... >)
Converts JSON parameters to a tuple of arguments based on the specified types.
Definition details.h:346
constexpr auto make_inst_tuple(C inst)
Creates a tuple containing the instance if it is a class pointer, or an empty tuple if it is a null p...
Definition details.h:239
Contains the implementation details of the JSON RPC library.
Definition details.h:31
Primary template for function traits.
Definition details.h:172