Skip to main content

C++

Ring Buffer Series Part 10 — Exception Safety: naming the guarantees & fixing our bookkeeping

buffer10
At the end of part 9 we realized that our buffer still had one issue. Our copy constructor placement-news each element one at a time; if the third of five copies throws, the first two are already built, but because the constructor itself failed, the object’s destructor will never run, and those two elements leak. The same fragility lives in push_back and emplace_back: we increment count_ before we construct the element, so a throwing constructor leaves the buffer believing it holds an object that was never built, and our destructor will later walk into that slot and try to destroy it. OneThrow

Ring Buffer Series Part 9 — The Rule of Five - deep copies & noexcept

class
In part 6 we modified the buffer to work by storing bytes, this means that the compiler treats those bytes as raw data and not as the objects that data represents. This means that we also had to do the following:

    RingBuffer(const RingBuffer&)            = delete;
    RingBuffer(RingBuffer&&)                 = delete;
    RingBuffer& operator=(const RingBuffer&) = delete;
    RingBuffer& operator=(RingBuffer&&)      = delete;

Which effectively makes memberwise copy/move meaningless. Remember, we are using a raw byte buffer and placement new. Because of this we are managing object lifetimes manually. Usually a default compiler-generated copy would simply perform bitwise copy of the std::byte array and this creates a few problems. Bitwise copying does not call the actual copy constructors of the T objects stored in the buffer, so if T is a complex type such as std::string, copying its bits without calling the constructor would result in a shallow copy leading to a corrupted state or dangling pointers, if this isn’t bad enough, there is also wasted effort as the compiler would copy the entire buffer, including the empty slots that have not been constructed yet. Let us explore some concepts in more detail.

Ring Buffer Series Part 8 — Building in Place - emplace_back, variadic templates & perfect forwarding

class
We closed part 7 with some unfinished business, recall that push_back(T&&) still builds a temporary object and then moves it. Consider the output of our last example:

Pushing Temporary:
   [Ctro] Created 1
   [MOVE] Stealing data from 1
   [Dtor] Destroyed 1

This works, but as a wise man once said; “we are still not happy”. What if we could get it to just [Ctro] Created 1. No temporary, no move?

The Goal: Construct From Arguments, Not From a Finished Object

Our push_back() takes a fully constructed object T. Unlike push_back(), emplace_back() takes a variadic number of arguments which are perfectly forwarded to the constructor of the element type T. This allows the object to be built directly in place within the container’s memory. Before we can move on to the implementation, there are some concepts that need to be explained.

Ring Buffer Series Part 7 — Move Semantics - Don't copy, steal

class In part 6 we closed by pointing towards move semantics. In this post we will tackle the inefficiency of unnecessary copying, which happens when we create a temporary object, copy it into a container and then destroy the original. We acknowledged that this process is wasted work and in our RingBuffer it happens because our push_back implementation only supports copying, even when the source is a temporary object. Consider the following:

Ring Buffer Series Part 6 — Storage and Lifetimes

class We closed part 5 by promising to tackle move semantics, but there is foundational work that needs to be done before that can happen. Move semantics will come in the future, but in this post we need to make sure our storage is capable of supporting it and we will have to rebuild one of the components we made in part 2, so buckle up because this will be a long one.

Ring Buffer Series Part 5 — const_iterator

class Previously we expanded our RingBuffer by implementing its own iterator class along with iterator traits. This allowed us to make it work with range-based for loops and most algorithms in the C++ Standard Library, but it still has a limitation.

void print_contents(const RingBuffer<int, 8>& buf)
{
    for(auto x : buf)
    {
        std::cout << x << ' ';
    }
}

The compiler will not allow it. Our begin() and end() are not const qualified, which means they can’t be called on a const object. In this episode we will fix this by introducing const_iterator, an iterator that promises not to modify the elements it visits.

Ring Buffer Series Part 4 — Iterators: Walking the Ring

class In Part 3 we expanded on the original buffer and it now supports any type and any size. Our RingBuffer almost behaves like any of the STL types, such as std::vector and I say almost. Consider the following code:

int main()
{
    RingBuffer<int, 10> my_buffer;
    for (int i = 0; i < 10; ++i)
    {
        my_buffer.push_back(i);
    }

    for (auto x : my_buffer)       // This line produces an error.
    {
        std::cout << x << ' ';
    }
}

This code should work, but if we actually try to run it we will get a compiler error.

Ring Buffer Series Part 3 — Templates: One Buffer, Any Type, Any Size

class In Part 2 we built a buffer that works, however it is stuck with unsigned int and size 8, but what if we wanted to use it with std::string or custom types like game events, or only store the last 4 frames? Do we write a buffer per class? We could, but that would be a nightmare, wouldn’t it?

Meet Templates

In C++, templates are a powerful mechanism for generating code. A template is a blueprint that the compiler uses to generate actual code. Think about it this way, a class is a blueprint for a thing (a type of thing), templates are blueprints for classes.

Ring Buffer Series Part 2 - Implementation

class In Part 1 we covered the fundamental building blocks: classes, functions, constructors and how to organize code into header and implementation files. We created a simple Person class to illustrate these concepts and now it is time to apply what we learned to our main project: The RingBuffer. Remember, our ring buffer needs to do a few key things: store elements, track where to add the next one, and know when it’s full. Let’s begin by creating a class.

Ring Buffer Series Part 1 - Classes and Functions - The Building Blocks

class Hello! If you recall from the previous series, we built a program that handled text input, stored numbers into a vector and performed calculations. However there were some concepts in there that we used, but that I didn’t really explain. I am referring to iterators. In this new series I aim to go over what they are, how they work and how to use them, so get ready because it will be a bumpy ride and as usual, we will have to build our way there because fundamentals are always important.

Input Basics In C++ Part 3 - Multiple Inputs and Calculations

Input Validation In Part 2 we covered some basic principles about standard input in C++, we talked about the input buffer stream, the iostate and some mechanisms that the C++ programming language provides us with to get input from users. We ended with a program that takes input from users and stores them, but that had the fatal flaw of only being able to store a single number, which really doesn’t make for a good calculator, does it? So here are a few changes.

Input Basics In C++ Part 2 - Streams, Flags and Validation

Input Validation

In Part 1 of this series I covered some C++ fundamentals by building a small unit converter. While the program worked, it had a critical weakness: it assumed the “happy path,” where users never make mistakes. In the real world, things are messy, and as programmers, we have to anticipate how our applications might fail. We can’t predict everything, but we can certainly guard against the most common issues.

Input Basics In C++ Part 1 - Introduction

Input Basics In your programming journey, have you ever reached a point where you feel overwhelmed with everything a language can do, leaving you unsure of what to learn first or which features are truly important? I find myself in that exact situation. With each new C++ standard, I feel like I’m falling behind. Here we are in 2025, and I’m still trying to master move semantics and how to best use smart pointers. Learning a language with a rich history like C++ can be grueling. I’ve noticed I often get mesmerized by what’s new, shiny, and exciting, causing me to neglect the fundamentals. That’s what this post is about: practicing some of the fundamentals of the language and its standard library. In this new series, I will explore the basics of the <iostream> library.