基于C++ Coroutines提案 ‘Stackless Resumable Functions’编写的协程库
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

test_async_modern_cb.cpp 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. 
  2. #include "librf.h"
  3. #include <chrono>
  4. #include <iostream>
  5. #include <string>
  6. #include <thread>
  7. #include <future>
  8. //原旨主义的异步函数,其回调写法大致如下
  9. template<typename _Input_t, typename _Callable_t>
  10. __declspec(noinline)
  11. void tostring_async_originalism(_Input_t&& value, _Callable_t&& token)
  12. {
  13. std::thread([callback = std::move(token), value = std::forward<_Input_t>(value)]
  14. {
  15. callback(std::to_string(value));
  16. }).detach();
  17. }
  18. //使用原旨主义的方式扩展异步方法来支持future
  19. template<typename _Input_t>
  20. auto tostring_async_originalism_future(_Input_t&& value)
  21. {
  22. std::promise<std::string> _promise;
  23. std::future<std::string> _future = _promise.get_future();
  24. std::thread([_promise = std::move(_promise), value = std::forward<_Input_t>(value)]() mutable
  25. {
  26. _promise.set_value(std::to_string(value));
  27. }).detach();
  28. return std::move(_future);
  29. }
  30. //----------------------------------------------------------------------------------------------------------------------
  31. //以下演示如何通过现代回调(Modern Callback), 使用回调适配器模型,
  32. //将异步回调函数扩展到支持future模式,调用链模式,以及协程。
  33. //首先,准备modern_call_return_void_t和modern_callback_adapter_t给异步函数使用
  34. //通过一个间接的类来解决返回void的语法问题,以便于优化返回值
  35. struct modern_call_return_void_t
  36. {
  37. void get(){}
  38. };
  39. //回调适配器的模板类
  40. //_Callable_t 要符合 _Signature_t 签名
  41. //这个类除了转移token外,不做任何有效的工作
  42. //有效工作等待特列化的类去做
  43. template<typename _Callable_t, typename _Signature_t>
  44. struct modern_callback_adapter_t
  45. {
  46. using callback_type = _Callable_t;
  47. using return_type = modern_call_return_void_t;
  48. static std::tuple<callback_type, return_type> traits(_Callable_t&& token)
  49. {
  50. return { std::forward<_Callable_t>(token), {} };
  51. }
  52. };
  53. //一个使用回调处理结果的异步函数,会涉及以下概念:
  54. //_Input_t:异步函数的输入参数;
  55. //_Signature_t: 此异步回调的函数签名;应当满足‘void(_Exception_t, _Result_t...)’或者‘void(_Result_t...)’类型;
  56. //_Callable_t:回调函数或标记,如果是回调函数,则需要符合_Signature_t的签名类型。这个回调,必须调用一次,且只能调用一次;
  57. //_Return_t:异步函数的返回值;
  58. //_Result_t...:异步函数完成后的结果值,作为回调函数的入参部分;这个参数可以有零至多个;
  59. //_Exception_t:回调函数的异常, 如果不喜欢异常的则忽略这个部分,但就得异步代码将异常处置妥当;
  60. //
  61. //在回调适配器模型里,_Input_t/_Result_t/_Exception_t(可选)是异步函数提供的功能所固有的部分;_Callable_t/_Return_t
  62. //部分并不直接使用,而是通过适配器去另外处理。这样给予适配器一次扩展到future模式,调用链模式的机会,以及支持协程的机会。
  63. //
  64. //tostring_async 演示了在其他线程里,将_Input_t的输入值,转化为std::string类型的_Result_t。
  65. //然后调用_Signature_t为 ‘void(std::string &&)’ 类型的 _Callable_t。
  66. //忽视异常处理,故没有_Exception_t。
  67. //
  68. template<typename _Input_t, typename _Callable_t>
  69. __declspec(noinline)
  70. auto tostring_async(_Input_t&& value, _Callable_t&& token)
  71. {
  72. //适配器类型
  73. using _Adapter_t = modern_callback_adapter_t<typename resumef::remove_cvref_t<_Callable_t>, void(std::string)>;
  74. //通过适配器获得兼容_Signature_t类型的真正的回调,以及返回值_Return_t
  75. auto adapter = _Adapter_t::traits(std::forward<_Callable_t>(token));
  76. //callback与token未必是同一个变量,甚至未必是同一个类型
  77. std::thread([callback = std::move(std::get<0>(adapter)), value = std::forward<_Input_t>(value)]
  78. {
  79. using namespace std::literals;
  80. std::this_thread::sleep_for(0.1s);
  81. callback(std::to_string(value));
  82. }).detach();
  83. //返回适配器的_Return_t变量
  84. return std::move(std::get<1>(adapter)).get();
  85. }
  86. //或者宏版本写法
  87. #define MODERN_CALLBACK_TRAITS(_Token_value, _Signature_t) \
  88. using _Adapter_t = modern_callback_adapter_t<typename resumef::remove_cvref_t<_Callable_t>, _Signature_t>; \
  89. auto _Adapter_value = _Adapter_t::traits(std::forward<_Callable_t>(_Token_value))
  90. #define MODERN_CALLBACK_CALL() std::move(std::get<0>(_Adapter_value))
  91. #define MODERN_CALLBACK_RETURN() return std::move(std::get<1>(_Adapter_value)).get()
  92. template<typename _Input_t, typename _Callable_t>
  93. auto tostring_async_macro(_Input_t&& value, _Callable_t&& token)
  94. {
  95. MODERN_CALLBACK_TRAITS(token, void(std::string));
  96. std::thread([callback = MODERN_CALLBACK_CALL(), value = std::forward<_Input_t>(value)]
  97. {
  98. callback(std::to_string(value));
  99. }).detach();
  100. MODERN_CALLBACK_RETURN();
  101. }
  102. //----------------------------------------------------------------------------------------------------------------------
  103. //下面演示如何扩展tostring_async函数,以支持future模式
  104. //future库有多种,但应当都提供遵循promise/future对,兼容std::promise/std::future用法
  105. //这样的话,可以做一个更加通用的支持future的callback类
  106. //实现use_future_callback_t的基类,避免写一些重复代码
  107. template<typename _Promise_traits, typename _Result_t>
  108. struct use_future_callback_base_t
  109. {
  110. //回调函数的结果类型,已经排除掉了异常参数
  111. using result_type = _Result_t;
  112. //通过_Promise_traits获取真正的promise类型
  113. using promise_type = typename _Promise_traits::template promise_type<result_type>;
  114. //此类持有一个std::promise<_Result_t>,便于设置值和异常
  115. //而将与promise关联的future作为返回值_Return_t,让tostring_async返回。
  116. mutable promise_type _promise;
  117. auto get_future() const
  118. {
  119. return this->_promise.get_future();
  120. }
  121. };
  122. //此类的实例作为真正的callback,交给异步回调函数,替换token。
  123. //在实际应用中,需要针对是否有异常参数,结果值为0,1,多个等情况做特殊处理,故还需要通过更多的偏特化版本来支持。
  124. //具体的异常参数,需要根据实际应用去特里化。这里仅演示通过std::exception_ptr作为异常传递的情况。
  125. template<typename...>
  126. struct use_future_callback_t;
  127. //无异常,无结果的callback类型:void()
  128. template<typename _Promise_traits>
  129. struct use_future_callback_t<_Promise_traits> : public use_future_callback_base_t<_Promise_traits, void>
  130. {
  131. using use_future_callback_base_t<_Promise_traits, void>::use_future_callback_base_t;
  132. void operator()() const
  133. {
  134. this->_promise.set_value();
  135. }
  136. };
  137. //有异常,无结果的callback类型:void(exception_ptr)
  138. template<typename _Promise_traits>
  139. struct use_future_callback_t<_Promise_traits, std::exception_ptr> : public use_future_callback_base_t<_Promise_traits, void>
  140. {
  141. using use_future_callback_base_t<_Promise_traits, void>::use_future_callback_base_t;
  142. void operator()(std::exception_ptr eptr) const
  143. {
  144. if (!eptr)
  145. this->_promise.set_value();
  146. else
  147. this->_promise.set_exception(std::move(eptr));
  148. }
  149. };
  150. //无异常,单结果的callback类型:void(_Result_t)
  151. template<typename _Promise_traits, typename _Result_t>
  152. struct use_future_callback_t<_Promise_traits, _Result_t> : public use_future_callback_base_t<_Promise_traits, _Result_t>
  153. {
  154. using use_future_callback_base_t<_Promise_traits, _Result_t>::use_future_callback_base_t;
  155. template<typename Arg>
  156. void operator()(Arg && arg) const
  157. {
  158. this->_promise.set_value(std::forward<Arg>(arg));
  159. }
  160. };
  161. //有异常,单结果的callback类型:void(std::exception_ptr, _Result_t)
  162. template<typename _Promise_traits, typename _Result_t>
  163. struct use_future_callback_t<_Promise_traits, std::exception_ptr, _Result_t> : public use_future_callback_base_t<_Promise_traits, _Result_t>
  164. {
  165. using use_future_callback_base_t<_Promise_traits, _Result_t>::use_future_callback_base_t;
  166. template<typename Arg>
  167. void operator()(std::exception_ptr eptr, Arg && arg) const
  168. {
  169. if (!eptr)
  170. this->_promise.set_value(std::forward<Arg>(arg));
  171. else
  172. this->_promise.set_exception(std::move(eptr));
  173. }
  174. };
  175. //无异常,多结果的callback类型:void(_Result_t...)
  176. template<typename _Promise_traits, typename... _Result_t>
  177. struct use_future_callback_t<_Promise_traits, _Result_t...> : public use_future_callback_base_t<_Promise_traits, std::tuple<_Result_t...> >
  178. {
  179. using use_future_callback_base_t<_Promise_traits, std::tuple<_Result_t...> >::use_future_callback_base_t;
  180. template<typename... Args>
  181. void operator()(Args&&... args) const
  182. {
  183. static_assert(sizeof...(Args) == sizeof...(_Result_t), "");
  184. this->_promise.set_value(std::make_tuple(std::forward<Args>(args)...));
  185. }
  186. };
  187. //有异常,多结果的callback类型:void(std::exception_ptr, _Result_t...)
  188. template <typename _Promise_traits, typename... _Result_t>
  189. struct use_future_callback_t<_Promise_traits, std::exception_ptr, _Result_t...> : public use_future_callback_base_t<_Promise_traits, std::tuple<_Result_t...> >
  190. {
  191. using use_future_callback_base_t<_Promise_traits, std::tuple<_Result_t...> >::use_future_callback_base_t;
  192. template<typename... Args>
  193. void operator()(std::exception_ptr eptr, Args&&... args) const
  194. {
  195. static_assert(sizeof...(Args) == sizeof...(_Result_t), "");
  196. if (!eptr)
  197. this->_promise.set_value(std::make_tuple(std::forward<Args>(args)...));
  198. else
  199. this->_promise.set_exception(std::move(eptr));
  200. }
  201. };
  202. //与use_future_callback_t配套的获得_Return_t的类
  203. template<typename _Future_traits, typename _Result_t>
  204. struct use_future_return_t
  205. {
  206. using result_type = _Result_t;
  207. using future_type = typename _Future_traits::template future_type<result_type>;
  208. future_type _future;
  209. use_future_return_t(future_type && ft)
  210. : _future(std::move(ft)) {}
  211. future_type get()
  212. {
  213. return std::move(_future);
  214. }
  215. };
  216. //利用use_future_callback_t + use_future_return_t 实现的callback适配器
  217. template<typename _Token_as_callable_t, typename... _Result_t>
  218. struct modern_callback_adapter_impl_t
  219. {
  220. using traits_type = _Token_as_callable_t;
  221. using callback_type = use_future_callback_t<traits_type, _Result_t...>;
  222. using result_type = typename callback_type::result_type;
  223. using return_type = use_future_return_t<traits_type, result_type>;
  224. static std::tuple<callback_type, return_type> traits(const _Token_as_callable_t& /*没人关心这个变量*/)
  225. {
  226. callback_type callback{};
  227. auto future = callback.get_future();
  228. return { std::move(callback), std::move(future) };
  229. }
  230. };
  231. //----------------------------------------------------------------------------------------------------------------------
  232. //一、做一个使用std::promise/std::future的辅助类。
  233. //这个类还负责萃取promise/future对的类型。
  234. struct std_future_t
  235. {
  236. template<typename _Result_t>
  237. using promise_type = std::promise<_Result_t>;
  238. template<typename _Result_t>
  239. using future_type = std::future<_Result_t>;
  240. };
  241. //二、申明这个辅助类的全局变量。不申明这个变量也行,就是每次要写use_future_t{},麻烦些。
  242. //以后就使用std_future,替代tostring_async的token参数了。
  243. //这个参数其实不需要实质传参,最后会被编译器优化没了。
  244. //仅仅是要指定_Callable_t的类型为std_future_t,
  245. //从而在tostring_async函数内,使用偏特化的modern_callback_adapter_t<std_future_t, ...>而已。
  246. constexpr std_future_t std_future{};
  247. //三、偏特化_Callable_t为std_future_t类型的modern_callback_adapter_t
  248. //真正的回调类型是use_future_callback_t,返回类型_Return_t是use_future_return_t。
  249. //配合use_future_callback_t的promise<result_type>,和use_future_return_t的future<result_type>,正好组成一对promise/future对。
  250. //promise在真正的回调里设置结果值;
  251. //future返回给调用者获取结果值。
  252. template<typename R, typename... _Result_t>
  253. struct modern_callback_adapter_t<std_future_t, R(_Result_t...)> : public modern_callback_adapter_impl_t<std_future_t, _Result_t...>
  254. {
  255. };
  256. //----------------------------------------------------------------------------------------------------------------------
  257. //同理,可以制作支持C++20的协程的下列一系列类(其实,这才是我的最终目的)
  258. struct use_librf_t
  259. {
  260. template<typename _Result_t>
  261. using promise_type = resumef::awaitable_t<_Result_t>;
  262. template<typename _Result_t>
  263. using future_type = resumef::future_t<_Result_t>;
  264. };
  265. constexpr use_librf_t use_librf{};
  266. template<typename R, typename... _Result_t>
  267. struct modern_callback_adapter_t<use_librf_t, R(_Result_t...)> : public modern_callback_adapter_impl_t<use_librf_t, _Result_t...>
  268. {
  269. };
  270. //所以,我现在的看法是,支持异步操作的库,尽可能如此设计回调。这样便于支持C++20的协程。以及future::then这样的任务链。
  271. //这才是“摩登C++”!
  272. //----------------------------------------------------------------------------------------------------------------------
  273. //使用范例
  274. //演示异步库有多个异步回调函数,只要按照Modern Callback范式去做回调,就不再需要写额外的代码,就可以适配到future+librf,以及更多的其他库
  275. template<typename _Ty1, typename _Ty2, typename _Callable_t>
  276. auto add_async(_Ty1&& val1, _Ty2&& val2, _Callable_t&& token)
  277. {
  278. MODERN_CALLBACK_TRAITS(token, void(decltype(val1 + val2)));
  279. std::thread([=, callback = MODERN_CALLBACK_CALL()]
  280. {
  281. using namespace std::literals;
  282. std::this_thread::sleep_for(0.1s);
  283. callback(val1 + val2);
  284. }).detach();
  285. MODERN_CALLBACK_RETURN();
  286. }
  287. //演示异步库有多个异步回调函数,只要按照Modern Callback范式去做回调,就不再需要写额外的代码,就可以适配到future+librf,以及更多的其他库
  288. template<typename _Ty1, typename _Ty2, typename _Callable_t>
  289. auto muldiv_async(_Ty1&& val1, _Ty2&& val2, _Callable_t&& token)
  290. {
  291. MODERN_CALLBACK_TRAITS(token, void(std::exception_ptr, decltype(val1 * val2), decltype(val1 / val2)));
  292. std::thread([=, callback = MODERN_CALLBACK_CALL()]
  293. {
  294. using namespace std::literals;
  295. std::this_thread::sleep_for(0.1s);
  296. auto v1 = val1 * val2;
  297. if (val2 == 0)
  298. callback(std::make_exception_ptr(std::logic_error("divided by zero")), v1, 0);
  299. else
  300. callback(nullptr, v1, val1 / val2);
  301. }).detach();
  302. MODERN_CALLBACK_RETURN();
  303. }
  304. __declspec(noinline)
  305. void resumable_main_modern_cb()
  306. {
  307. using namespace std::literals;
  308. //使用lambda作为异步回调函数,传统用法
  309. tostring_async_originalism(-1.0, [](std::string && value)
  310. {
  311. std::cout << value << std::endl;
  312. });
  313. std::this_thread::sleep_for(0.5s);
  314. tostring_async(1.0, [](std::string && value)
  315. {
  316. std::cout << value << std::endl;
  317. });
  318. std::this_thread::sleep_for(0.5s);
  319. std::cout << "......" << std::endl;
  320. //支持future的用法
  321. std::future<std::string> f1 = tostring_async_originalism_future(5);
  322. std::cout << f1.get() << std::endl;
  323. std::future<std::string> f2 = tostring_async(6.0f, std_future);
  324. std::cout << f2.get() << std::endl;
  325. //支持librf的用法
  326. GO
  327. {
  328. #ifndef __clang__
  329. try
  330. #endif
  331. {
  332. int val = co_await add_async(1, 2, use_librf);
  333. std::cout << val << std::endl;
  334. //muldiv_async函数可能会抛异常,取决于val是否是0
  335. //异常将会带回到本协程里的代码,所以需要try-catch
  336. auto ab = co_await muldiv_async(9, val, use_librf);
  337. //C++17:
  338. //auto [a, b] = co_await muldiv_async(9, val, use_librf);
  339. std::string result = co_await tostring_async(std::get<0>(ab) + std::get<1>(ab), use_librf);
  340. std::cout << result << std::endl;
  341. }
  342. #ifndef __clang__
  343. catch (const std::exception& e)
  344. {
  345. std::cout << "exception signal : " << e.what() << std::endl;
  346. }
  347. catch (...)
  348. {
  349. std::cout << "exception signal : who knows?" << std::endl;
  350. }
  351. #endif
  352. };
  353. resumef::this_scheduler()->run_until_notask();
  354. }