8000 Changed iterator ctor to take a container by moving or copying · connectivecpp/wait-queue@89cfece · GitHub
[go: up one dir, main page]

Skip to content

Commit 89cfece

Browse files
Changed iterator ctor to take a container by moving or copying
1 parent 9b43ce1 commit 89cfece

File tree

2 files changed

+103
-98
lines changed

2 files changed

+103
-98
lines changed

include/queue/wait_queue.hpp

Lines changed: 47 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@
2727
*
2828
* In particular, @c wait_queue:
2929
*
30-
* - Has been tested with Martin Moene's @c ring_span library for the internal container.
31-
* A @c ring_span is traditionally known as a "ring buffer" or "circular buffer". This
32-
* implies that the @c wait_queue can be used in environments where dynamic memory
33-
* management (heap) is not allowed or is problematic. In particular, no heap memory is
34-
* directly allocated within the @c wait_queue object.
30+
* - Has been tested with Martin Moene's @c ring_span library for the internal container,
31+
* as well as Justin Masiulis' @c circular_buffer library. A "ring buffer" or
32+
* "circular buffer" uses a fixed size container and implies that the @c wait_queue can
33+
* be used in environments where dynamic memory management (heap) is not allowed or is
34+
* problematic. In particular, no heap memory will be directly allocated within the
35+
* @c wait_queue object. A @c ring_span is a view on a container object instead of
36+
* directly owning the container, so there are differences in construction and
37+
* container management.
3538
*
3639
* - Does not throw or catch exceptions anywhere in its code base. If a value being pushed
3740
* on to the queue throws an exception, it can be caught by the pushing code (or higher
@@ -102,13 +105,13 @@
102105
* @code
103106
* const int sz = 20;
104107
* int buf[sz];
105-
* chops::wait_queue<int, nonstd::ring_span<int> > wq(buf+0, buf+sz);
108+
* chops::wait_queue<int, nonstd::ring_span<int> >
109+
* wq { nonstd::ring_span<int> { buf+0, buf+sz } };
106110
* // push and pop same as code with default container
107111
* @endcode
108112
*
109113
* The container type must support the following (depending on which
110-
* methods are called): default construction, construction from a
111-
* begin and end iterator, construction with an initial size,
114+
* methods are called): default construction, construction with an initial size,
112115
* @c push_back (preferably overloaded for both copy and move),
113116
* @c emplace_back (with a template parameter pack), @c front, @c pop_front,
114117
* @c empty, and @c size. The container must also have a @c size_type
@@ -134,8 +137,9 @@
134137
* @c circular_buffer then the default constructor for @c wait_queue cannot be used
135138
* (since it would result in a container with an empty capacity).
136139 *
140+
* Thanks go to Lou Langholtz for adding DBC (Design by Contract) assertions.
137141
*
138-
* @authors Cliff Green, Anthony Williams
142+
* @authors Cliff Green, Lou Langholtz, Anthony Williams
139143
*
140144
* @copyright (c) 2017-2024 by Cliff Green
141145
*
@@ -186,9 +190,9 @@ class wait_queue {
186190
* anything, so a different @c wait_queue constructor must be used if
187191
* instantiated with a @c boost @c circular_buffer.
188192
*
189-
* @post @c empty returns true.
193+
* @post @c empty returns @c true.
190194
* @post @c size returns 0.
191-
* @post @c stop_requested return false.
195+
* @post @c stop_requested returns @c false.
192196
*/
193197
wait_queue()
194198
// noexcept(std::is_nothrow_constructible<Container>::value)
@@ -204,7 +208,7 @@ class wait_queue {
204208
* @param stop_tok A @c std::stop_token which can be used to shutdown @c wait_queue
205209
* processing.
206210
*
207-
* @post @c empty returns true.
211+
* @post @c empty returns @c true.
208212
* @post @c size returns 0.
209213
*/
210214
wait_queue(std::stop_token stop_tok)
@@ -215,55 +219,52 @@ class wait_queue {
215219
}
216220

217221
/**
218-
* @brief Construct a @c wait_queue with an iterator range for the container.
222+
* @brief Construct a @c wait_queue by moving in an already constructed
223+
* container.
219224
*
220-
* Construct the container (or container view) with an iterator range. Whether
221-
* element copies are performed depends on the container type. Most container
222-
* types copy initial elements as defined by the range and the initial size is
223-
* set accordingly. A @c ring_span, however, uses the range distance to define
224-
* a capacity and sets the initial size to zero.
225+
* This constructor allows a container view to be used for the @c wait_queue
226+
* container. Typically a container view is initialized with an underlying
227+
* object, for example a statically allocated array. This allows @c wait_queue
228+
* to be used where dynamic memory is not allowed.
229+
*
230+
* This constructor also allows arbitrary initialization of the data inside
231+
* the container before it is managed by the @c wait_queue.
225232
*
226233
* An internal @c std::stop_source is used to provide a @c std::stop_token for
227234
* coordinating shutdown.
228235
*
229-
* @note This is the only constructor that can be used with a @c ring_span
230-
* container type.
231-
*
232-
* @param beg Beginning iterator.
233-
*
234-
* @param end Ending iterator.
236+
* @param container Container object to be moved from (or copied from if not
237+
* movable).
235238
*
236-
* @post @c empty returns true if @c beg equals @c end otherwise returns false.
239+
* @post @c empty returns @c true if @c beg equals @c end otherwise returns @c false.
237240
* @post @c size returns the distance between @c beg and @c end parameters.
238241
*/
239-
template <typename Iter>
240-
wait_queue(Iter beg, Iter end)
241-
// noexcept(std::is_nothrow_constructible<Container, Iter, Iter>::value)
242+
wait_queue(Container&& container)
243+
// noexcept(std::is_ something movable or maybe copyable )
242244
: m_stop_src(std::stop_source{}), m_stop_tok((*m_stop_src).get_token()),
243-
m_data_queue(beg, end) {
244-
assert(empty() == (beg == end));
245-
assert((size() == size_type(0)) == (beg == end)); // std::distance constrains beg, end.
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.
246248
}
247249

248250
/**
249-
* @brief Construct a @c wait_queue with an iterator range and a @c std::stop_token.
251+
* This constructor allows a container view to be used for the @c wait_queue
252+
* container. It also takes a @c std::stop_token for external shutdown.
250253
*
251254
* @param stop_tok A @c std::stop_token which can be used to shutdown @c wait_queue
252255
* processing.
253256
*
254-
* @param beg Beginning iterator.
255-
*
256-
* @param end Ending iterator.
257+
* @param container Container object to be moved from (or copied from if not
258+
* movable).
257259
*
258-
* @post @c empty returns true if @c beg equals @c end otherwise returns false.
260+
* @post @c empty returns @c true if @c beg equals @c end otherwise returns @c false.
259261
* @post @c size returns the distance between @c beg and @c end parameters.
260262
*/
261-
template <typename Iter>
262-
wait_queue(std::stop_token stop_tok, Iter beg, Iter end)
263+
wait_queue(std::stop_token stop_tok, Container&& container)
263264
// noexcept(std::is_nothrow_constructible<Container, Iter, Iter>::value)
264-
: m_stop_tok(stop_tok), m_data_queue(beg, end) {
265-
assert(empty() == (beg == end));
266-
assert((size() == size_type(0)) == (beg == end)); // std::distance constrains beg, end.
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.
267268
}
268269

269270
/**
@@ -285,7 +286,7 @@ class wait_queue {
285286
*
286287
* @param sz Capacity or initial size, depending on container type.
287288
*
288-
* @post If @c sz is 0 @c empty returns true, else behavior depends on container used.
289+
* @post If @c sz is 0 @c empty returns @c true, else behavior depends on container used.
289290
* @post @c size returns 0 or @c sz depending on container used.
290291
*/
291292
wait_queue(size_type sz)
@@ -305,7 +306,7 @@ class wait_queue {
305306
*
306307
* @param sz Capacity or initial size, depending on container type.
307308
*
308-
* @post If @c sz is 0 @c empty returns true, else behavior depends on container used.
309+
* @post If @c sz is 0 @c empty returns @c true, else behavior depends on container used.
309310
* @post @c size returns 0 or @c sz depending on container used.
310311
*/
311312
wait_queue(std::stop_token stop_tok, size_type sz)
@@ -360,7 +361,7 @@ class wait_queue {
360361
* @return @c true if successful, @c false if the @c wait_queue has been
361362
* requested to stop.
362363
*
363-
* @post If @c true is returned and @c empty is false, one of any threads waiting for a
364+
* @post If @c true is returned and @c empty is @c false, one of any threads waiting for a
364365
* value will be unblocked.
365366
*/
366367
auto push(const T& val) /* noexcept(std::is_nothrow_copy_constructible<T>::value) */
@@ -382,7 +383,7 @@ class wait_queue {
382383
* This method has the same semantics as the other @c push, except that the value will
383384
* be moved (if possible) instead of copied.
384385
*
385-
* @post If @c true is returned and @c empty is false, one of any threads waiting for a
386+
* @post If @c true is returned and @c empty is @c false, one of any threads waiting for a
386387
* value will be unblocked.
387388
*/
388389
auto push(T&& val) /* noexcept(std::is_nothrow_move_constructible<T>::value) */
@@ -411,7 +412,7 @@ class wait_queue {
411412
* @return @c true if successful, @c false if the @c wait_queue is has been requested
412413
* to stop.
413414
*
414-
* @post If @c true is returned and @c empty is false, one of any threads waiting for a
415+
* @post If @c true is returned and @c empty is @c false, one of any threads waiting for a
415416
* value will be unblocked.
416417
*/
417418
template <typename ... Args>

test/wait_queue_test.cpp

Lines changed: 56 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -39,24 +39,6 @@ using namespace std::literals::string_literals;
3939

4040
constexpr int N = 40;
4141

42-
// WQ creation function with non-used pointer for overloading
43-
template <typename T>
44-
auto create_wait_queue(const std::deque<T>*) {
45-
return chops::wait_queue<T, std::deque<T> > { };
46-
}
47-
48-
template <typename T>
49-
auto create_wait_queue(const nonstd::ring_span<T>*) {
50-
static T buf[N];
51-
return chops::wait_queue<T, nonstd::ring_span<T> > { buf+0, buf+N };
52-
}
53-
54-
template <typename T>
55-
auto create_wait_queue(const jm::circular_buffer<T, N>*) {
56-
return chops::wait_queue<T, jm::circular_buffer<T, N> > { };
57-
}
58-
59-
6042
template <typename Q>
6143
void non_threaded_push_test(Q& wq, const typename Q::value_type& val, int count) {
6244

@@ -110,6 +92,28 @@ void non_threaded_arithmetic_test(Q& wq, int count) {
11092

11193
}
11294

95+
template <typename Q>
96+
void non_threaded_test (Q& wq) {
97+
using val_type = typename Q::value_type;
98+
val_type val1;
99+
val_type val2;
100+
if constexpr (std::is_arithmetic_v<val_type>) {
101+
val1 = 42;
102+
val2 = 43;
103+
}
104+
else { // assume std::string value type in container - generalize as needed
105+
val1 = "Howzit going, bro!";
106+
val2 = "It's hanging, bro!";
107+
}
108+
109+
non_threaded_push_test(wq, val1, N);
110+
if constexpr (std::is_arithmetic_v<val_type>) {
111+
non_threaded_arithmetic_test(wq, N);
112+
}
113+
}
114+
115+
// threaded testing code
116+
113117
template <typename T>
114118
using set_elem = std::pair<int, T>;
115119

@@ -187,36 +191,35 @@ bool threaded_test(Q& wq, int num_readers, int num_writers, int slice, const T&
187191

188192
// non threaded test, multiple container types, multiple element types
189193

190-
TEMPLATE_TEST_CASE ( "Non-threaded wait_queue test", "[wait_queue] [non_threaded]",
191-
(std::deque<int>), (std::deque<double>), (std::deque<short>), (std::deque<std::string>),
192-
(nonstd::ring_span<int>), (nonstd::ring_span<double>), (nonstd::ring_span<short>), (nonstd::ring_span<std::string>),
193-
(jm::circular_buffer<int, N>), (jm::circular_buffer<double, N>),
194-
(jm::circular_buffer<short, N>), (jm::circular_buffer<std::string, N>) ) {
194+
TEMPLATE_TEST_CASE ( "Non-threaded wait_queue test, deque",
195+
"[wait_queue] [non_threaded] [deque]",
196+
int, double, short, std::string ) {
197+
chops::wait_queue<TestType> wq;
198+
non_threaded_test(wq);
199+
}
195200

196-
using val_type = typename TestType::value_type;
197-
val_type val1;
198-
val_type val2;
199-
if constexpr (std::is_arithmetic_v<val_type>) {
200-
val1 = 42;
201-
val2 = 43;
202-
}
203-
else { // assume std::string value type in container - generalize as needed
204-
val1 = "Howzit going, bro!";
205-
val2 = "It's hanging, bro!";
206-
}
201+
TEMPLATE_TEST_CASE ( "Non-threaded wait_queue test, ring_span",
202+
"[wait_queue] [non_threaded] [ring_span]",
203+
int, double, short, std::string ) {
207204

208-
auto wq = create_wait_queue( static_cast<const TestType*>(nullptr) );
209-
non_threaded_push_test(wq, val1, N);
210-
if constexpr (std::is_arithmetic_v<val_type>) {
211-
non_threaded_arithmetic_test(wq, N);
212-
}
205+
static TestType buf[N];
206+
chops::wait_queue<TestType, nonstd::ring_span<TestType>> wq
207+
{ nonstd::ring_span<TestType> { buf+0, buf+N } };
208+
non_threaded_test(wq);
209+
}
210+
211+
TEMPLATE_TEST_CASE ( "Non-threaded wait_queue test, circular_buffer",
212+
"[wait_queue] [non_threaded] [circular_buffer]",
213+
int, double, short, std::string ) {
214+
chops::wait_queue<TestType, jm::circular_buffer<TestType, N>> wq;
215+
non_threaded_test(wq);
213216
}
214217

215218
/*
216219
*/
217220

218-
SCENARIO ( "Non-threaded wait_queue test, testing copy construction without move construction",
219-
"[wait_queue] [no_move]" ) {
221+
TEST_CASE ( "Non-threaded wait_queue test, testing copy construction without move construction",
222+
"[wait_queue] [no_move]" ) {
220223

221224
struct Foo {
222225
Foo() = delete;
@@ -235,8 +238,8 @@ SCENARIO ( "Non-threaded wait_queue test, testing copy construction without move
235238
non_threaded_push_test(wq, Foo(42.0), N);
236239
}
237240

238-
SCENARIO ( "Non-threaded wait_queue test, testing move construction without copy construction",
239-
"[wait_queue] [no_copy]" ) {
241+
TEST_CASE ( "Non-threaded wait_queue test, testing move construction without copy construction",
242+
"[wait_queue] [no_copy]" ) {
240243

241244
struct Bar {
242245
Bar() = delete;
@@ -262,8 +265,8 @@ SCENARIO ( "Non-threaded wait_queue test, testing move construction without copy
262265
REQUIRE (wq.empty());
263266
}
264267

265-
SCENARIO ( "Non-threaded wait_queue test, testing complex constructor and emplacement",
266-
"[wait_queue] [complex_type] [deque]" ) {
268+
TEST_CASE ( "Non-threaded wait_queue test, testing complex constructor and emplacement",
269+
"[wait_queue] [complex_type] [deque]" ) {
267270

268271
struct Band {
269272
using engagement_type = std::vector<std::vector<std::string> >;
@@ -375,12 +378,13 @@ TEST_CASE ( "Vector of vector of float, move and copy",
375378

376379
}
377380

378-
SCENARIO ( "Fixed size ring_span, testing wrap around with int type",
379-
"[wait_queue] [int] [ring_span_wrap_around]" ) {
381+
TEST_CASE ( "Fixed size ring_span, testing wrap around with int type",
382+
"[wait_queue] [int] [ring_span_wrap_around]" ) {
380383

381384

382385
int buf[N];
383-
chops::wait_queue<int, nonstd::ring_span<int> > wq(buf+0, buf+N);
386+
chops::wait_queue<int, nonstd::ring_span<int> > wq
387+
{ nonstd::ring_span<int> { buf+0, buf+N } };
384388

385389
constexpr int Answer = 42;
386390
constexpr int AnswerPlus = 42+5;
@@ -409,8 +413,8 @@ SCENARIO ( "Fixed size ring_span, testing wrap around with int type",
409413
REQUIRE (wq.empty());
410414
}
411415

412-
SCENARIO ( "Threaded wait queue, deque int",
413-
"[wait_queue] [threaded] [int] [deque]" ) {
416+
TEST_CASE ( "Threaded wait queue, deque int",
417+
"[wait_queue] [threaded] [int] [deque]" ) {
414418

415419
chops::wait_queue<set_elem<int> > wq;
416420

@@ -426,8 +430,8 @@ SCENARIO ( "Threaded wait queue, deque int",
426430
}
427431
}
428432

429-
SCENARIO ( "Threaded wait queue, deque string",
430-
"[wait_queue] [threaded] [string] [deque]" ) {
433+
TEST_CASE ( "Threaded wait queue, deque string",
434+
"[wait_queue] [threaded] [string] [deque]" ) {
431435

432436
chops::wait_queue<set_elem<std::string> > wq;
433437

0 commit comments

Comments
 (0)
0