1

Apologies if my question does not contain all relevant info. Please comment and I will amend accordingly.

I use CLion on Win7 with MinGW and gcc


I have been experimenting with circular buffers and came across boost::circular_buffer, but for the size of my project I want to use circular buffer by Pete Goodlife, which seems like a solid implementation in just one .hpp.

Note: I am aware of how to reduce boost dependecies thanks to Boost dependencies and bcp.

However, the following example with Pete's implementation does not behave as expected, i.e. the result to std::adjacent_difference(cbuf.begin(),cbuf.end(),df.begin()); comes out empty. I would like to understand why and possibly correct its behaviour.

Follows a MWE:

#include "circular.h"
#include <iostream>
#include <algorithm>

typedef circular_buffer<int> cbuf_type;

void print_cbuf_contents(cbuf_type &cbuf){
  std::cout << "Printing cbuf size("
            <<cbuf.size()<<"/"<<cbuf.capacity()<<") contents...\n";
  for (size_t n = 0; n < cbuf.size(); ++n)
    std::cout << "  " << n << ": " << cbuf[n] << "\n";

  if (!cbuf.empty())  {
    std::cout << "  front()=" << cbuf.front()
              << ", back()=" << cbuf.back() << "\n";
  }  else  {
    std::cout << "  empty\n";
  }
}

int main()
{
  cbuf_type cbuf(5);
  for (int n = 0; n < 3; ++n) cbuf.push_back(n);
  print_cbuf_contents(cbuf);

  cbuf_type df(5);
  std::adjacent_difference(cbuf.begin(),cbuf.end(),df.begin());
  print_cbuf_contents(df);
}

Which prints the following:

Printing cbuf size(3/5) contents...
  0: 0
  1: 1
  2: 2
  front()=0, back()=2
Printing cbuf size(0/5) contents...
  empty

Unfortunately, being new to c++ I can’t figure out why the df.begin() iterator is not dereferenced as an lvalue.

I supsect the culprit is (or don't completely uderstand) the member call of the circular_buffer_iterator on line 72 in Pete's circular.h:

elem_type &operator*() { return (*buf_)[pos_]; }

Any help is very much appreciated.

Oleg
  • 10,406
  • 3
  • 29
  • 57

2 Answers2

2

The iterator you pass as the output iterator is dereferenced and treated as an lvalue, and most probably the data you expect is actually stored in the circular buffer's buffer.

The problem is, that apart from the actual storage buffer, most containers also contain some internal book-keeping state that has to be maintained. (for instance: how many elements is in the buffer, how much frees space is left etc).

Dereferencing and incrementing the container doesn't update the internal state, so the container does not "know" that new data has been added.

Consider the following code:

std::vector<int> v;
v.reserve(3);

auto i = v.begin();
*(i++) = 1; // this simply writes to memory
*(i++) = 2; // but doesn't update the internal
*(i++) = 3; // state of the vector
assert(v.size() == 0); // so the vector still "thinks" it's empty

Using push_back would work as expected:

std::vector<int> v;
v.reserve(3);

v.push_back(1); // adds to the storage AND updates internal state
v.push_back(2);
v.push_back(3);

assert(v.size() == 3); // so the vector "knows" it has 3 elements

In your case, you should use std::back_inserter, an iterator that calls "push_back" on a container every time it is dereferenced:

std::adjacent_difference(
    cbuf.begin(), cbuf.end(),
    std::back_inserter(df));
maciek gajewski
  • 903
  • 7
  • 5
  • The thing is, Pete's container claims it is STL compliant, and I expect it to behave like this [example](http://en.cppreference.com/w/cpp/algorithm/adjacent_difference#Example) from `adjacent_difference()` on a vector. What adjustment do I need to introduce to trigger state updates? – Oleg Oct 13 '16 at 21:00
  • I think I am starting to understand, after reading a bit more about `back_inserter()` in [this post](http://stackoverflow.com/questions/27215748/what-happens-if-i-use-vectorbegin-instead-of-stdback-insertervector-for), which basically describes my use case. However, I am unsure why the cppreference would include such an example... – Oleg Oct 13 '16 at 22:37
1

std::adjacent_difference writes to the result iterator. In your case, that result iterator points into df, which has a size of 0 and a capacity of 5. Those writes will be into the reserved memory of df, but will not change the size of the container, so size will still be 0, and the first 3 ints of the reserved container space will have your difference. In order to see the results, the container being written into must already have data stored in the slots being written to.

So to see the results you must put data into the circular buffer before the difference, then resize the container to the appropriate size (based in the iterator returned by adjacent_difference.

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56
  • This sounds like double work, populating the `df`, then overwriting them. I dunno if that's what `boost::circular_buffer` was doing, but I did not have this problem. – Oleg Oct 13 '16 at 20:56