158158#include < stop_token> // std::stop_source, std::stop_token
159159#include < optional>
160160#include < utility> // std::move, std::move_if_noexcept
161- #include < type_traits> // for noexcept specs
161+ #include < type_traits> // for requires clauses and noexcept specs
162162
163163namespace chops {
164164
165165template <typename T, typename Container = std::deque<T> >
166+ requires std::is_copy_constructible_v<T> || std::is_move_constructible_v<T>
166167class wait_queue {
167168private:
168169 mutable std::mutex m_mut;
@@ -195,8 +196,10 @@ class wait_queue {
195196 * @post @c stop_requested returns @c false.
196197 */
197198 wait_queue ()
198- // noexcept(std::is_nothrow_constructible<Container>::value)
199- : m_stop_src(std::stop_source{}), m_stop_tok((*m_stop_src).get_token()) {
199+ requires std::is_default_constructible_v<Container>
200+ // noexcept(std::is_nothrow_constructible_v<Container>)
201+ : m_stop_src(std::stop_source{}), m_stop_tok((*m_stop_src).get_token())
202+ {
200203 assert (empty ());
201204 assert (size () == size_type (0 ));
202205 assert (!stop_requested ());
@@ -212,8 +215,10 @@ class wait_queue {
212215 * @post @c size returns 0.
213216 */
214217 wait_queue (std::stop_token stop_tok)
215- // noexcept(std::is_nothrow_constructible<Container>::value)
216- : m_stop_tok(stop_tok) {
218+ requires std::is_default_constructible_v<Container>
219+ // noexcept(std::is_nothrow_constructible_v<Container, std::stop_token>)
220+ : m_stop_tok(stop_tok)
221+ {
217222 assert (empty ());
218223 assert (size () == size_type (0 ));
219224 }
@@ -236,15 +241,16 @@ class wait_queue {
236241 * @param container Container object to be moved from (or copied from if not
237242 * movable).
238243 *
239- * @post @c empty returns @c true if @c beg equals @c end otherwise returns @c false.
240- * @post @c size returns the distance between @c beg and @c end parameters.
244+ * @post @c empty and @c size match moved (or copied) in container.
241245 */
242246 wait_queue (Container&& container)
243- // noexcept(std::is_ something movable or maybe copyable )
247+ requires std::is_move_constructible_v<Container> ||
248+ std::is_copy_constructible_v<Container>
249+ // noexcept(std::is_nnthrow_constructible_v<Container, Container&&>)
244250 : m_stop_src(std::stop_source{}), m_stop_tok((*m_stop_src).get_token()),
245- m_data_queue (std::move(container)) {
246- // assert(empty() == (beg == end));
247- // assert((size() == size_type(0)) == (beg == end)); // std::distance constrains beg, end.
251+ m_data_queue (std::move(container))
252+ {
253+ // not easily assertible until contracts added to C++
248254 }
249255
250256 /* *
@@ -257,14 +263,14 @@ class wait_queue {
257263 * @param container Container object to be moved from (or copied from if not
258264 * movable).
259265 *
260- * @post @c empty returns @c true if @c beg equals @c end otherwise returns @c false.
261- * @post @c size returns the distance between @c beg and @c end parameters.
266+ * @post @c empty and @c size match moved (or copied) in container.
262267 */
263268 wait_queue (std::stop_token stop_tok, Container&& container)
264- // noexcept(std::is_nothrow_constructible<Container, Iter, Iter>::value)
265- : m_stop_tok(stop_tok), m_data_queue(std::move(container)) {
266- // assert(empty() == (beg == end));
267- // assert((size() == size_type(0)) == (beg == end)); // std::distance constrains beg, end.
269+ requires std::is_move_constructible_v<Container> ||
270+ std::is_copy_constructible_v<Container>
271+ // noexcept(std::is_nothrow_constructible_v<Container, std::stop_token, Container&&>)
272+ : m_stop_tok(stop_tok), m_data_queue(std::move(container))
273+ {
268274 }
269275
270276 /* *
@@ -290,9 +296,11 @@ class wait_queue {
290296 * @post @c size returns 0 or @c sz depending on container used.
291297 */
292298 wait_queue (size_type sz)
293- // noexcept(std::is_nothrow_constructible<Container, size_type>::value)
299+ requires std::is_constructible_v<Container, size_type>
300+ // noexcept(std::is_nothrow_constructible_v<Container, size_type>)
294301 : m_stop_src(std::stop_source{}), m_stop_tok((*m_stop_src).get_token()),
295- m_data_queue (sz) {
302+ m_data_queue (sz)
303+ {
296304 assert ((sz != size_type (0 )) || empty ());
297305 assert ((size () == size_type (0 )) || (size () == sz));
298306 }
@@ -310,8 +318,10 @@ class wait_queue {
310318 * @post @c size returns 0 or @c sz depending on container used.
311319 */
312320 wait_queue (std::stop_token stop_tok, size_type sz)
313- // noexcept(std::is_nothrow_constructible<Container, size_type>::value)
314- : m_stop_tok((*m_stop_src).get_token()), m_data_queue(sz) {
321+ requires std::is_constructible_v<Container, std::stop_token, size_type>
322+ // noexcept(std::is_nothrow_constructible_v<Container, std::stop_token, size_type>)
323+ : m_stop_tok((*m_stop_src).get_token()), m_data_queue(sz)
324+ {
315325 assert ((sz != size_type (0 )) || empty ());
316326 assert ((size () == size_type (0 )) || (size () == sz));
317327 }
@@ -341,13 +351,12 @@ class wait_queue {
341351 * external @c std::stop_token was passed in.
342352 */
343353 auto request_stop () noexcept
344- -> bool {
345-
354+ -> bool
355+ {
346356 if (m_stop_src) {
347357 return (*m_stop_src).request_stop ();
348358 }
349359 return false ;
350-
351360 }
352361
353362 /* *
@@ -364,9 +373,11 @@ class wait_queue {
364373 * @post If @c true is returned and @c empty is @c false, one of any threads waiting for a
365374 * value will be unblocked.
366375 */
367- auto push (const T& val) /* noexcept(std::is_nothrow_copy_constructible<T>::value) */
368- -> bool {
376+ auto push (const T& val) /* noexcept(std::is_nothrow_copy_constructible_v<T>) */
377+ -> bool
378+ requires requires (T val, Container c) { c.push_back (val); }
369379
380+ {
370381 if (m_stop_tok.stop_requested ()) {
371382 return false ;
372383 }
@@ -386,9 +397,11 @@ class wait_queue {
386397 * @post If @c true is returned and @c empty is @c false, one of any threads waiting for a
387398 * value will be unblocked.
388399 */
389- auto push (T&& val) /* noexcept(std::is_nothrow_move_constructible<T>::value) */
390- -> bool {
400+ auto push (T&& val) /* noexcept(std::is_nothrow_move_constructible_v<T>) */
401+ -> bool
402+ requires requires (T val, Container c) { c.push_back (val); }
391403
404+ {
392405 if (m_stop_tok.stop_requested ()) {
393406 return false ;
394407 }
@@ -416,17 +429,18 @@ class wait_queue {
416429 * value will be unblocked.
417430 */
418431 template <typename ... Args>
419- auto emplace_push (Args &&... args) /* noexcept(std::is_nothrow_constructible<T, Args...>::value)*/
420- -> bool {
421-
432+ auto emplace_push (Args &&... args) /* noexcept(std::is_nothrow_constructible_v<T, Args...>)*/
433+ -> bool
434+ requires requires (Args && args, Container c) { c.emplace_back (args); }
435+
436+ {
422437 if (m_stop_tok.stop_requested ()) {
423438 return false ;
424439 }
425440 lock_guard lk{m_mut};
426441 m_data_queue.emplace_back (std::forward<Args>(args)...);
427442 m_data_cond.notify_one ();
428443 return true ;
429-
430444 }
431445
432446 /* *
@@ -443,9 +457,11 @@ class wait_queue {
443457 * @post If a non empty value is returned, until a push function is called, @c size is one
444458 * less than before this function was called.
445459 */
446- auto wait_and_pop () /* noexcept(std::is_nothrow_constructible<T>::value) */
447- -> std::optional<T> {
460+ [[nodiscard]] auto wait_and_pop () /* noexcept(std::is_nothrow_constructible_v<T>) */
461+ -> std::optional<T>
462+ requires requires (Container c) { c.empty (); c.pop_front (); }
448463
464+ {
449465 std::unique_lock<std::mutex> lk{m_mut};
450466 if (!m_data_cond.wait ( lk, m_stop_tok, [this ] { return !m_data_queue.empty (); } )) {
451467 return std::optional<T> {}; // queue was request to stop, no data available
@@ -472,9 +488,10 @@ class wait_queue {
472488 * @post If a non empty value is returned, until a push function is called, @c size is one
473489 * less than before this function was called.
474490 */
475- auto try_pop () /* noexcept(std::is_nothrow_constructible<T>::value) */
476- -> std::optional<T> {
477-
491+ [[nodiscard]] auto try_pop () /* noexcept(std::is_nothrow_constructible_v<T>) */
492+ -> std::optional<T>
493+ requires requires (Container c) { c.empty (); c.pop_front (); }
494+ {
478495 if (m_stop_tok.stop_requested ()) {
479496 return std::optional<T> {};
480497 }
@@ -520,9 +537,11 @@ class wait_queue {
520537 * same @c wait_queue since it results in recursive mutex locks.
521538 */
522539 template <typename F>
523- auto apply (F&& func) const /* noexcept(std::is_nothrow_invocable<F&&, const T&>::value) */
524- -> void {
540+ auto apply (F&& func) const /* noexcept(std::is_nothrow_invocable_v<F&&, const T&>) */
541+ -> void
542+ requires requires (T elem, F func) { func (elem); }
525543
544+ {
526545 lock_guard lk{m_mut};
527546 for (const T& elem : m_data_queue) {
528547 func (elem);
@@ -536,21 +555,23 @@ class wait_queue {
536555 *
537556 * @return @c true if the @c stop_requested has been called.
538557 */
539- auto stop_requested () const noexcept
540- -> bool {
558+ [[nodiscard]] auto stop_requested () const noexcept
559+ -> bool
541560
561+ {
542562 return m_stop_tok.stop_requested ();
543-
544563 }
545564
546565 /* *
547566 * Query whether the @c wait_queue is empty or not.
548567 *
549568 * @return @c true if the @c wait_queue is empty.
550569 */
551- auto empty () const /* noexcept */
552- -> bool {
570+ [[nodiscard]] auto empty () const /* noexcept */
571+ -> bool
572+ requires requires (Container c) { c.empty (); }
553573
574+ {
554575 lock_guard lk{m_mut};
555576 return m_data_queue.empty ();
556577
@@ -561,9 +582,11 @@ class wait_queue {
561582 *
562583 * @return Number of elements in the @c wait_queue.
563584 */
564- auto size () const /* noexcept */
565- -> size_type {
585+ [[nodiscard]] auto size () const /* noexcept */
586+ -> size_type
587+ requires requires (Container c) { c.size (); }
566588
589+ {
567590 lock_guard lk{m_mut};
568591 return m_data_queue.size ();
569592
0 commit comments