rpc.hpp 0.8.1
Simple RPC Header-Only Library
rpc_rapidjson.hpp
Go to the documentation of this file.
1
36
37#pragma once
38
39#include "../rpc.hpp"
40
41#include <rapidjson/document.h>
42#include <rapidjson/stringbuffer.h>
43#include <rapidjson/writer.h>
44
45namespace rpc_hpp
46{
47namespace adapters
48{
49 class rapidjson_adapter;
50
51 template<>
53 {
54 using serial_t = rapidjson::Document;
55 using bytes_t = std::string;
56 };
57
58 class rapidjson_adapter : public detail::serial_adapter_base<rapidjson_adapter>
59 {
60 public:
61 [[nodiscard]] static std::string to_bytes(rapidjson::Document&& serial_obj)
62 {
63 rapidjson::StringBuffer buffer{};
64 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
65 std::move(serial_obj).Accept(writer);
66 return buffer.GetString();
67 }
68
69 [[nodiscard]] static std::optional<rapidjson::Document> from_bytes(std::string&& bytes)
70 {
71 rapidjson::Document d{};
72 d.SetObject();
73 d.Parse(std::move(bytes).c_str());
74
75 if (d.HasParseError())
76 {
77 return std::nullopt;
78 }
79
80 if (const auto ex_it = d.FindMember("except_type"); ex_it != d.MemberEnd())
81 {
82 if (const auto& ex_val = ex_it->value;
83 !ex_val.IsInt() || (ex_val.GetInt() != 0 && !d.HasMember("err_mesg")))
84 {
85 return std::nullopt;
86 }
87
88 // Objects with exceptions can be otherwise empty
89 return std::make_optional(std::move(d));
90 }
91
92 if (const auto fname_it = d.FindMember("func_name"); fname_it == d.MemberEnd()
93 || !fname_it->value.IsString() || fname_it->value.GetStringLength() == 0)
94 {
95 return std::nullopt;
96 }
97
98 if (const auto args_it = d.FindMember("args");
99 args_it == d.MemberEnd() || !args_it->value.IsArray())
100 {
101 return std::nullopt;
102 }
103
104 return std::make_optional(std::move(d));
105 }
106
107 static rapidjson::Document empty_object()
108 {
109 rapidjson::Document d{};
110 d.SetObject();
111 return d;
112 }
113
114 template<typename R, typename... Args>
115 [[nodiscard]] static rapidjson::Document serialize_pack(
116 const detail::packed_func<R, Args...>& pack)
117 {
118 rapidjson::Document d{};
119 auto& alloc = d.GetAllocator();
120 d.SetObject();
121 d.AddMember("func_name",
122 rapidjson::Value{}.SetString(pack.get_func_name().c_str(), alloc), alloc);
123
124 if constexpr (!std::is_void_v<R>)
125 {
126 rapidjson::Value result{};
127
128 if (pack)
129 {
130 if constexpr (std::is_arithmetic_v<R>)
131 {
132 result.Set<R>(pack.get_result());
133 }
134 else if constexpr (std::is_same_v<R, std::string>)
135 {
136 result.SetString(pack.get_result().c_str(), alloc);
137 }
138 else if constexpr (rpc_hpp::detail::is_container_v<R>)
139 {
140 const auto& container = pack.get_result();
141 result.SetArray();
142 result.Reserve(static_cast<rapidjson::SizeType>(container.size()), alloc);
143
144 for (const auto& val : container)
145 {
146 result.PushBack(val, alloc);
147 }
148 }
149 else if constexpr (rpc_hpp::detail::is_serializable_v<rapidjson_adapter, R>)
150 {
151 const rapidjson::Document tmp =
152 R::template serialize<rapidjson_adapter>(pack.get_result());
153
154 result.CopyFrom(tmp, alloc);
155 }
156 else
157 {
158 result = serialize<R>(pack.get_result(), alloc);
159 }
160 }
161
162 d.AddMember("result", result, alloc);
163 }
164
165 rapidjson::Value args{};
166 args.SetArray();
167 args.Reserve(static_cast<rapidjson::SizeType>(sizeof...(Args)), alloc);
168 detail::for_each_tuple(pack.get_args(),
169 [&args, &alloc](auto&& elem)
170 { push_args(std::forward<decltype(elem)>(elem), args, alloc); });
171
172 d.AddMember("args", std::move(args), alloc);
173 return d;
174 }
175
176 template<typename R, typename... Args>
177 [[nodiscard]] static detail::packed_func<R, Args...> deserialize_pack(
178 const rapidjson::Document& serial_obj)
179 {
180 const auto& args_val = serial_obj["args"];
181 [[maybe_unused]] unsigned arg_counter = 0;
182 typename detail::packed_func<R, Args...>::args_t args{ parse_args<Args>(
183 args_val, arg_counter)... };
184
185 if constexpr (std::is_void_v<R>)
186 {
187 detail::packed_func<void, Args...> pack(
188 serial_obj["func_name"].GetString(), std::move(args));
189
190 if (serial_obj.HasMember("except_type"))
191 {
192 pack.set_exception(serial_obj["err_mesg"].GetString(),
193 static_cast<exception_type>(serial_obj["except_type"].GetInt()));
194 }
195
196 return pack;
197 }
198 else
199 {
200 if (serial_obj.HasMember("result") && !serial_obj["result"].IsNull())
201 {
202 const rapidjson::Value& result = serial_obj["result"];
203 return detail::packed_func<R, Args...>(
204 serial_obj["func_name"].GetString(), parse_arg<R>(result), std::move(args));
205 }
206
207 detail::packed_func<R, Args...> pack(
208 serial_obj["func_name"].GetString(), std::nullopt, std::move(args));
209
210 if (serial_obj.HasMember("except_type"))
211 {
212 pack.set_exception(serial_obj["err_mesg"].GetString(),
213 static_cast<exception_type>(serial_obj["except_type"].GetInt()));
214 }
215
216 return pack;
217 }
218 }
219
220 [[nodiscard]] static std::string get_func_name(const rapidjson::Document& serial_obj)
221 {
222 return serial_obj["func_name"].GetString();
223 }
224
225 [[nodiscard]] static rpc_exception extract_exception(const rapidjson::Document& serial_obj)
226 {
227 return rpc_exception{ serial_obj["err_mesg"].GetString(),
228 static_cast<exception_type>(serial_obj["except_type"].GetInt()) };
229 }
230
231 static void set_exception(rapidjson::Document& serial_obj, const rpc_exception& ex)
232 {
233 auto& alloc = serial_obj.GetAllocator();
234
235 if (const auto ex_it = serial_obj.FindMember("except_type");
236 ex_it != serial_obj.MemberEnd())
237 {
238 ex_it->value.SetInt(static_cast<int>(ex.get_type()));
239 serial_obj["err_mesg"].SetString(ex.what(), alloc);
240 }
241 else
242 {
243 serial_obj.AddMember("except_type", static_cast<int>(ex.get_type()), alloc);
244 serial_obj.AddMember(
245 "err_mesg", rapidjson::Value{}.SetString(ex.what(), alloc), alloc);
246 }
247 }
248
249 template<typename T>
250 static rapidjson::Value serialize(
251 const T& val, rapidjson::MemoryPoolAllocator<>& alloc) = delete;
252
253 template<typename T>
254 static T deserialize(const rapidjson::Value& serial_obj) = delete;
255
256 private:
257 // nodiscard because this function is pointless without checking the bool
258 template<typename T>
259 [[nodiscard]] static constexpr bool validate_arg(const rapidjson::Value& arg) noexcept
260 {
261 if constexpr (std::is_same_v<T, bool>)
262 {
263 return arg.IsBool();
264 }
265 else if constexpr (std::is_integral_v<T>)
266 {
267 if constexpr (std::is_signed_v<T>)
268 {
269 if constexpr (sizeof(T) < 8)
270 {
271 return arg.IsInt();
272 }
273 else
274 {
275 return arg.IsInt64();
276 }
277 }
278 else
279 {
280 if constexpr (sizeof(T) < 8)
281 {
282 return arg.IsUint();
283 }
284 else
285 {
286 return arg.IsUint64();
287 }
288 }
289 }
290 else if constexpr (std::is_same_v<T, float>)
291 {
292 return arg.IsFloat();
293 }
294 else if constexpr (std::is_same_v<T, double>)
295 {
296 return arg.IsDouble();
297 }
298 else if constexpr (std::is_same_v<T, std::string>)
299 {
300 return arg.IsString();
301 }
302 else if constexpr (rpc_hpp::detail::is_container_v<T>)
303 {
304 return arg.IsArray();
305 }
306 else
307 {
308 return !arg.IsNull();
309 }
310 }
311
312 // nodiscard because expect_type is consumed by the function
313 [[nodiscard]] static std::string mismatch_message(
314 std::string&& expect_type, const rapidjson::Value& obj)
315 {
316 const auto get_type_str = [&obj]() noexcept
317 {
318 if (obj.IsNull())
319 {
320 return "null";
321 }
322
323 if (obj.IsInt64())
324 {
325 return "int64";
326 }
327
328 if (obj.IsInt())
329 {
330 return "int";
331 }
332
333 if (obj.IsUint64())
334 {
335 return "uint64";
336 }
337
338 if (obj.IsUint())
339 {
340 return "uint";
341 }
342
343 if (obj.IsDouble())
344 {
345 return "double";
346 }
347
348 if (obj.IsFloat())
349 {
350 return "float";
351 }
352
353 if (obj.IsBool())
354 {
355 return "bool";
356 }
357
358 if (obj.IsString())
359 {
360 return "string";
361 }
362
363 if (obj.IsArray())
364 {
365 return "array";
366 }
367
368 if (obj.IsObject())
369 {
370 return "object";
371 }
372
373 return "unknown";
374 };
375
376 return { "rapidjson expected type: " + std::move(expect_type)
377 + ", got type: " + get_type_str() };
378 }
379
380 template<typename T>
381 static void push_arg(
382 T&& arg, rapidjson::Value& obj, rapidjson::MemoryPoolAllocator<>& alloc)
383 {
384 using no_ref_t = std::remove_cv_t<std::remove_reference_t<T>>;
385
386 if constexpr (std::is_same_v<no_ref_t, std::string>)
387 {
388 obj.SetString(arg.c_str(), alloc);
389 }
390 else if constexpr (std::is_arithmetic_v<no_ref_t>)
391 {
392 // Rapidjson does not have generic support for 8/16 bit numbers, so upgrade to 32-bit
393 if constexpr (
394 std::is_same_v<no_ref_t,
395 char> || std::is_same_v<no_ref_t, int8_t> || std::is_same_v<no_ref_t, int16_t>)
396 {
397 obj.SetInt(arg);
398 }
399 else if constexpr (std::is_same_v<no_ref_t,
400 uint8_t> || std::is_same_v<no_ref_t, uint16_t>)
401 {
402 obj.SetUint(arg);
403 }
404 else
405 {
406 obj.Set<no_ref_t>(arg);
407 }
408 }
409 else if constexpr (rpc_hpp::detail::is_container_v<no_ref_t>)
410 {
411 obj.SetArray();
412 obj.Reserve(static_cast<rapidjson::SizeType>(arg.size()), alloc);
413
414 for (auto&& val : arg)
415 {
416 push_args(std::forward<decltype(val)>(val), obj, alloc);
417 }
418 }
419 else if constexpr (rpc_hpp::detail::is_serializable_v<rapidjson_adapter, no_ref_t>)
420 {
421 const rapidjson::Document serialized =
422 no_ref_t::template serialize<rapidjson_adapter>(std::forward<T>(arg));
423
424 obj.CopyFrom(serialized, alloc);
425 }
426 else
427 {
428 obj = serialize<no_ref_t>(std::forward<T>(arg), alloc);
429 }
430 }
431
432 template<typename T>
433 static void push_args(
434 T&& arg, rapidjson::Value& obj_arr, rapidjson::MemoryPoolAllocator<>& alloc)
435 {
436 rapidjson::Value tmp{};
437 push_arg(std::forward<T>(arg), tmp, alloc);
438 obj_arr.PushBack(std::move(tmp), alloc);
439 }
440
441 // nodiscard because parsing can be expensive, and it makes no sense to not use the parsed result
442 template<typename T>
443 [[nodiscard]] static std::remove_cv_t<std::remove_reference_t<T>> parse_arg(
444 const rapidjson::Value& arg)
445 {
446 using no_ref_t = std::remove_cv_t<std::remove_reference_t<T>>;
447
448 if (!validate_arg<no_ref_t>(arg))
449 {
450 throw function_mismatch(mismatch_message(typeid(no_ref_t).name(), arg));
451 }
452
453 if constexpr (std::is_same_v<no_ref_t, std::string>)
454 {
455 return arg.GetString();
456 }
457 else if constexpr (std::is_arithmetic_v<no_ref_t>)
458 {
459 // Rapidjson does not have generic support for 8/16 bit numbers, so upgrade to 32-bit
460 if constexpr (
461 std::is_same_v<no_ref_t,
462 char> || std::is_same_v<no_ref_t, int8_t> || std::is_same_v<no_ref_t, int16_t>)
463 {
464 return static_cast<no_ref_t>(arg.GetInt());
465 }
466 else if constexpr (std::is_same_v<no_ref_t,
467 uint8_t> || std::is_same_v<no_ref_t, uint16_t>)
468 {
469 return static_cast<no_ref_t>(arg.GetUint());
470 }
471 else
472 {
473 return arg.Get<no_ref_t>();
474 }
475 }
476 else if constexpr (rpc_hpp::detail::is_container_v<no_ref_t>)
477 {
478 using subvalue_t = typename no_ref_t::value_type;
479
480 no_ref_t container{};
481 container.reserve(arg.Size());
482 unsigned arg_counter = 0;
483
484 for (const auto& val : arg.GetArray())
485 {
486 container.push_back(parse_args<subvalue_t>(val, arg_counter));
487 }
488
489 return container;
490 }
491 else if constexpr (rpc_hpp::detail::is_serializable_v<rapidjson_adapter, no_ref_t>)
492 {
493 rapidjson::Document d{};
494 d.CopyFrom(arg, d.GetAllocator());
495 return no_ref_t::template deserialize<rapidjson_adapter>(d);
496 }
497 else
498 {
499 return deserialize<no_ref_t>(arg);
500 }
501 }
502
503 // nodiscard because parsing can be expensive, and it makes no sense to not use the parsed result
504 template<typename T>
505 [[nodiscard]] static std::remove_cv_t<std::remove_reference_t<T>> parse_args(
506 const rapidjson::Value& arg_arr, unsigned& index)
507 {
508 if (!arg_arr.IsArray())
509 {
510 return parse_arg<T>(arg_arr);
511 }
512
513 const auto& arr = arg_arr.GetArray();
514
515 if (index >= arr.Size())
516 {
517 throw function_mismatch("Argument count mismatch");
518 }
519
520 return parse_arg<T>(arr[index++]);
521 }
522 };
523} // namespace adapters
524} // namespace rpc_hpp
Definition: rpc_rapidjson.hpp:59
Definition: rpc.hpp:182
Definition: rpc.hpp:107
Top-level namespace for rpc.hpp classes and functions.
Definition: rpc.hpp:88
Definition: rpc.hpp:254