112112 *
113113 * The container type must support the following (depending on which
114114 * methods are called): default construction, construction with an initial size,
115- * @c push_back (preferably overloaded for both copy and move),
115+ * @c push_back (preferably overloaded for both copy and move semantics ),
116116 * @c emplace_back (with a template parameter pack), @c front, @c pop_front,
117117 * @c empty, and @c size. The container must also have a @c size_type
118- * defined.
118+ * defined. Type constraints and concepts are defined for the various
119+ * type requirements.
119120 *
120121 * Iterators on a @c wait_queue are not supported, due to obvious difficulties
121122 * with maintaining consistency and integrity. The @c apply method can be used to
159160#include < optional>
160161#include < utility> // std::move, std::move_if_noexcept
161162#include < type_traits> // for requires clauses and noexcept specs
163+ #include < concepts>
162164
163165namespace chops {
164166
167+ // requirements for wait_queue container
168+
169+ template <typename Ctr, typename T>
170+ concept supports_push_back = requires (Ctr ctr, T val) {
171+ ctr.push_back (val);
172+ };
173+
174+ template <typename Ctr, typename ... Args>
175+ concept supports_emplace_back = requires (Ctr ctr, Args args) {
176+ ctr.emplace_back (args);
177+ };
178+
179+ template <typename Ctr>
180+ concept supports_empty = requires (Ctr ctr) {
181+ ctr.empty ();
182+ };
183+
184+ template <typename Ctr>
185+ concept supports_pop_front = requires (Ctr ctr) {
186+ ctr.pop_front ();
187+ };
188+
189+ template <typename Ctr>
190+ concept supports_size = requires (Ctr ctr) {
191+ ctr.size ();
192+ };
193+
194+ // declaration for wait_queue
195+
165196template <typename T, typename Container = std::deque<T> >
166197 requires std::is_copy_constructible_v<T> || std::is_move_constructible_v<T>
167198class wait_queue {
@@ -375,7 +406,7 @@ class wait_queue {
375406 */
376407 auto push (const T& val) /* noexcept(std::is_nothrow_copy_constructible_v<T>) */
377408 -> bool
378- requires requires (T val, Container c) { c. push_back (val); }
409+ requires supports_push_back<Container, T>
379410
380411 {
381412 if (m_stop_tok.stop_requested ()) {
@@ -399,7 +430,7 @@ class wait_queue {
399430 */
400431 auto push (T&& val) /* noexcept(std::is_nothrow_move_constructible_v<T>) */
401432 -> bool
402- requires requires (T val, Container c) { c. push_back (val); }
433+ requires supports_push_back<Container, T>
403434
404435 {
405436 if (m_stop_tok.stop_requested ()) {
@@ -431,7 +462,7 @@ class wait_queue {
431462 template <typename ... Args>
432463 auto emplace_push (Args &&... args) /* noexcept(std::is_nothrow_constructible_v<T, Args...>)*/
433464 -> bool
434- // requires requires ( Container c) { c.emplace_back(); }
465+ requires supports_emplace_back< Container, Args...>
435466
436467 {
437468 if (m_stop_tok.stop_requested ()) {
@@ -459,7 +490,7 @@ class wait_queue {
459490 */
460491 [[nodiscard]] auto wait_and_pop () /* noexcept(std::is_nothrow_constructible_v<T>) */
461492 -> std::optional<T>
462- requires requires ( Container c) { c. empty (); c. pop_front (); }
493+ requires supports_empty< Container> && supports_pop_front<Container>
463494
464495 {
465496 std::unique_lock<std::mutex> lk{m_mut};
@@ -490,7 +521,7 @@ class wait_queue {
490521 */
491522 [[nodiscard]] auto try_pop () /* noexcept(std::is_nothrow_constructible_v<T>) */
492523 -> std::optional<T>
493- requires requires ( Container c) { c. empty (); c. pop_front (); }
524+ requires supports_empty< Container> && supports_pop_front<Container>
494525 {
495526 if (m_stop_tok.stop_requested ()) {
496527 return std::optional<T> {};
@@ -539,7 +570,7 @@ class wait_queue {
539570 template <typename F>
540571 auto apply (F&& func) const /* noexcept(std::is_nothrow_invocable_v<F&&, const T&>) */
541572 -> void
542- requires requires (T elem, F func) { func (elem); }
573+ requires std::is_invocable_v<F, T>
543574
544575 {
545576 lock_guard lk{m_mut};
@@ -569,7 +600,7 @@ class wait_queue {
569600 */
570601 [[nodiscard]] auto empty () const /* noexcept */
571602 -> bool
572- requires requires ( Container c) { c. empty (); }
603+ requires supports_empty< Container>
573604
574605 {
575606 lock_guard lk{m_mut};
@@ -584,7 +615,7 @@ class wait_queue {
584615 */
585616 [[nodiscard]] auto size () const /* noexcept */
586617 -> size_type
587- requires requires ( Container c) { c. size (); }
618+ requires supports_size< Container>
588619
589620 {
590621 lock_guard lk{m_mut};
0 commit comments