|
Boost Users : |
Subject: Re: [Boost-users] [Boost.CircularBuffer] begin()/end() arithmetic not working out quite right
From: David Baird (dhbaird_at_[hidden])
Date: 2009-07-25 16:25:46
Hmmm, well, I think I understand what you are saying. But it seems
counter-intuitive to me that end() will always remain constant and
that begin() will always move back. Even this graphic here seems to
indicate that end() moves:
http://www.boost.org/doc/libs/1_39_0/libs/circular_buffer/doc/circular_buffer.png
(from http://www.boost.org/doc/libs/1_39_0/libs/circular_buffer/doc/circular_buffer.html)
Unfortunately, that makes it hard to use circular_buffer in some of
our applications. But I might be mistaken...
It seems that circular_buffer works great for this scenario:
for(i = buf.begin(); i= buf.end(); ++i) { ... }
But my application is a little bit different from that. Essentially,
we have a serial data stream that is being observed by several
processes. There is one producer and many consumers. Each consumer
has its own iterator. Our code is akin to this:
// Make SIZE sufficiently large to obviate overflows:
const int SIZE = 1024;
char buf[SIZE];
int tail = 0;
int i1 = 0;
int i2 = 0;
// Producer process:
while (true) { buf[tail++ % SIZE] = c; ... }
// Consumer process 1:
while (true) {
while (tail != i1) { c = buf[i1++ % SIZE]; ... }
}
// Consumer process 2:
while (true) {
while (tail != i2) { c = buf[i2++ % SIZE]; ... }
}
At first glance, this seemed like a perfect application for
Boost::circular_buffer. But when I delved in further, I just couldn't
get the iterators to work out right in circular_buffer. I thought
that something like this would be appropriate, but yet this does not
work:
const int SIZE = 1024;
typedef circular_buffer<char> buf_type;
buf_type buf(size);
buf_type::iterator i1 = buf.begin();
buf_type::iterator i2 = buf.begin();
// Producer process:
while (true) { buf.push_back(c); ... }
// Consumer process 1:
while (true) {
// i1 will always equal buf.end(), despite .push_back() being called
// ...thus, this fails:
while (buf.end() != i1) { c = *i1++; ... }
}
// Consumer process 2:
while (true) {
while (buf.end() != i2) { c = *i2++; ... }
}
Do you think I am exceeding the intended application scope for
circular_buffer here?
I don't know where to find the specifications for the semantics of
begin()/end(). Can you direct me towards a link? i've tried
searching for things like "C++ container concepts" but that didn't
yield any precise details about how begin()/end() should be affected
by operations like push_back(). If I had to make a wild guess, I
would guess that it actually isn't specified somewhere :-/
Thanks,
David
On Wed, Jul 22, 2009 at 3:31 PM, Jan Gaspar<jano_gaspar_at_[hidden]> wrote:
> Ah ... one more thing ... I made a mistake in my previous reply.
>
>> you just cannot rely on this. I don't think standard says anything about begin() moving forward after push_back(). Correct me if I'm wrong.
>
> should be read as
>
> "you just cannot rely on this. I don't think standard says anything about end() moving forward after push_back(). Correct me if I'm wrong."
>
> Sorry about the confusion.
>
> Jan
>
>
>
> ----- Original Message ----
> From: David Baird <dhbaird_at_[hidden]>
> To: Jan Gaspar <jano_gaspar_at_[hidden]>
> Cc: boost-users_at_[hidden]
> Sent: Wednesday, 22 July, 2009 22:30:13
> Subject: Re: [Boost.CircularBuffer] begin()/end() arithmetic not working out quite right
>
> Hi Jan,
>
> Thanks for your reply and sorry for taking so long to respond.
>
> I am a bit confused by your response. I never did expect begin() to
> move forward after push_back(). In my original post, what I said is
> that I expect begin() will remain constant and that **end() will move
> forward** each time push_back() is called.
>
> My main question is this: why does end() remain constant when
> push_back() is called on a circular_buffer? In other words, why does
> this assertion fail?:
>
> circular_buffer<int> buf(8);
> circular_buffer<int>::iterator a;
> circular_buffer<int>::iterator b;
> a = buf.end();
> buf.push_back(1);
> b = buf.end();
> assert(a != b); // fails
>
> If I use an STL vector instead of circular_buffer, I get exactly the
> results I expect (as along as I call .reserve() on it prior, so that
> iterators are not invalidated by a realloc).
>
> -David
>
> On Thu, Jun 11, 2009 at 1:58 AM, Jan Gaspar<jano_gaspar_at_[hidden]> wrote:
>>
>> Hi David,
>>
>> you just cannot rely on this. I don't think standard says anything about begin() moving forward after push_back(). Correct me if I'm wrong.
>>
>> The way how it is implemented is that if the circular_buffer is empty begin() returns the same iterator as end().
>>
>> There is also a note about iterator invalidation for push_back():
>> "Does not invalidate any iterators with the exception of iterators pointing to the overwritten element."
>>
>> It means iterator 'a' in your first example will point to the same element as it was before calling push_back() - which is end(). This explains the behaviour you are describing.
>>
>> Regards,
>>
>> Jan
>>
>>
>>
>>
>> ----- Original Message ----
>> From: David Baird <dhbaird_at_[hidden]>
>> To: Jan Gaspar <jano_gaspar_at_[hidden]>; boost-users_at_[hidden]
>> Sent: Thursday, 11 June, 2009 1:01:54
>> Subject: [Boost.CircularBuffer] begin()/end() arithmetic not working out quite right
>>
>> Hi,
>>
>> Firstly, thanks for the work on a circular buffer. This is very
>> useful since many of my applications require it. I am having a
>> problem though...
>>
>> I am using Boost 1.38.0. When I call push_back(), the iterator math
>> makes it appear that begin() moves backwards and end() remains
>> constant. (Based on other STL libraries, I expect that begin() will
>> remain constant and end() will keep advancing as I call push_back()).
>>
>> In other words, this assertion fails (but I expect it to succeed):
>>
>> circular_buffer<int> buf(8);
>> circular_buffer<int>::iterator a;
>> circular_buffer<int>::iterator b;
>> a = buf.begin();
>> buf.push_back(1);
>> b = buf.begin();
>> assert(a == b); // Fails!!
>>
>> Also, this code fails too (but I expect it to succeed):
>>
>> circular_buffer<int> buf(8);
>> circular_buffer<int>::iterator a;
>> circular_buffer<int>::iterator b;
>> a = buf.end();
>> buf.push_back(1);
>> b = buf.end();
>> assert(a != b); // Also fails!
>>
>> Below is a full example that you can compile and try out:
>>
>> #include <boost/circular_buffer.hpp>
>> #include <stdio.h>
>>
>> int
>> main ()
>> {
>> typedef boost::circular_buffer<int> buf_type;
>> // Instead of "end" moving ahead, "begin" is moving backwards when //
>> // using "push_back".
>> {
>> buf_type buf1(1024);
>> buf_type::iterator a;
>> buf_type::iterator b;
>> a = buf1.end();
>> buf1.push_back(1);
>> buf1.push_back(2);
>> b = buf1.end();
>> printf ("%d\n", a == b);
>> // >>>
>> // got: 1
>> // expected: 0
>> printf ("%d, %d\n", a-buf1.begin(), b-buf1.begin());
>> // >>>
>> // got: 2, 2
>> // expected: 0, 2
>> }
>> {
>> buf_type buf2(1024);
>> buf_type::iterator c;
>> buf_type::iterator d;
>> c = buf2.begin();
>> buf2.push_back(1);
>> buf2.push_back(2);
>> d = buf2.begin();
>> printf ("%d\n", c == d);
>> // >>>
>> // got: 0
>> // expected: 1
>> // (i.e. "begin()" is not still pointing to first item!!
>> // This is incorrect, isn't it?)
>> printf ("%d, %d\n", buf2.end()-c, buf2.end()-d);
>> // >>>
>> // got: 0, 2
>> // expected: 2, 2
>> }
>> return 0;
>> }
>>
>>
>> Thanks,
>> David
>>
>>
>>
>>
>>
>
>
>
>
>
Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net