Generic Circular Buffer

This is the generic version of the Circular Buffer I have posted before. You can find the non-generic version of the circular buffer here


The header file:
CircularBuffer.hpp

#ifndef MCP_CIRCULAR_BUFFER_GENERIC_H_
#define MCP_CIRCULAR_BUFFER_GENERIC_H_

template<class T>
class CircularBuffer {
public:
  // Creates a buffer with 'slots' slots.
  explicit CircularBuffer(int slots);
  // Destructor.
  ~CircularBuffer();
  // Writes 'value' to the next available slot. It may overwrite
  // values that were not yet read out of the buffer.
  void write(const T & value);
  // Returns the next value available for reading, in the order they
  // were written, and marks slot as read. 
  // If the buffer is empty returns -1.
  void read(T* val);
  // Removes all the elements from the buffer.
  void clear();
  // returns true if the Circular buffer is empty, false otherwise
  bool isEmpty();

private:
  //array of integers
  T* data_;
  // the size of the buffer
  int  num_of_slots_;
  //index to read the next integer from buffer
  int  read_index_;
  //index to write a new integer to buffer
  int  write_index_;
  // Non-copyable, non-assignable.
  CircularBuffer(CircularBuffer&);
  CircularBuffer& operator=(const CircularBuffer&);
};

template <class T>
CircularBuffer<T>::CircularBuffer(int slots) {
  if (slots <= 0) {
    num_of_slots_ = 10; /*pre-assigned value */
  } else {
      num_of_slots_ = slots;
  }
  clear();
}

template <class T>
CircularBuffer<T>::~CircularBuffer() {
  delete[] data_;
}

template <class T>
void CircularBuffer<T>::write(const T & value) {
  data_[write_index_] = value;
  if (read_index_ == -1) {
    //if buffer is empty, set the read index to the
    //current write index. because that will be the first
    //slot to be read later.
    read_index_ = write_index_;
  }
  write_index_ = (write_index_ + 1) % num_of_slots_;
}

template <class T>
void CircularBuffer<T>::read(T* val) {
  if (read_index_ != -1) {  // if buffer is not empty
    *val = data_[read_index_];
    read_index_ = (read_index_ + 1) % num_of_slots_;
    if (read_index_ == write_index_) {
      /*all available data is read, now buffer is empty*/
      read_index_ = -1;
    }
  }
}

template <class T>
void CircularBuffer<T>::clear() {
  read_index_ = -1; /* buffer empty */
  write_index_ = 0; /* first time writing to that buffer*/
  delete[] data_;
  data_ = new T[num_of_slots_]; /* allocate space for buffer */
}

template <class T>
bool CircularBuffer<T>::isEmpty() {
  return read_index_ == -1;
}

#endif  // MCP_CIRCULAR_BUFFER_GENERIC_H_

Sample main file main.cpp

#include <iostream>
#include "include/CircularBuffer.hpp"

using namespace std;

int main()
{
    CircularBuffer<unsigned int> cb(5);
    unsigned int val;
    for (unsigned int i=1;i<=15;i++)
        cb.write(i);
    while(!cb.isEmpty())
    {
        cb.read(&val);
        cout << val << endl;
    }
    return 0;
}

4 comments:

  1. Thanks for the example!
    You should update the code of the destructor. The data_ was created as an array and should therefor be deleted as an array delete[] data_;

    ReplyDelete
  2. As far as I can see the member function 'clear()' is public, therefore is aimed to provide a way for user of your class to delete all the content of buffer. But don't you think the way you implement leak memory, since you are allocating new buffer (on 'clear()') without 'delete' on the previous buffer. I think each time clear gets called you will leak (number_of_slots * sizeof(T)) bytes(except the first time when called within constructor).

    Why don't you create the buffer directly in constructor?
    If you want to use a new buffer for 'clear()', you can still delete the previous buffer (hence returning the memory to OS) and create another one inside 'clear()'?
    Or, much better solution might be usage of std::vector or (in case of C++11/14) std::unique_ptr??

    I am not an expert in C++, so I might be wrong :)

    ReplyDelete
    Replies
    1. You are right. thank you for the suggestion :)

      Delete

Python Line Profilers using Decorator Pattern

You can use any of the following decorators to profile your functions line by line.  The first one(Profiler1) is a decorator as a class and...