result.hpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. // Copyright (c) 2023 Klemens D. Morgenstern
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  4. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_COBALT_RESULT_HPP
  6. #define BOOST_COBALT_RESULT_HPP
  7. #include <boost/cobalt/concepts.hpp>
  8. #include <boost/core/no_exceptions_support.hpp>
  9. #include <boost/system/result.hpp>
  10. namespace boost::cobalt
  11. {
  12. namespace detail
  13. {
  14. template<typename T>
  15. concept result_error =
  16. requires (const T & t, const source_location & loc)
  17. {
  18. system::throw_exception_from_error(t, loc);
  19. }
  20. || // ADL
  21. requires (const T & t, const source_location & loc)
  22. {
  23. throw_exception_from_error(t, loc);
  24. }
  25. ;
  26. }
  27. inline constexpr auto interpret_as_result(std::tuple<> &&)
  28. {
  29. return system::result<void>();
  30. }
  31. template<typename Arg>
  32. auto interpret_as_result(std::tuple<Arg> && args)
  33. {
  34. if constexpr (detail::result_error<Arg>)
  35. {
  36. if (std::get<0>(args))
  37. return system::result<void, Arg>(system::in_place_error, std::get<0>(args));
  38. else
  39. return system::result<void, Arg>(system::in_place_value);
  40. }
  41. else
  42. return system::result<Arg>(std::move(std::get<0>(args)));
  43. }
  44. template<typename First, typename ... Args>
  45. requires (!detail::result_error<First> && sizeof...(Args) > 0u)
  46. auto interpret_as_result(std::tuple<First, Args...> && args) -> system::result<std::tuple<First, Args...>>
  47. {
  48. return std::move(args);
  49. }
  50. template<detail::result_error Error, typename ... Args>
  51. requires (sizeof...(Args) > 1u)
  52. auto interpret_as_result(std::tuple<Error, Args...> && args) -> system::result<std::tuple<Args...>, Error>
  53. {
  54. if (std::get<0>(args))
  55. return {system::in_place_error, std::move(std::get<0>(args))};
  56. return {
  57. system::in_place_value,
  58. std::apply([](auto, auto && ... rest) {return std::make_tuple(std::move(rest)...);})
  59. };
  60. }
  61. template<detail::result_error Error, typename Arg>
  62. auto interpret_as_result(std::tuple<Error, Arg> && args) -> system::result<Arg, Error>
  63. {
  64. if (std::get<0>(args))
  65. return {system::in_place_error, std::get<0>(args)};
  66. return {system::in_place_value, std::get<1>(std::move(args))};
  67. }
  68. struct as_result_tag {};
  69. struct as_tuple_tag {};
  70. template<awaitable Aw>
  71. struct as_result_t
  72. {
  73. as_result_t(Aw && aw) : aw_(std::forward<Aw>(aw)) {}
  74. bool await_ready() { return aw_.await_ready();}
  75. template<typename T>
  76. auto await_suspend(std::coroutine_handle<T> h) { return aw_.await_suspend(h);}
  77. auto await_resume()
  78. {
  79. if constexpr (requires {aw_.await_resume(as_result_tag{});})
  80. return aw_.await_resume(as_result_tag{});
  81. else
  82. {
  83. using type = decltype(aw_.await_resume());
  84. if constexpr (std::is_void_v<type>)
  85. {
  86. using res_t = system::result<type, std::exception_ptr>;
  87. BOOST_TRY
  88. {
  89. aw_.await_resume();
  90. return res_t{system::in_place_value};
  91. }
  92. BOOST_CATCH (...)
  93. {
  94. return res_t{system::in_place_error, std::current_exception()};
  95. }
  96. BOOST_CATCH_END
  97. }
  98. else
  99. {
  100. using res_t = system::result<type, std::exception_ptr>;
  101. BOOST_TRY
  102. {
  103. return res_t{system::in_place_value, aw_.await_resume()};
  104. }
  105. BOOST_CATCH (...)
  106. {
  107. return res_t{system::in_place_error, std::current_exception()};
  108. }
  109. BOOST_CATCH_END
  110. }
  111. }
  112. }
  113. private:
  114. Aw aw_;
  115. };
  116. template<awaitable Aw>
  117. as_result_t(Aw &&) -> as_result_t<Aw>;
  118. template<awaitable_type Aw>
  119. auto as_result(Aw && aw) -> as_result_t<Aw>
  120. {
  121. return as_result_t<Aw>(std::forward<Aw>(aw));
  122. }
  123. template<typename Aw>
  124. requires requires (Aw && aw)
  125. {
  126. {std::forward<Aw>(aw).operator co_await()} -> awaitable_type;
  127. }
  128. auto as_result(Aw && aw)
  129. {
  130. struct lazy_tuple
  131. {
  132. Aw aw;
  133. auto operator co_await ()
  134. {
  135. return as_result(std::forward<Aw>(aw).operator co_await());
  136. }
  137. };
  138. return lazy_tuple{std::forward<Aw>(aw)};
  139. }
  140. template<typename Aw>
  141. requires requires (Aw && aw)
  142. {
  143. {operator co_await(std::forward<Aw>(aw))} -> awaitable_type;
  144. }
  145. auto as_result(Aw && aw)
  146. {
  147. struct lazy_tuple
  148. {
  149. Aw aw;
  150. auto operator co_await ()
  151. {
  152. return as_result(operator co_await(std::forward<Aw>(aw)));
  153. }
  154. };
  155. return lazy_tuple{std::forward<Aw>(aw)};
  156. }
  157. template<awaitable Aw>
  158. struct as_tuple_t
  159. {
  160. as_tuple_t(Aw && aw) : aw_(std::forward<Aw>(aw)) {}
  161. bool await_ready() { return aw_.await_ready();}
  162. template<typename T>
  163. auto await_suspend(std::coroutine_handle<T> h) { return aw_.await_suspend(h);}
  164. auto await_resume()
  165. {
  166. using type = decltype(aw_.await_resume());
  167. if constexpr (requires {aw_.await_resume(as_tuple_tag{});})
  168. return aw_.await_resume(as_tuple_tag{});
  169. else if (noexcept(aw_.await_resume()))
  170. {
  171. if constexpr (std::is_void_v<type>)
  172. {
  173. aw_.await_resume();
  174. return std::make_tuple();
  175. }
  176. else
  177. return std::make_tuple(aw_.await_resume());
  178. }
  179. else
  180. {
  181. if constexpr (std::is_void_v<type>)
  182. {
  183. BOOST_TRY
  184. {
  185. aw_.await_resume();
  186. return std::make_tuple(std::exception_ptr());
  187. }
  188. BOOST_CATCH (...)
  189. {
  190. return make_tuple_(std::current_exception());
  191. }
  192. BOOST_CATCH_END
  193. }
  194. else
  195. {
  196. BOOST_TRY
  197. {
  198. return make_tuple_(std::exception_ptr(), aw_.await_resume());
  199. }
  200. BOOST_CATCH (...)
  201. {
  202. return make_tuple_(std::current_exception(), type());
  203. }
  204. BOOST_CATCH_END
  205. }
  206. }
  207. }
  208. private:
  209. template<typename ... Args>
  210. std::tuple<std::exception_ptr, Args...> make_tuple_(std::exception_ptr ep, std::tuple<Args...> && tup)
  211. {
  212. return std::apply(
  213. [&](auto ... args)
  214. {
  215. return std::make_tuple(std::move(ep), std::move(args)...);
  216. }, std::move(tup));
  217. }
  218. template<typename Arg>
  219. std::tuple<std::exception_ptr, Arg> make_tuple_(std::exception_ptr ep, Arg && arg)
  220. {
  221. return std::make_tuple(std::move(ep), std::move(arg));
  222. }
  223. private:
  224. Aw aw_;
  225. };
  226. template<awaitable Aw>
  227. as_tuple_t(Aw &&) -> as_tuple_t<Aw>;
  228. template<awaitable_type Aw>
  229. auto as_tuple(Aw && aw) -> as_tuple_t<Aw>
  230. {
  231. return as_tuple_t<Aw>(std::forward<Aw>(aw));
  232. }
  233. template<typename Aw>
  234. requires requires (Aw && aw)
  235. {
  236. {std::forward<Aw>(aw).operator co_await()} -> awaitable_type;
  237. }
  238. auto as_tuple(Aw && aw)
  239. {
  240. struct lazy_tuple
  241. {
  242. Aw aw;
  243. auto operator co_await ()
  244. {
  245. return as_tuple(std::forward<Aw>(aw).operator co_await());
  246. }
  247. };
  248. return lazy_tuple{std::forward<Aw>(aw)};
  249. }
  250. template<typename Aw>
  251. requires requires (Aw && aw)
  252. {
  253. {operator co_await(std::forward<Aw>(aw))} -> awaitable_type;
  254. }
  255. auto as_tuple(Aw && aw)
  256. {
  257. struct lazy_tuple
  258. {
  259. Aw aw;
  260. auto operator co_await ()
  261. {
  262. return as_tuple(operator co_await(std::forward<Aw>(aw)));
  263. }
  264. };
  265. return lazy_tuple{std::forward<Aw>(aw)};
  266. }
  267. }
  268. #endif //BOOST_COBALT_RESULT_HPP