rpc.hpp 0.8.1
Simple RPC Header-Only Library
rpc.hpp
Go to the documentation of this file.
1
37
38#pragma once
39
40#if defined(RPC_HPP_DOXYGEN_GEN)
42# define RPC_HPP_ENABLE_SERVER_CACHE
44# define RPC_HPP_CLIENT_IMPL
46# define RPC_HPP_MODULE_IMPL
48# define RPC_HPP_SERVER_IMPL
49#endif
50
51#if !defined(RPC_HPP_CLIENT_IMPL) && !defined(RPC_HPP_SERVER_IMPL) && !defined(RPC_HPP_MODULE_IMPL)
52# error At least one implementation type must be defined using 'RPC_HPP_{CLIENT, SERVER, MODULE}_IMPL'
53#endif
54
55#include <cassert> // for assert
56#include <cstddef> // for size_t
57#include <optional> // for nullopt, optional
58#include <stdexcept> // for runtime_error
59#include <string> // for string
60#include <tuple> // for tuple, forward_as_tuple
61#include <type_traits> // for declval, false_type, is_same, integral_constant
62#include <utility> // for move, index_sequence, make_index_sequence
63
64#if defined(RPC_HPP_MODULE_IMPL) || defined(RPC_HPP_SERVER_IMPL)
65# include <functional> // for function
66# include <unordered_map> // for unordered_map
67#endif
68
69#if defined(RPC_HPP_SERVER_IMPL) || defined(RPC_HPP_MODULE_IMPL)
70# define RPC_HEADER_FUNC(RETURN, FUNCNAME, ...) extern RETURN FUNCNAME(__VA_ARGS__)
71#elif defined(RPC_HPP_CLIENT_IMPL)
72# define RPC_HEADER_FUNC(RETURN, FUNCNAME, ...) inline RETURN (*FUNCNAME)(__VA_ARGS__) = nullptr
73#endif
74
75#define RPC_HPP_PRECONDITION(EXPR) assert(EXPR)
76#define RPC_HPP_POSTCONDITION(EXPR) assert(EXPR)
77
78#if defined(__GNUC__) || defined(__clang__)
79# define RPC_HPP_INLINE __attribute__((always_inline))
80#elif defined(_MSC_VER)
81# define RPC_HPP_INLINE __forceinline
82#else
83# define RPC_HPP_INLINE
84#endif
85
87namespace rpc_hpp
88{
90static constexpr unsigned version[]{ 0, 8, 1 };
91
92enum class exception_type
93{
94 none,
95 func_not_found,
96 remote_exec,
97 serialization,
98 deserialization,
99 signature_mismatch,
100 client_send,
101 client_receive,
102 server_send,
103 server_receive,
104};
105
106class rpc_exception : public std::runtime_error
107{
108public:
109 explicit rpc_exception(const std::string& mesg, exception_type type) noexcept
110 : std::runtime_error(mesg), m_type(type)
111 {
112 }
113
114 explicit rpc_exception(const char* mesg, exception_type type) noexcept
115 : std::runtime_error(mesg), m_type(type)
116 {
117 }
118
119 exception_type get_type() const noexcept { return m_type; }
120
121private:
122 exception_type m_type;
123};
124
126{
127public:
128 explicit function_not_found(const std::string& mesg) noexcept
129 : rpc_exception(mesg, exception_type::func_not_found)
130 {
131 }
132
133 explicit function_not_found(const char* mesg) noexcept
134 : rpc_exception(mesg, exception_type::func_not_found)
135 {
136 }
137};
138
140{
141public:
142 explicit remote_exec_error(const std::string& mesg) noexcept
143 : rpc_exception(mesg, exception_type::remote_exec)
144 {
145 }
146
147 explicit remote_exec_error(const char* mesg) noexcept
148 : rpc_exception(mesg, exception_type::remote_exec)
149 {
150 }
151};
152
154{
155public:
156 explicit serialization_error(const std::string& mesg) noexcept
157 : rpc_exception(mesg, exception_type::serialization)
158 {
159 }
160
161 explicit serialization_error(const char* mesg) noexcept
162 : rpc_exception(mesg, exception_type::serialization)
163 {
164 }
165};
166
168{
169public:
170 explicit deserialization_error(const std::string& mesg) noexcept
171 : rpc_exception(mesg, exception_type::deserialization)
172 {
173 }
174
175 explicit deserialization_error(const char* mesg) noexcept
176 : rpc_exception(mesg, exception_type::deserialization)
177 {
178 }
179};
180
182{
183public:
184 explicit function_mismatch(const std::string& mesg) noexcept
185 : rpc_exception(mesg, exception_type::signature_mismatch)
186 {
187 }
188
189 explicit function_mismatch(const char* mesg) noexcept
190 : rpc_exception(mesg, exception_type::signature_mismatch)
191 {
192 }
193};
194
196{
197public:
198 explicit client_send_error(const std::string& mesg) noexcept
199 : rpc_exception(mesg, exception_type::client_send)
200 {
201 }
202
203 explicit client_send_error(const char* mesg) noexcept
204 : rpc_exception(mesg, exception_type::client_send)
205 {
206 }
207};
208
210{
211public:
212 explicit client_receive_error(const std::string& mesg) noexcept
213 : rpc_exception(mesg, exception_type::client_receive)
214 {
215 }
216
217 explicit client_receive_error(const char* mesg) noexcept
218 : rpc_exception(mesg, exception_type::client_receive)
219 {
220 }
221};
222
224{
225public:
226 explicit server_send_error(const std::string& mesg) noexcept
227 : rpc_exception(mesg, exception_type::server_send)
228 {
229 }
230
231 explicit server_send_error(const char* mesg) noexcept
232 : rpc_exception(mesg, exception_type::server_send)
233 {
234 }
235};
236
238{
239public:
240 explicit server_receive_error(const std::string& mesg) noexcept
241 : rpc_exception(mesg, exception_type::server_receive)
242 {
243 }
244
245 explicit server_receive_error(const char* mesg) noexcept
246 : rpc_exception(mesg, exception_type::server_receive)
247 {
248 }
249};
250
251namespace adapters
252{
253 template<typename T>
255}
256
258namespace detail
259{
260#if !defined(RPC_HPP_DOXYGEN_GEN)
261 template<typename, typename T>
262 struct is_serializable_base
263 {
264 static_assert(std::integral_constant<T, false>::value,
265 "Second template parameter needs to be of function type");
266 };
267
268 template<typename C, typename R, typename... Args>
269 struct is_serializable_base<C, R(Args...)>
270 {
271 private:
272 template<typename T>
273 static constexpr auto check(T*) noexcept ->
274 typename std::is_same<decltype(std::declval<T>().serialize(std::declval<Args>()...)),
275 R>::type;
276
277 template<typename>
278 static constexpr std::false_type check(...) noexcept;
279
280 using type = decltype(check<C>(nullptr));
281
282 public:
283 static constexpr bool value = type::value;
284 };
285
286 template<typename, typename T>
287 struct is_deserializable_base
288 {
289 static_assert(std::integral_constant<T, false>::value,
290 "Second template parameter needs to be of function type");
291 };
292
293 template<typename C, typename R, typename... Args>
294 struct is_deserializable_base<C, R(Args...)>
295 {
296 private:
297 template<typename T>
298 static constexpr auto check(T*) noexcept ->
299 typename std::is_same<decltype(std::declval<T>().deserialize(std::declval<Args>()...)),
300 R>::type;
301
302 template<typename>
303 static constexpr std::false_type check(...) noexcept;
304
305 using type = decltype(check<C>(nullptr));
306
307 public:
308 static constexpr bool value = type::value;
309 };
310
311 template<typename Serial, typename Value>
312 struct is_serializable :
313 std::integral_constant<bool,
314 is_serializable_base<Value, typename Serial::serial_t(const Value&)>::value
315 && is_deserializable_base<Value, Value(const typename Serial::serial_t&)>::value>
316 {
317 };
318
319 template<typename Serial, typename Value>
320 inline constexpr bool is_serializable_v = is_serializable<Serial, Value>::value;
321
322 template<typename C>
323 struct has_begin
324 {
325 private:
326 template<typename T>
327 static constexpr auto check(T*) noexcept ->
328 typename std::is_same<decltype(std::declval<T>().begin()), typename T::iterator>::type;
329
330 template<typename>
331 static constexpr std::false_type check(...) noexcept;
332
333 using type = decltype(check<C>(nullptr));
334
335 public:
336 static constexpr bool value = type::value;
337 };
338
339 template<typename C>
340 struct has_end
341 {
342 private:
343 template<typename T>
344 static constexpr auto check(T*) noexcept ->
345 typename std::is_same<decltype(std::declval<T>().end()), typename T::iterator>::type;
346
347 template<typename>
348 static constexpr std::false_type check(...) noexcept;
349
350 using type = decltype(check<C>(nullptr));
351
352 public:
353 static constexpr bool value = type::value;
354 };
355
356 template<typename C>
357 struct has_size
358 {
359 private:
360 template<typename T>
361 static constexpr auto check(T*) noexcept ->
362 typename std::is_same<decltype(std::declval<T>().size()), size_t>::type;
363
364 template<typename>
365 static constexpr std::false_type check(...) noexcept;
366
367 using type = decltype(check<C>(nullptr));
368
369 public:
370 static constexpr bool value = type::value;
371 };
372
373 template<typename C>
374 struct is_container :
375 std::integral_constant<bool, has_size<C>::value && has_begin<C>::value && has_end<C>::value>
376 {
377 };
378
379 template<typename C>
380 inline constexpr bool is_container_v = is_container<C>::value;
381
382 template<typename F, typename... Ts, size_t... Is>
383 constexpr void for_each_tuple(const std::tuple<Ts...>& tuple, const F& func,
384 [[maybe_unused]] std::index_sequence<Is...> iseq)
385 {
386 using expander = int[];
387 std::ignore = expander{ 0, ((void)func(std::get<Is>(tuple)), 0)... };
388 }
389
390 template<typename F, typename... Ts>
391 constexpr void for_each_tuple(const std::tuple<Ts...>& tuple, const F& func)
392 {
393 for_each_tuple(tuple, func, std::make_index_sequence<sizeof...(Ts)>());
394 }
395
396# if defined(RPC_HPP_CLIENT_IMPL)
397 // Allows passing in string literals
398 template<typename T>
399 struct decay_str
400 {
401 static_assert(!std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<T>>>,
402 "Pointer parameters are not allowed");
403
404 static_assert(!std::is_array_v<std::remove_cv_t<std::remove_reference_t<T>>>,
405 "C-style array parameters are not allowed");
406
407 using type = T;
408 };
409
410 template<>
411 struct decay_str<const char*>
412 {
413 using type = const std::string&;
414 };
415
416 template<>
417 struct decay_str<const char*&>
418 {
419 using type = const std::string&;
420 };
421
422 template<>
423 struct decay_str<const char* const&>
424 {
425 using type = const std::string&;
426 };
427
428 template<size_t N>
429 struct decay_str<const char (&)[N]>
430 {
431 using type = const std::string&;
432 };
433
434 template<typename T>
435 using decay_str_t = typename decay_str<T>::type;
436
437 template<typename... Args, size_t... Is>
438 constexpr void tuple_bind(
439 const std::tuple<std::remove_cv_t<std::remove_reference_t<decay_str_t<Args>>>...>& src,
440 std::index_sequence<Is...>, Args&&... dest)
441 {
442 using expander = int[];
443 std::ignore = expander{ 0,
444 (
445 (void)[](auto&& x, auto&& y) {
446 if constexpr (
447 std::is_reference_v<
448 decltype(x)> && !std::is_const_v<std::remove_reference_t<decltype(x)>> && !std::is_pointer_v<std::remove_reference_t<decltype(x)>>)
449 {
450 x = std::forward<decltype(y)>(y);
451 }
452 }(dest, std::get<Is>(src)),
453 0)... };
454 }
455
456 template<typename... Args>
457 constexpr void tuple_bind(
458 const std::tuple<std::remove_cv_t<std::remove_reference_t<decay_str_t<Args>>>...>& src,
459 Args&&... dest)
460 {
461 tuple_bind(src, std::make_index_sequence<sizeof...(Args)>(), std::forward<Args>(dest)...);
462 }
463# endif
464
465 template<typename... Args>
466 class packed_func_base
467 {
468 public:
469 using args_t = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>;
470
471 packed_func_base() = delete;
472 packed_func_base(std::string func_name, args_t args) noexcept
473 : m_func_name(std::move(func_name)), m_args(std::move(args))
474 {
475 }
476
477 explicit operator bool() const noexcept { return m_except_type == exception_type::none; }
478 const std::string& get_err_mesg() const noexcept { return m_err_mesg; }
479 const std::string& get_func_name() const noexcept { return m_func_name; }
480 exception_type get_except_type() const noexcept { return m_except_type; }
481
482 void set_exception(std::string&& mesg, const exception_type type) & noexcept
483 {
484 m_except_type = type;
485 m_err_mesg = std::move(mesg);
486 }
487
488 const args_t& get_args() const noexcept { return m_args; }
489 args_t& get_args() noexcept { return m_args; }
490
491 protected:
492 ~packed_func_base() noexcept = default;
493 packed_func_base(const packed_func_base&) = default;
494 packed_func_base(packed_func_base&&) noexcept = default;
495 packed_func_base& operator=(const packed_func_base&) & = default;
496 packed_func_base& operator=(packed_func_base&&) & noexcept = default;
497
498 [[noreturn]] void throw_ex() const noexcept(false)
499 {
500 switch (m_except_type)
501 {
502 case exception_type::func_not_found:
503 throw function_not_found(m_err_mesg);
504
505 case exception_type::remote_exec:
506 throw remote_exec_error(m_err_mesg);
507
508 case exception_type::serialization:
509 throw serialization_error(m_err_mesg);
510
511 case exception_type::deserialization:
512 throw deserialization_error(m_err_mesg);
513
514 case exception_type::signature_mismatch:
515 throw function_mismatch(m_err_mesg);
516
517 case exception_type::client_send:
518 throw client_send_error(m_err_mesg);
519
520 case exception_type::client_receive:
521 throw client_receive_error(m_err_mesg);
522
523 case exception_type::server_send:
524 throw server_send_error(m_err_mesg);
525
526 case exception_type::server_receive:
527 throw server_receive_error(m_err_mesg);
528
529 case exception_type::none:
530 default:
531 throw rpc_exception(m_err_mesg, exception_type::none);
532 }
533 }
534
535 private:
536 exception_type m_except_type{ exception_type::none };
537 std::string m_func_name;
538 std::string m_err_mesg{};
539 args_t m_args;
540 };
541
542 template<typename R, typename... Args>
543 class packed_func final : public packed_func_base<Args...>
544 {
545 public:
546 using result_t = R;
547 using typename packed_func_base<Args...>::args_t;
548
549 packed_func(std::string func_name, std::optional<result_t> result, args_t args) noexcept
550 : packed_func_base<Args...>(std::move(func_name), std::move(args)),
551 m_result(std::move(result))
552 {
553 }
554
555 explicit operator bool() const noexcept
556 {
557 return m_result.has_value() && packed_func_base<Args...>::operator bool();
558 }
559
560 const R& get_result() const
561 {
562 if (!static_cast<bool>(*this))
563 {
564 // throws exception based on except_type
565 this->throw_ex();
566 }
567
568 return m_result.value();
569 }
570
571 void set_result(const R& value) & noexcept(std::is_nothrow_copy_assignable_v<R>)
572 {
573 m_result = value;
574 }
575
576 void set_result(R&& value) & noexcept(std::is_nothrow_move_assignable_v<R>)
577 {
578 m_result = std::move(value);
579 }
580
581 void clear_result() & noexcept { m_result.reset(); }
582
583 private:
584 std::optional<result_t> m_result{};
585 };
586
587 template<typename... Args>
588 class packed_func<void, Args...> final : public packed_func_base<Args...>
589 {
590 public:
591 using result_t = void;
592 using typename packed_func_base<Args...>::args_t;
593 using packed_func_base<Args...>::packed_func_base;
594 using packed_func_base<Args...>::operator bool;
595
596 void get_result() const
597 {
598 if (!static_cast<bool>(*this))
599 {
600 // throws exception based on except_type
601 this->throw_ex();
602 }
603 }
604 };
605
606 template<typename Adapter>
607 struct serial_adapter_base
608 {
609 using serial_t = typename adapters::serial_traits<Adapter>::serial_t;
610 using bytes_t = typename adapters::serial_traits<Adapter>::bytes_t;
611
612 static std::optional<serial_t> from_bytes(bytes_t&& bytes) = delete;
613 static bytes_t to_bytes(serial_t&& serial_obj) = delete;
614 static serial_t empty_object() = delete;
615
616 template<typename R, typename... Args>
617 static serial_t serialize_pack(const packed_func<R, Args...>& pack) = delete;
618
619 template<typename R, typename... Args>
620 static packed_func<R, Args...> deserialize_pack(const serial_t& serial_obj) = delete;
621
622 static std::string get_func_name(const serial_t& serial_obj) = delete;
623 static rpc_exception extract_exception(const serial_t& serial_obj) = delete;
624 static void set_exception(serial_t& serial_obj, const rpc_exception& ex) = delete;
625 };
626#endif
627} // namespace detail
628
629#if defined(RPC_HPP_SERVER_IMPL) || defined(RPC_HPP_MODULE_IMPL)
633inline namespace server
634{
638 template<typename Serial>
640 {
641 public:
642 using adapter_t = Serial;
643
644 server_interface() noexcept = default;
645
646 // Prevent copying
647 server_interface(const server_interface&) = delete;
648 server_interface& operator=(const server_interface&) = delete;
649
650 server_interface(server_interface&&) noexcept = default;
651 server_interface& operator=(server_interface&&) noexcept = default;
652
653# if defined(RPC_HPP_SERVER_IMPL) && defined(RPC_HPP_ENABLE_SERVER_CACHE)
659 template<typename Val>
660 std::unordered_map<typename Serial::bytes_t, Val>& get_func_cache(
661 const std::string& func_name)
662 {
663 RPC_HPP_PRECONDITION(!func_name.empty());
664
665 update_all_cache<Val>(func_name);
666 return *static_cast<std::unordered_map<typename Serial::bytes_t, Val>*>(
667 m_cache_map.at(func_name));
668 }
669
671 RPC_HPP_INLINE void clear_all_cache() noexcept { m_cache_map.clear(); }
672# endif
673
680 template<typename R, typename... Args>
681 void bind_cached(std::string func_name, R (*func_ptr)(Args...))
682 {
683 m_dispatch_table.emplace(std::move(func_name),
684 [this, func_ptr](typename Serial::serial_t& serial_obj)
685 {
686 try
687 {
688 dispatch_cached_func(func_ptr, serial_obj);
689 }
690 catch (const rpc_exception& ex)
691 {
692 Serial::set_exception(serial_obj, ex);
693 }
694 });
695 }
696
704 template<typename R, typename... Args, typename F>
705 RPC_HPP_INLINE void bind_cached(std::string func_name, F&& func)
706 {
707 using fptr_t = R (*)(Args...);
708
709 bind_cached(std::move(func_name), fptr_t{ std::forward<F>(func) });
710 }
711
718 template<typename R, typename... Args>
719 void bind(std::string func_name, R (*func_ptr)(Args...))
720 {
721 m_dispatch_table.emplace(std::move(func_name),
722 [func_ptr](typename Serial::serial_t& serial_obj)
723 {
724 try
725 {
726 dispatch_func(func_ptr, serial_obj);
727 }
728 catch (const rpc_exception& ex)
729 {
730 Serial::set_exception(serial_obj, ex);
731 }
732 });
733 }
734
742 template<typename R, typename... Args, typename F>
743 RPC_HPP_INLINE void bind(std::string func_name, F&& func)
744 {
745 using fptr_t = R (*)(Args...);
746
747 bind(std::move(func_name), fptr_t{ std::forward<F>(func) });
748 }
749
755 [[nodiscard]] typename Serial::bytes_t dispatch(typename Serial::bytes_t&& bytes) const
756 {
757 auto serial_obj = Serial::from_bytes(std::move(bytes));
758
759 if (!serial_obj.has_value())
760 {
761 auto err_obj = Serial::empty_object();
762 Serial::set_exception(err_obj, server_receive_error("Invalid RPC object received"));
763 return Serial::to_bytes(std::move(err_obj));
764 }
765
766 const auto func_name = adapter_t::get_func_name(serial_obj.value());
767
768 if (const auto it = m_dispatch_table.find(func_name); it != m_dispatch_table.end())
769 {
770 it->second(serial_obj.value());
771 return Serial::to_bytes(std::move(serial_obj).value());
772 }
773
774 Serial::set_exception(serial_obj.value(),
775 function_not_found("RPC error: Called function: \"" + func_name + "\" not found"));
776
777 return Serial::to_bytes(std::move(serial_obj).value());
778 }
779
780 protected:
781 ~server_interface() noexcept = default;
782
783# if defined(RPC_HPP_SERVER_IMPL) && defined(RPC_HPP_ENABLE_SERVER_CACHE)
784 template<typename R, typename... Args>
785 void dispatch_cached_func(R (*func)(Args...), typename Serial::serial_t& serial_obj)
786 {
787 RPC_HPP_PRECONDITION(func != nullptr);
788
789 auto pack = [&serial_obj]
790 {
791 try
792 {
793 return Serial::template deserialize_pack<R, Args...>(serial_obj);
794 }
795 catch (const rpc_exception&)
796 {
797 throw;
798 }
799 catch (const std::exception& ex)
800 {
801 throw deserialization_error(ex.what());
802 }
803 }();
804
805 auto& result_cache = get_func_cache<R>(pack.get_func_name());
806
807 if constexpr (!std::is_void_v<R>)
808 {
809 auto bytes = Serial::to_bytes(std::move(serial_obj));
810
811 if (const auto it = result_cache.find(bytes); it != result_cache.end())
812 {
813 pack.set_result(it->second);
814
815 try
816 {
817 serial_obj = Serial::template serialize_pack<R, Args...>(pack);
818 return;
819 }
820 catch (const rpc_exception&)
821 {
822 throw;
823 }
824 catch (const std::exception& ex)
825 {
826 throw serialization_error(ex.what());
827 }
828 }
829
830 run_callback(func, pack);
831 result_cache[std::move(bytes)] = pack.get_result();
832 }
833 else
834 {
835 run_callback(func, pack);
836 }
837
838 try
839 {
840 serial_obj = Serial::template serialize_pack<R, Args...>(pack);
841 }
842 catch (const rpc_exception&)
843 {
844 throw;
845 }
846 catch (const std::exception& ex)
847 {
848 throw serialization_error(ex.what());
849 }
850 }
851# else
852 template<typename R, typename... Args>
853 RPC_HPP_INLINE void dispatch_cached_func(
854 R (*func)(Args...), typename Serial::serial_t& serial_obj) const
855 {
856 dispatch_func(func, serial_obj);
857 }
858# endif
859
860 template<typename R, typename... Args>
861 static void dispatch_func(R (*func)(Args...), typename Serial::serial_t& serial_obj)
862 {
863 RPC_HPP_PRECONDITION(func != nullptr);
864
865 auto pack = [&serial_obj]
866 {
867 try
868 {
869 return Serial::template deserialize_pack<R, Args...>(serial_obj);
870 }
871 catch (const rpc_exception&)
872 {
873 throw;
874 }
875 catch (const std::exception& ex)
876 {
877 throw deserialization_error(ex.what());
878 }
879 }();
880
881 run_callback(func, pack);
882
883 try
884 {
885 serial_obj = Serial::template serialize_pack<R, Args...>(pack);
886 }
887 catch (const rpc_exception&)
888 {
889 throw;
890 }
891 catch (const std::exception& ex)
892 {
893 throw serialization_error(ex.what());
894 }
895 }
896
897 private:
898 template<typename R, typename... Args>
899 static void run_callback(R (*func)(Args...), detail::packed_func<R, Args...>& pack)
900 {
901 RPC_HPP_PRECONDITION(func != nullptr);
902
903 auto& args = pack.get_args();
904
905 if constexpr (std::is_void_v<R>)
906 {
907 try
908 {
909 std::apply(func, args);
910 }
911 catch (const std::exception& ex)
912 {
913 throw remote_exec_error(ex.what());
914 }
915 }
916 else
917 {
918 try
919 {
920 auto result = std::apply(func, args);
921 pack.set_result(std::move(result));
922 }
923 catch (const std::exception& ex)
924 {
925 throw remote_exec_error(ex.what());
926 }
927 }
928 }
929
930# if defined(RPC_HPP_SERVER_IMPL) && defined(RPC_HPP_ENABLE_SERVER_CACHE)
931 template<typename Val>
932 static void* get_func_cache_impl(const std::string& func_name)
933 {
934 static std::unordered_map<std::string,
935 std::unordered_map<typename Serial::bytes_t, Val>>
936 cache{};
937
938 return &cache[func_name];
939 }
940
941 template<typename Val>
942 void update_all_cache(const std::string& func_name)
943 {
944 RPC_HPP_PRECONDITION(!func_name.empty());
945
946 m_cache_map.insert_or_assign(func_name, get_func_cache_impl<Val>(func_name));
947 }
948
949 std::unordered_map<std::string, void*> m_cache_map{};
950# endif
951
952 std::unordered_map<std::string, std::function<void(typename Serial::serial_t&)>>
953 m_dispatch_table{};
954 };
955} // namespace server
956#endif
957
958#if defined(RPC_HPP_CLIENT_IMPL)
959# define call_header_func(FUNCNAME, ...) call_header_func_impl(FUNCNAME, # FUNCNAME, __VA_ARGS__)
960
964inline namespace client
965{
969 template<typename Serial>
971 {
972 public:
973 virtual ~client_interface() noexcept = default;
974 client_interface() noexcept = default;
975
976 // Prevent copying
977 client_interface(const client_interface&) = delete;
978 client_interface& operator=(const client_interface&) = delete;
979
980 // Prevent slicing
981 client_interface& operator=(client_interface&&) = delete;
982
993 template<typename R = void, typename... Args>
994 [[nodiscard]] R call_func(std::string func_name, Args&&... args)
995 {
996 RPC_HPP_PRECONDITION(!func_name.empty());
997
998 auto bytes =
999 serialize_call<R, Args...>(std::move(func_name), std::forward<Args>(args)...);
1000
1001 try
1002 {
1003 send(std::move(bytes));
1004 }
1005 catch (const std::exception& ex)
1006 {
1007 throw client_send_error(ex.what());
1008 }
1009
1010 try
1011 {
1012 bytes = receive();
1013 }
1014 catch (const std::exception& ex)
1015 {
1016 throw client_receive_error(ex.what());
1017 }
1018
1019 const auto pack = deserialize_call<R, Args...>(std::move(bytes));
1020
1021 // Assign values back to any (non-const) reference members
1022 detail::tuple_bind(pack.get_args(), std::forward<Args>(args)...);
1023 return pack.get_result();
1024 }
1025
1035 template<typename R, typename... Args>
1036 [[nodiscard]] R call_header_func_impl(
1037 [[maybe_unused]] R (*func)(Args...), std::string func_name, Args&&... args)
1038 {
1039 RPC_HPP_PRECONDITION(!func_name.empty());
1040
1041 return call_func<R, Args...>(std::move(func_name), std::forward<Args>(args)...);
1042 }
1043
1044 protected:
1045 client_interface(client_interface&&) noexcept = default;
1046
1050 virtual void send(const typename Serial::bytes_t& bytes) = 0;
1051
1055 virtual typename Serial::bytes_t receive() = 0;
1056
1057 private:
1058 template<typename R, typename... Args>
1059 static RPC_HPP_INLINE typename Serial::bytes_t serialize_call(
1060 std::string func_name, Args&&... args)
1061 {
1062 detail::packed_func<R, detail::decay_str_t<Args>...> pack = [&]() noexcept
1063 {
1064 if constexpr (std::is_void_v<R>)
1065 {
1066 return detail::packed_func<void, detail::decay_str_t<Args>...>{
1067 std::move(func_name), std::forward_as_tuple(args...)
1068 };
1069 }
1070 else
1071 {
1072 return detail::packed_func<R, detail::decay_str_t<Args>...>{
1073 std::move(func_name), std::nullopt, std::forward_as_tuple(args...)
1074 };
1075 }
1076 }();
1077
1078 auto serial_obj = [&pack]
1079 {
1080 try
1081 {
1082 return Serial::serialize_pack(pack);
1083 }
1084 catch (const rpc_exception&)
1085 {
1086 throw;
1087 }
1088 catch (const std::exception& ex)
1089 {
1090 throw serialization_error(ex.what());
1091 }
1092 }();
1093
1094 return Serial::to_bytes(std::move(serial_obj));
1095 }
1096
1097 template<typename R, typename... Args>
1098 static RPC_HPP_INLINE auto deserialize_call(typename Serial::bytes_t&& bytes)
1099 {
1100 const auto ret_obj = Serial::from_bytes(std::move(bytes));
1101
1102 if (!ret_obj.has_value())
1103 {
1104 throw client_receive_error("Client received invalid RPC object");
1105 }
1106
1107 try
1108 {
1109 return Serial::template deserialize_pack<R, detail::decay_str_t<Args>...>(
1110 ret_obj.value());
1111 }
1112 catch (const rpc_exception&)
1113 {
1114 throw;
1115 }
1116 catch (const std::exception& ex)
1117 {
1118 throw deserialization_error(ex.what());
1119 }
1120 }
1121 };
1122} // namespace client
1123#endif
1124} // namespace rpc_hpp
Class defining an interface for calling into an RPC server or module.
Definition: rpc.hpp:971
virtual Serial::bytes_t receive()=0
Receives serialized data from a server or module.
R call_func(std::string func_name, Args &&... args)
Sends an RPC call request to a server, waits for a response, then returns the result.
Definition: rpc.hpp:994
R call_header_func_impl(R(*func)(Args...), std::string func_name, Args &&... args)
Sends an RPC call request to a server, waits for a response, then returns the result.
Definition: rpc.hpp:1036
virtual void send(const typename Serial::bytes_t &bytes)=0
Sends serialized data to a server or module.
Definition: rpc.hpp:210
Definition: rpc.hpp:196
Definition: rpc.hpp:168
Definition: rpc.hpp:182
Definition: rpc.hpp:126
Definition: rpc.hpp:140
Definition: rpc.hpp:107
Definition: rpc.hpp:154
Class defining an interface for serving functions via RPC.
Definition: rpc.hpp:640
std::unordered_map< typename Serial::bytes_t, Val > & get_func_cache(const std::string &func_name)
Gets a reference to the server's function cache.
Definition: rpc.hpp:660
Serial::bytes_t dispatch(typename Serial::bytes_t &&bytes) const
Parses the received serialized data and determines which function to call.
Definition: rpc.hpp:755
void bind(std::string func_name, R(*func_ptr)(Args...))
Binds a string to a callback.
Definition: rpc.hpp:719
void bind_cached(std::string func_name, R(*func_ptr)(Args...))
Binds a string to a callback, utilizing the server's cache.
Definition: rpc.hpp:681
RPC_HPP_INLINE void clear_all_cache() noexcept
Clears the server's function cache.
Definition: rpc.hpp:671
Definition: rpc.hpp:238
Definition: rpc.hpp:224
Top-level namespace for rpc.hpp classes and functions.
Definition: rpc.hpp:88
Definition: rpc.hpp:254