rpc.hpp 0.8.1
Simple RPC Header-Only Library
rpc_boost_json.hpp
Go to the documentation of this file.
1
36
37#pragma once
38
39#include "../rpc.hpp"
40
41#include <boost/json.hpp>
42
43namespace rpc_hpp
44{
45namespace adapters
46{
47 class boost_json_adapter;
48
49 template<>
51 {
52 using serial_t = boost::json::object;
53 using bytes_t = std::string;
54 };
55
56 class boost_json_adapter : public detail::serial_adapter_base<boost_json_adapter>
57 {
58 public:
59 [[nodiscard]] static std::string to_bytes(boost::json::value&& serial_obj)
60 {
61 return boost::json::serialize(serial_obj);
62 }
63
64 [[nodiscard]] static std::optional<boost::json::object> from_bytes(std::string&& bytes)
65 {
66 boost::system::error_code ec;
67 boost::json::value val = boost::json::parse(bytes, ec);
68
69 if (ec)
70 {
71 return std::nullopt;
72 }
73
74 if (!val.is_object())
75 {
76 return std::nullopt;
77 }
78
79 const auto& obj = val.get_object();
80
81 if (const auto ex_it = obj.find("except_type"); ex_it != obj.end())
82 {
83 if (const auto& ex_val = ex_it->value();
84 !ex_val.is_int64() || (ex_val.get_int64() != 0 && !obj.contains("err_mesg")))
85 {
86 return std::nullopt;
87 }
88
89 // Objects with exceptions can be otherwise empty
90 return std::make_optional(std::move(obj));
91 }
92
93 if (const auto fname_it = obj.find("func_name"); fname_it == obj.end()
94 || !fname_it->value().is_string() || fname_it->value().get_string().empty())
95 {
96 return std::nullopt;
97 }
98
99 if (const auto args_it = obj.find("args");
100 args_it == obj.end() || !args_it->value().is_array())
101 {
102 return std::nullopt;
103 }
104
105 return std::make_optional(std::move(obj));
106 }
107
108 static boost::json::object empty_object() { return boost::json::object{}; }
109
110 template<typename R, typename... Args>
111 [[nodiscard]] static boost::json::object serialize_pack(
112 const detail::packed_func<R, Args...>& pack)
113 {
114 boost::json::object obj{};
115 obj["func_name"] = pack.get_func_name();
116 auto& args = obj["args"].emplace_array();
117 args.reserve(sizeof...(Args));
118 detail::for_each_tuple(pack.get_args(),
119 [&args](auto&& elem) { push_args(std::forward<decltype(elem)>(elem), args); });
120
121 if (!pack)
122 {
123 obj["except_type"] = static_cast<int>(pack.get_except_type());
124 obj["err_mesg"] = pack.get_err_mesg();
125 return obj;
126 }
127
128 if constexpr (!std::is_void_v<R>)
129 {
130 obj["result"] = {};
131 push_arg(pack.get_result(), obj["result"]);
132 }
133
134 return obj;
135 }
136
137 template<typename R, typename... Args>
138 [[nodiscard]] static detail::packed_func<R, Args...> deserialize_pack(
139 const boost::json::object& serial_obj)
140 {
141 const auto& args_val = serial_obj.at("args");
142 [[maybe_unused]] unsigned arg_counter = 0;
143 typename detail::packed_func<R, Args...>::args_t args{ parse_args<Args>(
144 args_val, arg_counter)... };
145
146 if constexpr (std::is_void_v<R>)
147 {
148 detail::packed_func<void, Args...> pack(
149 serial_obj.at("func_name").get_string().c_str(), std::move(args));
150
151 if (serial_obj.contains("except_type"))
152 {
153 pack.set_exception(serial_obj.at("err_mesg").get_string().c_str(),
154 static_cast<exception_type>(serial_obj.at("except_type").get_int64()));
155 }
156
157 return pack;
158 }
159 else
160 {
161 if (serial_obj.contains("result") && !serial_obj.at("result").is_null())
162 {
163 return detail::packed_func<R, Args...>(
164 serial_obj.at("func_name").get_string().c_str(),
165 parse_arg<R>(serial_obj.at("result")), std::move(args));
166 }
167
168 detail::packed_func<R, Args...> pack(
169 serial_obj.at("func_name").get_string().c_str(), std::nullopt, std::move(args));
170
171 if (serial_obj.contains("except_type"))
172 {
173 pack.set_exception(serial_obj.at("err_mesg").get_string().c_str(),
174 static_cast<exception_type>(serial_obj.at("except_type").get_int64()));
175 }
176
177 return pack;
178 }
179 }
180
181 [[nodiscard]] static std::string get_func_name(const boost::json::object& serial_obj)
182 {
183 return serial_obj.at("func_name").get_string().c_str();
184 }
185
186 [[nodiscard]] static rpc_exception extract_exception(const boost::json::object& serial_obj)
187 {
188 return rpc_exception{ serial_obj.at("err_mesg").as_string().c_str(),
189 static_cast<exception_type>(serial_obj.at("except_type").as_int64()) };
190 }
191
192 static void set_exception(boost::json::object& serial_obj, const rpc_exception& ex)
193 {
194 serial_obj["except_type"] = static_cast<int>(ex.get_type());
195 serial_obj["err_mesg"] = boost::json::string{ ex.what() };
196 }
197
198 template<typename T>
199 static boost::json::object serialize(const T& val) = delete;
200
201 template<typename T>
202 static T deserialize(const boost::json::object& serial_obj) = delete;
203
204 private:
205 template<typename T>
206 [[nodiscard]] static constexpr bool validate_arg(const boost::json::value& arg) noexcept
207 {
208 if constexpr (std::is_same_v<T, bool>)
209 {
210 return arg.is_bool();
211 }
212 else if constexpr (std::is_integral_v<T>)
213 {
214 return arg.is_int64() || arg.is_uint64();
215 }
216 else if constexpr (std::is_floating_point_v<T>)
217 {
218 return arg.is_double();
219 }
220 else if constexpr (std::is_same_v<T, std::string>)
221 {
222 return arg.is_string();
223 }
224 else if constexpr (rpc_hpp::detail::is_container_v<T>)
225 {
226 return arg.is_array();
227 }
228 else
229 {
230 return arg.is_object();
231 }
232 }
233
234 [[nodiscard]] static std::string mismatch_string(
235 std::string&& expect_type, const boost::json::value& obj)
236 {
237 const auto get_type_str = [&obj]() noexcept
238 {
239 switch (obj.kind())
240 {
241 case boost::json::kind::bool_:
242 return "bool";
243
244 case boost::json::kind::int64:
245 return "int64";
246
247 case boost::json::kind::uint64:
248 return "uint64";
249
250 case boost::json::kind::double_:
251 return "double";
252
253 case boost::json::kind::string:
254 return "string";
255
256 case boost::json::kind::array:
257 return "array";
258
259 case boost::json::kind::object:
260 return "object";
261
262 default:
263 case boost::json::kind::null:
264 return "null";
265 }
266 };
267
268 return { "Boost.JSON expected type: " + std::move(expect_type)
269 + ", got type: " + get_type_str() };
270 }
271
272 template<typename T>
273 static void push_arg(T&& arg, boost::json::value& obj)
274 {
275 using no_ref_t = std::remove_cv_t<std::remove_reference_t<T>>;
276
277 if constexpr (std::is_arithmetic_v<no_ref_t>)
278 {
279 obj = arg;
280 }
281 else if constexpr (std::is_same_v<no_ref_t, std::string>)
282 {
283 obj = boost::json::string{ arg.c_str() };
284 }
285 else if constexpr (rpc_hpp::detail::is_container_v<no_ref_t>)
286 {
287 obj = boost::json::array{};
288 auto& arr = obj.get_array();
289 arr.reserve(arg.size());
290
291 for (auto&& val : arg)
292 {
293 push_args(std::forward<decltype(val)>(val), arr);
294 }
295 }
296 else if constexpr (rpc_hpp::detail::is_serializable_v<boost_json_adapter, no_ref_t>)
297 {
298 obj = no_ref_t::template serialize<boost_json_adapter>(std::forward<T>(arg));
299 }
300 else
301 {
302 obj = serialize<no_ref_t>(std::forward<T>(arg));
303 }
304 }
305
306 template<typename T>
307 static void push_args(T&& arg, boost::json::array& obj_arr)
308 {
309 boost::json::value tmp{};
310 push_arg(std::forward<T>(arg), tmp);
311 obj_arr.push_back(std::move(tmp));
312 }
313
314 template<typename T>
315 [[nodiscard]] static std::remove_cv_t<std::remove_reference_t<T>> parse_arg(
316 const boost::json::value& arg)
317 {
318 using no_ref_t = std::remove_cv_t<std::remove_reference_t<T>>;
319
320 if (!validate_arg<no_ref_t>(arg))
321 {
322 throw function_mismatch(mismatch_string(typeid(no_ref_t).name(), arg));
323 }
324
325 if constexpr (std::is_arithmetic_v<no_ref_t> || std::is_same_v<no_ref_t, std::string>)
326 {
327 return boost::json::value_to<no_ref_t>(arg);
328 }
329 else if constexpr (rpc_hpp::detail::is_container_v<no_ref_t>)
330 {
331 using subvalue_t = typename no_ref_t::value_type;
332
333 auto& arr = arg.get_array();
334 no_ref_t container{};
335 container.reserve(arr.size());
336 unsigned arg_counter = 0;
337
338 for (const auto& val : arr)
339 {
340 container.push_back(parse_args<subvalue_t>(val, arg_counter));
341 }
342
343 return container;
344 }
345 else if constexpr (rpc_hpp::detail::is_serializable_v<boost_json_adapter, no_ref_t>)
346 {
347 return no_ref_t::template deserialize<boost_json_adapter>(arg.get_object());
348 }
349 else
350 {
351 return deserialize<no_ref_t>(arg.get_object());
352 }
353 }
354
355 template<typename T>
356 [[nodiscard]] static std::remove_cv_t<std::remove_reference_t<T>> parse_args(
357 const boost::json::value& arg_arr, unsigned& index)
358 {
359 if (!arg_arr.is_array())
360 {
361 return parse_arg<T>(arg_arr);
362 }
363
364 const auto& arr = arg_arr.get_array();
365
366 if (index >= arr.size())
367 {
368 throw function_mismatch("Argument count mismatch");
369 }
370
371 return parse_arg<T>(arr[index++]);
372 }
373 };
374} // namespace adapters
375} // namespace rpc_hpp
Definition: rpc_boost_json.hpp:57
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