rpc_call_cp.hpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
  1. /*
  2. * Copyright (c) 2017-2023 zhllxt
  3. *
  4. * author : zhllxt
  5. * email : 37792738@qq.com
  6. *
  7. * Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. */
  10. /*
  11. Perfect capture in C++20
  12. template <typename ... Args>
  13. auto f(Args&& args){
  14. return [... args = std::forward<Args>(args)]{
  15. // use args
  16. };
  17. }
  18. C++17 and C++14 workaround
  19. In C++17 we can use a workaround with tuples:
  20. template <typename ... Args>
  21. auto f(Args&& ... args){
  22. return [args = std::make_tuple(std::forward<Args>(args) ...)]()mutable{
  23. return std::apply([](auto&& ... args){
  24. // use args
  25. }, std::move(args));
  26. };
  27. }
  28. */
  29. #ifndef __ASIO2_RPC_CALL_COMPONENT_HPP__
  30. #define __ASIO2_RPC_CALL_COMPONENT_HPP__
  31. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  32. #pragma once
  33. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  34. #include <cstdint>
  35. #include <memory>
  36. #include <chrono>
  37. #include <functional>
  38. #include <atomic>
  39. #include <string>
  40. #include <string_view>
  41. #include <queue>
  42. #include <any>
  43. #include <future>
  44. #include <tuple>
  45. #include <unordered_map>
  46. #include <type_traits>
  47. #include <map>
  48. #include <asio2/base/error.hpp>
  49. #include <asio2/base/detail/function_traits.hpp>
  50. #include <asio2/rpc/detail/rpc_serialization.hpp>
  51. #include <asio2/rpc/detail/rpc_protocol.hpp>
  52. #include <asio2/rpc/detail/rpc_invoker.hpp>
  53. namespace asio2::detail
  54. {
  55. template<class derived_t, class args_t>
  56. class rpc_call_cp
  57. {
  58. public:
  59. /**
  60. * @brief constructor
  61. */
  62. rpc_call_cp(rpc_serializer& sr, rpc_deserializer& dr)
  63. : sr_(sr), dr_(dr)
  64. {
  65. }
  66. /**
  67. * @brief destructor
  68. */
  69. ~rpc_call_cp() = default;
  70. protected:
  71. template<class derive_t>
  72. struct sync_call_op
  73. {
  74. template<class return_t, class Rep, class Period, class ...Args>
  75. inline static return_t exec(derive_t& derive,
  76. std::chrono::duration<Rep, Period> timeout, std::string name, Args&&... args)
  77. {
  78. using result_t = typename rpc_result_t<return_t>::type;
  79. if (!derive.is_started())
  80. {
  81. set_last_error(rpc::make_error_code(rpc::error::not_connected));
  82. if constexpr (!std::is_void_v<return_t>)
  83. {
  84. return result_t{};
  85. }
  86. else
  87. {
  88. return;
  89. }
  90. }
  91. std::shared_ptr<result_t> result = std::make_shared<result_t>();
  92. set_last_error(rpc::make_error_code(rpc::error::success));
  93. rpc_header::id_type id = derive.mkid();
  94. rpc_request<Args...> req(id, std::move(name), std::forward<Args>(args)...);
  95. std::shared_ptr<std::promise<error_code>> promise = std::make_shared<std::promise<error_code>>();
  96. std::future<error_code> future = promise->get_future();
  97. auto ex = [&derive, result, id, pm = std::move(promise)]
  98. (error_code ec, std::string_view data) mutable
  99. {
  100. detail::ignore_unused(data);
  101. // when async_send failed, the error category is not rpc category.
  102. //ASIO2_ASSERT(std::string_view(ec.category().name()) == rpc::rpc_category().name());
  103. ASIO2_ASSERT(derive.io_->running_in_this_thread());
  104. if (!ec)
  105. {
  106. #if !defined(ASIO_NO_EXCEPTIONS) && !defined(BOOST_ASIO_NO_EXCEPTIONS)
  107. try
  108. {
  109. #endif
  110. derive.dr_ >> ec;
  111. if constexpr (!std::is_void_v<return_t>)
  112. {
  113. if (!ec)
  114. derive.dr_ >> (*result);
  115. }
  116. else
  117. {
  118. std::ignore = result;
  119. }
  120. #if !defined(ASIO_NO_EXCEPTIONS) && !defined(BOOST_ASIO_NO_EXCEPTIONS)
  121. }
  122. catch (cereal::exception const&)
  123. {
  124. ec = rpc::make_error_code(rpc::error::no_data);
  125. }
  126. catch (std::exception const&)
  127. {
  128. ec = rpc::make_error_code(rpc::error::unspecified_error);
  129. }
  130. #endif
  131. }
  132. if (std::addressof(ec.category()) != std::addressof(rpc::rpc_category()))
  133. {
  134. ec.assign(ec.value(), rpc::rpc_category());
  135. }
  136. ASIO2_ASSERT(std::string_view(ec.category().name()) == rpc::rpc_category().name());
  137. set_last_error(ec);
  138. pm->set_value(ec);
  139. derive.reqs_.erase(id);
  140. };
  141. asio::post(derive.io_->context(), make_allocator(derive.callocator_,
  142. [&derive, req = std::move(req), ex = std::move(ex), p = derive.selfptr()]() mutable
  143. {
  144. derive.reqs_.emplace(req.id(), std::move(ex));
  145. derive.internal_async_send(std::move(p), (derive.sr_.reset() << req).str(),
  146. [&derive, id = req.id()]() mutable
  147. {
  148. if (get_last_error()) // send data failed with error
  149. {
  150. auto iter = derive.reqs_.find(id);
  151. if (iter != derive.reqs_.end())
  152. {
  153. auto& ex = iter->second;
  154. ex(get_last_error(), std::string_view{});
  155. }
  156. }
  157. });
  158. }));
  159. // Whether we run on the io_context thread
  160. if (!derive.io_->running_in_this_thread())
  161. {
  162. std::future_status status = future.wait_for(timeout);
  163. if (status == std::future_status::ready)
  164. {
  165. set_last_error(future.get());
  166. }
  167. else
  168. {
  169. set_last_error(rpc::make_error_code(rpc::error::timed_out));
  170. asio::post(derive.io_->context(), make_allocator(derive.callocator_,
  171. [&derive, id, p = derive.selfptr()]() mutable
  172. {
  173. detail::ignore_unused(p);
  174. derive.reqs_.erase(id);
  175. }));
  176. }
  177. }
  178. else
  179. {
  180. // If invoke synchronization rpc call function in communication thread, it will degenerates
  181. // into async_call and the return value is empty.
  182. asio::post(derive.io_->context(), make_allocator(derive.callocator_,
  183. [&derive, id, p = derive.selfptr()]() mutable
  184. {
  185. detail::ignore_unused(p);
  186. derive.reqs_.erase(id);
  187. }));
  188. set_last_error(rpc::make_error_code(rpc::error::in_progress));
  189. }
  190. ASIO2_ASSERT(std::string_view(get_last_error().category().name()) == rpc::rpc_category().name());
  191. // [20210818] don't throw an error, you can use get_last_error() to check
  192. // is there any exception.
  193. if constexpr (!std::is_void_v<return_t>)
  194. {
  195. return std::move(*result);
  196. }
  197. else
  198. {
  199. return;
  200. }
  201. }
  202. };
  203. template<class derive_t>
  204. struct async_call_op
  205. {
  206. template<class Callback>
  207. inline static auto to_safe_callback(Callback&& cb)
  208. {
  209. using cbtype = typename detail::remove_cvref_t<Callback>;
  210. if constexpr (detail::has_bool_operator<cbtype>::value)
  211. {
  212. using fun_traits_type = function_traits<cbtype>;
  213. if (!cb)
  214. return cbtype{ typename fun_traits_type::stl_lambda_type{} };
  215. else
  216. return std::forward<Callback>(cb);
  217. }
  218. else
  219. {
  220. return std::forward<Callback>(cb);
  221. }
  222. }
  223. template<class return_t, class Callback>
  224. inline static auto make_callback(derive_t& derive, Callback&& cb)
  225. {
  226. return async_call_op<derive_t>::template make_callback_impl<return_t>(
  227. derive, async_call_op<derive_t>::to_safe_callback(std::forward<Callback>(cb)));
  228. }
  229. template<class Callback>
  230. inline static auto make_callback(derive_t& derive, Callback&& cb)
  231. {
  232. using fun_traits_type = function_traits<std::remove_cv_t<std::remove_reference_t<Callback>>>;
  233. return async_call_op<derive_t>::template make_callback_argc(
  234. derive, async_call_op<derive_t>::to_safe_callback(std::forward<Callback>(cb)),
  235. std::integral_constant<int, fun_traits_type::argc>{});
  236. }
  237. template<class Callback>
  238. inline static auto make_callback_argc(derive_t& derive, Callback&& cb, std::integral_constant<int, 0>)
  239. {
  240. return async_call_op<derive_t>::template make_callback_impl<void>(
  241. derive, std::forward<Callback>(cb));
  242. }
  243. template<class Callback>
  244. inline static auto make_callback_argc(derive_t& derive, Callback&& cb, std::integral_constant<int, 1>)
  245. {
  246. using fun_traits_type = function_traits<std::remove_cv_t<std::remove_reference_t<Callback>>>;
  247. using return_type = typename fun_traits_type::template args<0>::type;
  248. static_assert(!std::is_same_v<return_type, void>);
  249. return async_call_op<derive_t>::template make_callback_impl<return_type>(
  250. derive, std::forward<Callback>(cb));
  251. }
  252. template<class return_t, class Callback>
  253. inline static auto make_callback_impl_0(derive_t& derive, Callback&& cb)
  254. {
  255. return [&derive, cb = std::forward<Callback>(cb)](auto ec, std::string_view) mutable
  256. {
  257. #if !defined(ASIO_NO_EXCEPTIONS) && !defined(BOOST_ASIO_NO_EXCEPTIONS)
  258. try
  259. {
  260. #endif
  261. if (!ec)
  262. derive.dr_ >> ec;
  263. #if !defined(ASIO_NO_EXCEPTIONS) && !defined(BOOST_ASIO_NO_EXCEPTIONS)
  264. }
  265. catch (cereal::exception const&)
  266. {
  267. ec = rpc::make_error_code(rpc::error::no_data);
  268. }
  269. catch (std::exception const&)
  270. {
  271. ec = rpc::make_error_code(rpc::error::unspecified_error);
  272. }
  273. #endif
  274. if (std::addressof(ec.category()) != std::addressof(rpc::rpc_category()))
  275. {
  276. ec.assign(ec.value(), rpc::rpc_category());
  277. }
  278. ASIO2_ASSERT(std::string_view(ec.category().name()) == rpc::rpc_category().name());
  279. set_last_error(ec);
  280. cb();
  281. };
  282. }
  283. template<class return_t, class Callback>
  284. inline static auto make_callback_impl_1(derive_t& derive, Callback&& cb)
  285. {
  286. return [&derive, cb = std::forward<Callback>(cb)](auto ec, std::string_view data) mutable
  287. {
  288. detail::ignore_unused(data);
  289. typename rpc_result_t<return_t>::type result{};
  290. #if !defined(ASIO_NO_EXCEPTIONS) && !defined(BOOST_ASIO_NO_EXCEPTIONS)
  291. try
  292. {
  293. #endif
  294. if (!ec)
  295. derive.dr_ >> ec;
  296. if (!ec)
  297. derive.dr_ >> result;
  298. #if !defined(ASIO_NO_EXCEPTIONS) && !defined(BOOST_ASIO_NO_EXCEPTIONS)
  299. }
  300. catch (cereal::exception const&)
  301. {
  302. ec = rpc::make_error_code(rpc::error::no_data);
  303. }
  304. catch (std::exception const&)
  305. {
  306. ec = rpc::make_error_code(rpc::error::unspecified_error);
  307. }
  308. #endif
  309. if (std::addressof(ec.category()) != std::addressof(rpc::rpc_category()))
  310. {
  311. ec.assign(ec.value(), rpc::rpc_category());
  312. }
  313. ASIO2_ASSERT(std::string_view(ec.category().name()) == rpc::rpc_category().name());
  314. set_last_error(ec);
  315. cb(std::move(result));
  316. };
  317. }
  318. template<class return_t, class Callback>
  319. inline static auto make_callback_impl(derive_t& derive, Callback&& cb)
  320. {
  321. if constexpr (std::is_same_v<return_t, void>)
  322. {
  323. return async_call_op<derive_t>::template make_callback_impl_0<return_t>(
  324. derive, std::forward<Callback>(cb));
  325. }
  326. else
  327. {
  328. return async_call_op<derive_t>::template make_callback_impl_1<return_t>(
  329. derive, std::forward<Callback>(cb));
  330. }
  331. }
  332. template<class Req>
  333. inline static void exec(derive_t& derive, Req&& req)
  334. {
  335. ASIO2_ASSERT(!req.id());
  336. if (!derive.is_started())
  337. {
  338. set_last_error(rpc::make_error_code(rpc::error::in_progress));
  339. derive.post_event([&derive, req = std::forward<Req>(req), p = derive.selfptr()]
  340. (event_queue_guard<derived_t> g) mutable
  341. {
  342. detail::ignore_unused(g);
  343. derive.internal_async_send(std::move(p), (derive.sr_.reset() << req).str());
  344. });
  345. return;
  346. }
  347. set_last_error(rpc::make_error_code(rpc::error::success));
  348. asio::post(derive.io_->context(), make_allocator(derive.callocator_,
  349. [&derive, req = std::forward<Req>(req), p = derive.selfptr()]() mutable
  350. {
  351. derive.internal_async_send(std::move(p), (derive.sr_.reset() << req).str());
  352. }));
  353. }
  354. template<class Callback, class Rep, class Period, class Req>
  355. inline static void exec(derive_t& derive, rpc_header::id_type id,
  356. std::chrono::duration<Rep, Period> timeout, Callback&& cb, Req&& req)
  357. {
  358. ASIO2_ASSERT(id);
  359. req.id(id);
  360. // 2020-12-03 Fix possible bug: move the "timer->async_wait" into the io_context thread.
  361. // otherwise the "derive.send" maybe has't called, the "timer->async_wait" has called
  362. // already.
  363. std::shared_ptr<asio::steady_timer> timer =
  364. std::make_shared<asio::steady_timer>(derive.io_->context());
  365. auto ex = [&derive, id, timer, cb = std::forward<Callback>(cb)]
  366. (error_code ec, std::string_view data) mutable
  367. {
  368. ASIO2_ASSERT(derive.io_->running_in_this_thread());
  369. detail::cancel_timer(*timer);
  370. cb(ec, data);
  371. derive.reqs_.erase(id);
  372. };
  373. auto fn = [&derive, timer = std::move(timer), timeout, req = std::forward<Req>(req),
  374. ex = std::move(ex), p = derive.selfptr()]() mutable
  375. {
  376. // 1. first, save the request id
  377. derive.reqs_.emplace(req.id(), std::move(ex));
  378. // 2. second, start the timeout timer.
  379. // note : cannot put "start timer" after "async_send", beacust the async_send
  380. // maybe failed immediately with the "derive.is_started() == false", then the
  381. // callback of async_send will be called immediately, and the "ex" will be called,
  382. // and the timer will be canceled, but at this time, the timer has't start yet,
  383. // when async_send is return, the timer will be begin "async_wait", but the timer
  384. // "cancel" is called already, so this will cause some small problem.
  385. // must start a timeout timer, othwise if not recved response, it will cause the
  386. // request id in the map forever.
  387. timer->expires_after(timeout);
  388. timer->async_wait(
  389. [p, &derive, id = req.id()]
  390. (const error_code& ec) mutable
  391. {
  392. detail::ignore_unused(p);
  393. if (ec == asio::error::operation_aborted)
  394. return;
  395. auto iter = derive.reqs_.find(id);
  396. if (iter != derive.reqs_.end())
  397. {
  398. auto& ex = iter->second;
  399. ex(rpc::make_error_code(rpc::error::timed_out), std::string_view{});
  400. }
  401. });
  402. // 3. third, send request.
  403. derive.internal_async_send(std::move(p), (derive.sr_.reset() << req).str(),
  404. [&derive, id = req.id()]() mutable
  405. {
  406. if (get_last_error()) // send data failed with error
  407. {
  408. auto iter = derive.reqs_.find(id);
  409. if (iter != derive.reqs_.end())
  410. {
  411. auto& ex = iter->second;
  412. ex(get_last_error(), std::string_view{});
  413. }
  414. }
  415. });
  416. };
  417. if (!derive.is_started())
  418. {
  419. set_last_error(rpc::make_error_code(rpc::error::in_progress));
  420. derive.post_event([fn = std::move(fn)](event_queue_guard<derived_t> g) mutable
  421. {
  422. detail::ignore_unused(g);
  423. fn();
  424. });
  425. return;
  426. }
  427. set_last_error(rpc::make_error_code(rpc::error::success));
  428. // 2019-11-28 fixed the bug of issue #6 : task() cannot be called directly
  429. // 2021-12-10 : can't save the request id in async_send's callback.
  430. // The following will occurs when using async_send with callback :
  431. // 1. call async_send with callback
  432. // 2. recv response by rpc_recv_op
  433. // 3. the callback was called
  434. // It means that : The callback function of async_send may be called after
  435. // recved response data.
  436. asio::post(derive.io_->context(), make_allocator(derive.callocator_, std::move(fn)));
  437. }
  438. };
  439. template<class derive_t>
  440. class sync_caller
  441. {
  442. template <class, class> friend class rpc_call_cp;
  443. protected:
  444. sync_caller(derive_t& d) noexcept
  445. : derive(d), id_(0), tm_(d.get_default_timeout()) {}
  446. sync_caller(sync_caller&& o) noexcept
  447. : derive(o.derive), id_(std::move(o.id_)), tm_(std::move(o.tm_)) {}
  448. sync_caller(const sync_caller&) = delete;
  449. sync_caller& operator=(sync_caller&&) = delete;
  450. sync_caller& operator=(const sync_caller&) = delete;
  451. public:
  452. ~sync_caller() = default;
  453. /**
  454. * @brief Set the timeout of this rpc call, only valid for this once call.
  455. */
  456. template<class Rep, class Period>
  457. inline sync_caller& set_timeout(std::chrono::duration<Rep, Period> timeout) noexcept
  458. {
  459. this->tm_ = std::move(timeout);
  460. return (*this);
  461. }
  462. /**
  463. * @brief Set the timeout of this rpc call, only valid for this once call. same as set_timeout
  464. */
  465. template<class Rep, class Period>
  466. inline sync_caller& timeout(std::chrono::duration<Rep, Period> timeout) noexcept
  467. {
  468. return this->set_timeout(std::move(timeout));
  469. }
  470. // If invoke synchronization rpc call function in communication thread, it will degenerates
  471. // into async_call and the return value is empty.
  472. template<class return_t, class ...Args>
  473. inline return_t call(std::string name, Args&&... args)
  474. {
  475. return sync_call_op<derive_t>::template exec<return_t>(
  476. derive, tm_, std::move(name), std::forward<Args>(args)...);
  477. }
  478. protected:
  479. derive_t& derive;
  480. rpc_header::id_type id_;
  481. asio::steady_timer::duration tm_;
  482. };
  483. template<class derive_t>
  484. class async_caller
  485. {
  486. template <class, class> friend class rpc_call_cp;
  487. protected:
  488. async_caller(derive_t& d) noexcept
  489. : derive(d), id_(0), tm_(d.get_default_timeout()) {}
  490. async_caller(async_caller&& o) noexcept
  491. : derive(o.derive)
  492. , id_(std::move(o.id_)), tm_(std::move(o.tm_))
  493. , cb_(std::move(o.cb_)), fn_(std::move(o.fn_)) {}
  494. async_caller(const async_caller&) = delete;
  495. async_caller& operator=(async_caller&&) = delete;
  496. async_caller& operator=(const async_caller&) = delete;
  497. using defer_fn = std::function<void(rpc_header::id_type, asio::steady_timer::duration,
  498. std::function<void(error_code, std::string_view)>)>;
  499. public:
  500. ~async_caller()
  501. {
  502. if (this->fn_)
  503. {
  504. (this->fn_)(std::move(this->id_), std::move(this->tm_), std::move(this->cb_));
  505. }
  506. }
  507. /**
  508. * @brief Set the timeout of this rpc call, only valid for this once call.
  509. */
  510. template<class Rep, class Period>
  511. inline async_caller& set_timeout(std::chrono::duration<Rep, Period> timeout) noexcept
  512. {
  513. this->tm_ = timeout;
  514. return (*this);
  515. }
  516. /**
  517. * @brief Set the timeout of this rpc call, only valid for this once call. same as set_timeout
  518. */
  519. template<class Rep, class Period>
  520. inline async_caller& timeout(std::chrono::duration<Rep, Period> timeout) noexcept
  521. {
  522. return this->set_timeout(std::move(timeout));
  523. }
  524. /**
  525. * @brief Set the callback function of this rpc call, only valid for this once call.
  526. */
  527. template<class Callback>
  528. inline async_caller& response(Callback&& cb)
  529. {
  530. this->id_ = derive.mkid();
  531. this->cb_ = async_call_op<derive_t>::template make_callback(derive, std::forward<Callback>(cb));
  532. return (*this);
  533. }
  534. template<class ...Args>
  535. inline async_caller& async_call(std::string name, Args&&... args)
  536. {
  537. derive_t& deriv = derive;
  538. this->fn_ = [&deriv, req = rpc_request<Args...>{ std::move(name),std::forward<Args>(args)... }]
  539. (rpc_header::id_type id, asio::steady_timer::duration timeout,
  540. std::function<void(error_code, std::string_view)> cb) mutable
  541. {
  542. if (!id)
  543. {
  544. async_call_op<derive_t>::template exec(deriv, std::move(req));
  545. }
  546. else
  547. {
  548. if (!cb)
  549. {
  550. async_call_op<derive_t>::template exec(deriv, std::move(id), std::move(timeout),
  551. [](error_code, std::string_view) {}, std::move(req));
  552. }
  553. else
  554. {
  555. async_call_op<derive_t>::template exec(deriv, std::move(id), std::move(timeout),
  556. std::move(cb), std::move(req));
  557. }
  558. }
  559. };
  560. return (*this);
  561. }
  562. protected:
  563. derive_t& derive;
  564. rpc_header::id_type id_;
  565. asio::steady_timer::duration tm_;
  566. std::function<void(error_code, std::string_view)> cb_;
  567. defer_fn fn_;
  568. };
  569. template<class derive_t>
  570. class base_caller
  571. {
  572. template <class, class> friend class rpc_call_cp;
  573. protected:
  574. base_caller(derive_t& d) noexcept
  575. : derive(d), tm_(d.get_default_timeout()) {}
  576. base_caller(base_caller&& o) noexcept
  577. : derive(o.derive), tm_(std::move(o.tm_)) {}
  578. base_caller& operator=(base_caller&&) = delete;
  579. base_caller(const base_caller&) = delete;
  580. base_caller& operator=(const base_caller&) = delete;
  581. public:
  582. ~base_caller() = default;
  583. /**
  584. * @brief Set the timeout of this rpc call, only valid for this once call.
  585. */
  586. template<class Rep, class Period>
  587. inline base_caller& set_timeout(std::chrono::duration<Rep, Period> timeout) noexcept
  588. {
  589. this->tm_ = std::move(timeout);
  590. return (*this);
  591. }
  592. /**
  593. * @brief Set the timeout of this rpc call, only valid for this once call. same as set_timeout
  594. */
  595. template<class Rep, class Period>
  596. inline base_caller& timeout(std::chrono::duration<Rep, Period> timeout) noexcept
  597. {
  598. return this->set_timeout(std::move(timeout));
  599. }
  600. /**
  601. * @brief Set the callback function of this rpc call, only valid for this once call.
  602. */
  603. template<class Callback>
  604. inline async_caller<derive_t> response(Callback&& cb)
  605. {
  606. async_caller<derive_t> caller{ derive };
  607. caller.set_timeout(std::move(this->tm_));
  608. caller.response(std::forward<Callback>(cb));
  609. return caller; // "caller" is local variable has RVO optimization, should't use std::move()
  610. }
  611. // If invoke synchronization rpc call function in communication thread, it will degenerates
  612. // into async_call and the return value is empty.
  613. template<class return_t, class ...Args>
  614. inline return_t call(std::string name, Args&&... args)
  615. {
  616. return sync_call_op<derive_t>::template exec<return_t>(derive, this->tm_,
  617. std::move(name), std::forward<Args>(args)...);
  618. }
  619. template<class ...Args>
  620. inline async_caller<derive_t> async_call(std::string name, Args&&... args)
  621. {
  622. async_caller<derive_t> caller{ derive };
  623. caller.set_timeout(std::move(this->tm_));
  624. caller.async_call(std::move(name), std::forward<Args>(args)...);
  625. return caller; // "caller" is local variable has RVO optimization, should't use std::move()
  626. }
  627. protected:
  628. derive_t& derive;
  629. asio::steady_timer::duration tm_;
  630. };
  631. public:
  632. /**
  633. * @brief call a rpc function
  634. * If invoke synchronization rpc call function in communication thread, it will degenerates
  635. * into async_call and the return value is empty.
  636. * You can use get_last_error to check whether there is an error of the call
  637. */
  638. template<class return_t, class Rep, class Period, class ...Args>
  639. inline return_t call(std::chrono::duration<Rep, Period> timeout, std::string name, Args&&... args)
  640. {
  641. derived_t& derive = static_cast<derived_t&>(*this);
  642. return sync_call_op<derived_t>::template exec<return_t>(derive, timeout,
  643. std::move(name), std::forward<Args>(args)...);
  644. }
  645. /**
  646. * @brief call a rpc function
  647. * If invoke synchronization rpc call function in communication thread, it will degenerates
  648. * into async_call and the return value is empty.
  649. * You can use get_last_error to check whether there is an error of the call
  650. */
  651. template<class return_t, class ...Args>
  652. inline return_t call(std::string name, Args&&... args)
  653. {
  654. derived_t& derive = static_cast<derived_t&>(*this);
  655. return sync_call_op<derived_t>::template exec<return_t>(derive,
  656. derive.get_default_timeout(), std::move(name), std::forward<Args>(args)...);
  657. }
  658. /**
  659. * @brief asynchronous call a rpc function
  660. * Callback signature : void(DataType result) example : [](std::string result){}
  661. * if result type is void, the Callback signature is : void()
  662. * Because the result value type is not specified in the first template parameter,
  663. * so the result value type must be specified in the Callback lambda.
  664. */
  665. template<class Callback, class ...Args>
  666. inline typename std::enable_if_t<is_callable_v<Callback>, void>
  667. async_call(Callback&& cb, std::string name, Args&&... args)
  668. {
  669. derived_t& derive = static_cast<derived_t&>(*this);
  670. async_call_op<derived_t>::template exec(derive, derive.mkid(), derive.get_default_timeout(),
  671. async_call_op<derived_t>::template make_callback(derive, std::forward<Callback>(cb)),
  672. rpc_request<Args...>{ std::move(name), std::forward<Args>(args)... });
  673. }
  674. /**
  675. * @brief asynchronous call a rpc function
  676. * Callback signature : void(DataType result) example : [](std::string result){}
  677. * if result type is void, the Callback signature is : void()
  678. * Because the result value type is not specified in the first template parameter,
  679. * so the result value type must be specified in the Callback lambda
  680. */
  681. template<class Callback, class Rep, class Period, class ...Args>
  682. inline typename std::enable_if_t<is_callable_v<Callback>, void>
  683. async_call(Callback&& cb, std::chrono::duration<Rep, Period> timeout, std::string name, Args&&... args)
  684. {
  685. derived_t& derive = static_cast<derived_t&>(*this);
  686. async_call_op<derived_t>::template exec(derive, derive.mkid(), timeout,
  687. async_call_op<derived_t>::template make_callback(derive, std::forward<Callback>(cb)),
  688. rpc_request<Args...>{ std::move(name), std::forward<Args>(args)... });
  689. }
  690. /**
  691. * @brief asynchronous call a rpc function
  692. * Callback signature : void(return_t result)
  693. * the return_t is the first template parameter.
  694. * if result type is void, the Callback signature is : void()
  695. */
  696. template<class return_t, class Callback, class ...Args>
  697. inline typename std::enable_if_t<detail::is_template_callable_v<Callback, return_t>, void>
  698. async_call(Callback&& cb, std::string name, Args&&... args)
  699. {
  700. derived_t& derive = static_cast<derived_t&>(*this);
  701. async_call_op<derived_t>::template exec(derive, derive.mkid(), derive.get_default_timeout(),
  702. async_call_op<derived_t>::template make_callback<return_t>(
  703. derive, std::forward<Callback>(cb)),
  704. rpc_request<Args...>{ std::move(name), std::forward<Args>(args)... });
  705. }
  706. /**
  707. * @brief asynchronous call a rpc function
  708. * Callback signature : void(return_t result)
  709. * the return_t is the first template parameter.
  710. * if result type is void, the Callback signature is : void()
  711. */
  712. template<class return_t, class Callback, class Rep, class Period, class ...Args>
  713. inline typename std::enable_if_t<detail::is_template_callable_v<Callback, return_t>, void>
  714. async_call(Callback&& cb, std::chrono::duration<Rep, Period> timeout, std::string name, Args&&... args)
  715. {
  716. derived_t& derive = static_cast<derived_t&>(*this);
  717. async_call_op<derived_t>::template exec(derive, derive.mkid(), timeout,
  718. async_call_op<derived_t>::template make_callback<return_t>(
  719. derive, std::forward<Callback>(cb)),
  720. rpc_request<Args...>{ std::move(name), std::forward<Args>(args)... });
  721. }
  722. /**
  723. * @brief asynchronous call a rpc function
  724. */
  725. template<class ...Args>
  726. inline async_caller<derived_t> async_call(std::string name, Args&&... args)
  727. {
  728. async_caller<derived_t> caller{ static_cast<derived_t&>(*this) };
  729. caller.async_call(std::move(name), std::forward<Args>(args)...);
  730. return caller; // "caller" is local variable has RVO optimization, should't use std::move()
  731. }
  732. /**
  733. * @brief Set the timeout of this rpc call, only valid for this once call.
  734. */
  735. template<class Rep, class Period>
  736. inline base_caller<derived_t> set_timeout(std::chrono::duration<Rep, Period> timeout)
  737. {
  738. base_caller<derived_t> caller{ static_cast<derived_t&>(*this) };
  739. caller.set_timeout(timeout);
  740. return caller; // "caller" is local variable has RVO optimization, should't use std::move()
  741. }
  742. /**
  743. * @brief Set the timeout of this rpc call, only valid for this once call. same as set_timeout
  744. */
  745. template<class Rep, class Period>
  746. inline base_caller<derived_t> timeout(std::chrono::duration<Rep, Period> timeout)
  747. {
  748. return this->set_timeout(std::move(timeout));
  749. }
  750. /**
  751. * @brief Set the callback function of this rpc call, only valid for this once call.
  752. */
  753. template<class Callback>
  754. inline async_caller<derived_t> response(Callback&& cb)
  755. {
  756. async_caller<derived_t> caller{ static_cast<derived_t&>(*this) };
  757. caller.response(std::forward<Callback>(cb));
  758. return caller; // "caller" is local variable has RVO optimization, should't use std::move()
  759. }
  760. protected:
  761. rpc_serializer & sr_;
  762. rpc_deserializer & dr_;
  763. /// The memory to use for handler-based custom memory allocation. used fo async_call.
  764. handler_memory<std::false_type, assizer<args_t>> callocator_;
  765. std::map<rpc_header::id_type, std::function<void(error_code, std::string_view)>> reqs_;
  766. };
  767. }
  768. #endif // !__ASIO2_RPC_CALL_COMPONENT_HPP__