Professional C__ - Marc Gregoire [458]
A common technique is to use a ring buffer to store a fixed number of messages, or messages in a fixed amount of memory. When the buffer fills up, it starts writing messages at the beginning of the buffer again, overwriting the older messages. This cycle can repeat indefinitely. The following sections provide an implementation of a ring buffer and show you how you can use it in your programs.
Ring Buffer Interface
The following RingBuffer class provides a simple debug ring buffer. The client specifies the number of entries in the constructor and adds messages with the addEntry() method. Once the number of entries exceeds the number allowed, new entries overwrite the oldest entries in the buffer.
The buffer also provides the option to print entries as they are added to the buffer. The client can specify an output stream in the constructor, and can reset it with the setOutput() method.
Finally, the buffer supports streaming to an output stream.
class RingBuffer
{
public:
// Constructs a ring buffer with space for numEntries.
// Entries are written to *ostr as they are queued.
RingBuffer(size_t numEntries = kDefaultNumEntries,
ostream* ostr = nullptr);
virtual ~RingBuffer();
// Adds the string to the ring buffer, possibly overwriting the
// oldest string in the buffer (if the buffer is full).
void addEntry(const string& entry);
// Streams the buffer entries, separated by newlines, to ostr.
friend ostream& operator<<(ostream& ostr, const RingBuffer& rb);
// Sets the output stream to which entries are streamed as they are added.
// Returns the old output stream.
ostream* setOutput(ostream* newOstr);
protected:
vector ostream* mOstr; size_t mNumEntries, mNext; bool mWrapped; static const size_t kDefaultNumEntries = 500; }; Code snippet from RingBuffer\RingBuffer.h Ring Buffer Implementation This implementation of the ring buffer stores a fixed number of strings. Each of these strings must be copied into the ring buffer, requiring dynamic allocation of memory. This approach certainly is not the most efficient solution. Other possibilities would be to provide a fixed number of bytes of memory for the buffer. However, that requires mucking with low-level C-strings and memory management, which you should avoid whenever possible. This implementation should be sufficient unless you’re writing a high-performance application. This ring buffer uses the STL vector to store the string entries. The use of the STL is straightforward except for the implementation of operator<< for the RingBuffer, which employs some fancy iterators. Consult Chapters 11 through 17 for details on the STL. // Initialize the vector to hold exactly numEntries. The vector size // does not need to change during the lifetime of the object. // Initialize the other members. RingBuffer::RingBuffer(size_t numEntries, ostream* ostr) : mEntries(numEntries), mOstr(ostr), mNumEntries(numEntries), mNext(0), mWrapped(false) { } RingBuffer::~RingBuffer() { } // The addEntry algorithm is pretty simple: add the entry to the next // free spot, then reset mNext to indicate the free spot after // that. If mNext reaches the end of the vector, it starts over at 0. // // The buffer needs to know if the buffer has wrapped or not so // that it knows whether to print the entries past mNext in operator<< void RingBuffer::addEntry(const string& entry) { // Add the entry to the next free spot and increment // mNext to point to the free spot after that. mEntries[mNext++] = entry; // Check if we've reached the end of the buffer. If so, we need to wrap. if (mNext >= mNumEntries) { mNext = 0; mWrapped = true; } // If there is a valid ostream, write this entry to it. if (mOstr != nullptr) { *mOstr << entry << endl; } } ostream* RingBuffer::setOutput(ostream* newOstr) { ostream* ret = mOstr; mOstr = newOstr; return ret; } // operator<< uses an ostream_iterator to "copy" entries directly