You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

365 lines
12KB

  1. /// \file
  2. // Range v3 library
  3. //
  4. // Copyright Casey Carter 2017
  5. //
  6. // Use, modification and distribution is subject to the
  7. // Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at
  9. // http://www.boost.org/LICENSE_1_0.txt)
  10. //
  11. // Project home: https://github.com/ericniebler/range-v3
  12. //
  13. #ifndef RANGES_V3_EXPERIMENTAL_UTILITY_GENERATOR_HPP
  14. #define RANGES_V3_EXPERIMENTAL_UTILITY_GENERATOR_HPP
  15. #include <range/v3/detail/config.hpp>
  16. #if RANGES_CXX_COROUTINES >= RANGES_CXX_COROUTINES_TS1
  17. #include <atomic>
  18. #include <cstddef>
  19. #include <exception>
  20. #include <experimental/coroutine>
  21. #include <utility>
  22. #include <meta/meta.hpp>
  23. #include <concepts/concepts.hpp>
  24. #include <range/v3/range_fwd.hpp>
  25. #include <range/v3/iterator/default_sentinel.hpp>
  26. #include <range/v3/range/traits.hpp>
  27. #include <range/v3/utility/box.hpp>
  28. #include <range/v3/utility/semiregular_box.hpp>
  29. #include <range/v3/utility/swap.hpp>
  30. #include <range/v3/view/all.hpp>
  31. #include <range/v3/view/facade.hpp>
  32. #if defined(_MSC_VER) && !defined(RANGES_SILENCE_COROUTINE_WARNING)
  33. #ifdef __clang__
  34. #pragma message( \
  35. "DANGER: clang doesn't (yet?) grok the MSVC coroutine ABI. " \
  36. "Use at your own risk. " \
  37. "(RANGES_SILENCE_COROUTINE_WARNING will silence this message.)")
  38. #elif defined RANGES_WORKAROUND_MSVC_835948
  39. #pragma message( \
  40. "DANGER: ranges::experimental::generator is fine, but this " \
  41. "version of MSVC likely miscompiles ranges::experimental::sized_generator. " \
  42. "Use the latter at your own risk. " \
  43. "(RANGES_SILENCE_COROUTINE_WARNING will silence this message.)")
  44. #endif
  45. #endif // RANGES_SILENCE_COROUTINE_WARNINGS
  46. namespace ranges
  47. {
  48. /// \addtogroup group-view
  49. /// @{
  50. namespace experimental
  51. {
  52. // The type of size() for a sized_generator
  53. using generator_size_t = std::size_t;
  54. // Type upon which to co_await to set the size of a sized_generator
  55. enum struct generator_size : generator_size_t
  56. {
  57. invalid = ~generator_size_t(0)
  58. };
  59. template<typename Promise = void>
  60. struct RANGES_EMPTY_BASES coroutine_owner;
  61. class enable_coroutine_owner
  62. {
  63. template<class>
  64. friend struct coroutine_owner;
  65. std::atomic<unsigned int> refcount_{1};
  66. };
  67. } // namespace experimental
  68. /// \cond
  69. namespace detail
  70. {
  71. inline void resume(std::experimental::coroutine_handle<> coro)
  72. {
  73. // Pre: coro refers to a suspended coroutine.
  74. RANGES_EXPECT(coro);
  75. RANGES_EXPECT(!coro.done());
  76. coro.resume();
  77. }
  78. namespace coroutine_owner_
  79. {
  80. struct adl_hook
  81. {};
  82. template<typename Promise>
  83. void swap(experimental::coroutine_owner<Promise> & x,
  84. experimental::coroutine_owner<Promise> & y) noexcept
  85. {
  86. x.swap(y);
  87. }
  88. } // namespace coroutine_owner_
  89. } // namespace detail
  90. /// \endcond
  91. namespace experimental
  92. {
  93. // An owning coroutine_handle
  94. template<typename Promise>
  95. struct RANGES_EMPTY_BASES coroutine_owner
  96. : private std::experimental::coroutine_handle<Promise>
  97. , private detail::coroutine_owner_::adl_hook
  98. {
  99. CPP_assert(derived_from<Promise, enable_coroutine_owner>);
  100. using base_t = std::experimental::coroutine_handle<Promise>;
  101. using base_t::operator bool;
  102. using base_t::done;
  103. using base_t::promise;
  104. coroutine_owner() = default;
  105. explicit constexpr coroutine_owner(base_t coro) noexcept
  106. : base_t(coro)
  107. {}
  108. coroutine_owner(coroutine_owner && that) noexcept
  109. : base_t(ranges::exchange(that.base(), {}))
  110. , copied_(that.copied_.load(std::memory_order_relaxed))
  111. {}
  112. coroutine_owner(coroutine_owner const & that) noexcept
  113. : base_t(that.handle())
  114. , copied_(that.handle() != nullptr)
  115. {
  116. if(*this)
  117. {
  118. that.copied_.store(true, std::memory_order_relaxed);
  119. base().promise().refcount_.fetch_add(1, std::memory_order_relaxed);
  120. }
  121. }
  122. ~coroutine_owner()
  123. {
  124. if(base() && (!copied_.load(std::memory_order_relaxed) ||
  125. 1 == base().promise().refcount_.fetch_sub(
  126. 1, std::memory_order_acq_rel)))
  127. base().destroy();
  128. }
  129. coroutine_owner & operator=(coroutine_owner that) noexcept
  130. {
  131. swap(that);
  132. return *this;
  133. }
  134. void resume()
  135. {
  136. detail::resume(handle());
  137. }
  138. void operator()()
  139. {
  140. detail::resume(handle());
  141. }
  142. void swap(coroutine_owner & that) noexcept
  143. {
  144. bool tmp = copied_.load(std::memory_order_relaxed);
  145. copied_.store(that.copied_.load(std::memory_order_relaxed),
  146. std::memory_order_relaxed);
  147. that.copied_.store(tmp, std::memory_order_relaxed);
  148. std::swap(base(), that.base());
  149. }
  150. base_t handle() const noexcept
  151. {
  152. return *this;
  153. }
  154. private:
  155. std::atomic<bool> copied_{false};
  156. base_t & base() noexcept
  157. {
  158. return *this;
  159. }
  160. };
  161. } // namespace experimental
  162. /// \cond
  163. namespace detail
  164. {
  165. template<typename Reference>
  166. struct generator_promise : experimental::enable_coroutine_owner
  167. {
  168. std::exception_ptr except_ = nullptr;
  169. CPP_assert(std::is_reference<Reference>::value ||
  170. copy_constructible<Reference>);
  171. generator_promise * get_return_object() noexcept
  172. {
  173. return this;
  174. }
  175. std::experimental::suspend_always initial_suspend() const noexcept
  176. {
  177. return {};
  178. }
  179. std::experimental::suspend_always final_suspend() const noexcept
  180. {
  181. return {};
  182. }
  183. void return_void() const noexcept
  184. {}
  185. void unhandled_exception() noexcept
  186. {
  187. except_ = std::current_exception();
  188. RANGES_EXPECT(except_);
  189. }
  190. template<typename Arg>
  191. auto yield_value(Arg && arg) noexcept(
  192. std::is_nothrow_assignable<semiregular_box_t<Reference> &, Arg>::value)
  193. -> CPP_ret(std::experimental::suspend_always)( //
  194. requires convertible_to<Arg, Reference> &&
  195. std::is_assignable<semiregular_box_t<Reference> &, Arg>::value)
  196. {
  197. ref_ = std::forward<Arg>(arg);
  198. return {};
  199. }
  200. std::experimental::suspend_never await_transform(
  201. experimental::generator_size) const noexcept
  202. {
  203. RANGES_ENSURE_MSG(false,
  204. "Invalid size request for a non-sized generator");
  205. return {};
  206. }
  207. meta::if_<std::is_reference<Reference>, Reference, Reference const &> read()
  208. const noexcept
  209. {
  210. return ref_;
  211. }
  212. private:
  213. semiregular_box_t<Reference> ref_;
  214. };
  215. template<typename Reference>
  216. struct sized_generator_promise : generator_promise<Reference>
  217. {
  218. sized_generator_promise * get_return_object() noexcept
  219. {
  220. return this;
  221. }
  222. std::experimental::suspend_never initial_suspend() const noexcept
  223. {
  224. // sized_generator doesn't suspend at its initial suspend point because...
  225. return {};
  226. }
  227. std::experimental::suspend_always await_transform(
  228. experimental::generator_size size) noexcept
  229. {
  230. // ...we need the coroutine set the size of the range first by
  231. // co_awaiting on a generator_size.
  232. size_ = size;
  233. return {};
  234. }
  235. experimental::generator_size_t size() const noexcept
  236. {
  237. RANGES_EXPECT(size_ != experimental::generator_size::invalid);
  238. return static_cast<experimental::generator_size_t>(size_);
  239. }
  240. private:
  241. experimental::generator_size size_ = experimental::generator_size::invalid;
  242. };
  243. } // namespace detail
  244. /// \endcond
  245. namespace experimental
  246. {
  247. template<typename Reference, typename Value = uncvref_t<Reference>>
  248. struct sized_generator;
  249. template<typename Reference, typename Value = uncvref_t<Reference>>
  250. struct generator : view_facade<generator<Reference, Value>>
  251. {
  252. using promise_type = detail::generator_promise<Reference>;
  253. constexpr generator() noexcept = default;
  254. generator(promise_type * p)
  255. : coro_{handle::from_promise(*p)}
  256. {
  257. RANGES_EXPECT(coro_);
  258. }
  259. private:
  260. friend range_access;
  261. friend struct sized_generator<Reference, Value>;
  262. using handle = std::experimental::coroutine_handle<promise_type>;
  263. coroutine_owner<promise_type> coro_;
  264. struct cursor
  265. {
  266. using value_type = Value;
  267. cursor() = default;
  268. constexpr explicit cursor(handle coro) noexcept
  269. : coro_{coro}
  270. {}
  271. bool equal(default_sentinel_t) const
  272. {
  273. RANGES_EXPECT(coro_);
  274. if(coro_.done())
  275. {
  276. auto & e = coro_.promise().except_;
  277. if(e)
  278. std::rethrow_exception(std::move(e));
  279. return true;
  280. }
  281. return false;
  282. }
  283. void next()
  284. {
  285. detail::resume(coro_);
  286. }
  287. Reference read() const
  288. {
  289. RANGES_EXPECT(coro_);
  290. return coro_.promise().read();
  291. }
  292. private:
  293. handle coro_ = nullptr;
  294. };
  295. cursor begin_cursor()
  296. {
  297. detail::resume(coro_.handle());
  298. return cursor{coro_.handle()};
  299. }
  300. };
  301. template<typename Reference, typename Value /* = uncvref_t<Reference>*/>
  302. struct sized_generator : generator<Reference, Value>
  303. {
  304. using promise_type = detail::sized_generator_promise<Reference>;
  305. using handle = std::experimental::coroutine_handle<promise_type>;
  306. constexpr sized_generator() noexcept = default;
  307. sized_generator(promise_type * p)
  308. : generator<Reference, Value>{p}
  309. {}
  310. generator_size_t size() const noexcept
  311. {
  312. return promise().size();
  313. }
  314. private:
  315. using generator<Reference, Value>::coro_;
  316. promise_type const & promise() const noexcept
  317. {
  318. RANGES_EXPECT(coro_);
  319. return static_cast<promise_type const &>(coro_.promise());
  320. }
  321. };
  322. } // namespace experimental
  323. /// @}
  324. } // namespace ranges
  325. #endif // RANGES_CXX_COROUTINES >= RANGES_CXX_COROUTINES_TS1
  326. #endif // RANGES_V3_EXPERIMENTAL_UTILITY_GENERATOR_HPP