How to do it...

In this recipe, we will learn how to create and use a ring buffer on top of a C++ array:

  1. In your working ~/test directory, create a subdirectory called ringbuf.
  2. Use your favorite text editor to create a ringbuf.cpp file in the ringbuf subdirectory.
  3. Define the RingBuffer class, starting from the private data fields:
#include <iostream>

template<class T, size_t N>
class RingBuffer {
private:
T objects[N];
size_t read;
size_t write;
size_t queued;
public:
RingBuffer(): read(0), write(0), queued(0) {}
  1. Now we add a method to push data to the buffer:
    T& push() {
T& current = objects[write];
write = (write + 1) % N;
queued++;
if (queued > N) {
queued = N;
read = write;
}
return current;
}

  1. Next, we add a method to pull data from the buffer:
    const T& pull() {
if (!queued) {
throw std::runtime_error("No data in the ring buffer");
}
T& current = objects[read];
read = (read + 1) % N;
queued--;
return current;
}
  1. Let's add a small method to check whether the buffer contains any data and wrap up the class definition:
bool has_data() {
return queued != 0;
}
};
  1. With RingBuffer defined, we can now add code that uses it. Firstly, let's define the data type we are going to use:
struct Frame {
uint32_t index;
uint8_t data[1024];
};
  1. Secondly, add the main function and define an instance of RingBuffer as its variable, along with code that tries to work with an empty buffer:
int main() {
RingBuffer<Frame, 10> frames;

std::cout << "Frames " << (frames.has_data() ? "" : "do not ")
<< "contain data" << std::endl;
try {
const Frame& frame = frames.pull();
} catch (std::runtime_error e) {
std::cout << "Exception caught: " << e.what() << std::endl;
}
  1. Next, add code that works with five elements in the buffer:
for (size_t i = 0; i < 5; i++) {
Frame& out = frames.push();
out.index = i;
out.data[0] = 'a' + i;
out.data[1] = '\0';
}
std::cout << "Frames " << (frames.has_data() ? "" : "do not ")
<< "contain data" << std::endl;
while (frames.has_data()) {
const Frame& in = frames.pull();
std::cout << "Frame " << in.index << ": " << in.data << std::endl;
}
  1. After that, add similar code that deals with a larger number of elements that can be added:
    for (size_t i = 0; i < 26; i++) {
Frame& out = frames.push();
out.index = i;
out.data[0] = 'a' + i;
out.data[1] = '\0';
}
std::cout << "Frames " << (frames.has_data() ? "" : "do not ")
<< "contain data" << std::endl;
while (frames.has_data()) {
const Frame& in = frames.pull();
std::cout << "Frame " << in.index << ": " << in.data << std::endl;
}
}
  1. Create a file called CMakeLists.txt in the loop subdirectory with the following content:
cmake_minimum_required(VERSION 3.5.1)
project(ringbuf)
add_executable(ringbuf ringbuf.cpp)

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

SET(CMAKE_CXX_FLAGS "--std=c++11")
set(CMAKE_CXX_COMPILER /usr/bin/arm-linux-gnueabi-g++)
  1. Build the application and copy the resulting executable binary to the target system. Use recipes from Chapter 2Setting Up the Environment, to do it.
  2. Switch to the target system terminal. Log in using user credentials, if needed.
  3. Run the binary.