Articles & Books

In an Atomic World -- Lucian Radu Teodorescu

logo.pngAtomics form a relatively low level, but fundamental part of sharing data across threads. Lucian Radu Teodorescu reminds us what atomics are and how and when to use them.

In an Atomic World

by Lucian Radu Teodorescu

From the article:

We often discuss mutexes as the basic building blocks of concurrency. However, there are more fundamental concepts upon which concurrent programs and synchronization primitives are constructed. The C++ language defines a memory model, which describes how programs behave when multiple threads are involved. Additionally, C++ introduces atomic operations that serve as foundation for working with data across threads, ensuring both safety and performance. The goal of C++ atomics is to closely align with the hardware and eliminate the need for lower-level operations that must work across threads.

The topic of atomics is often overlooked, and the prevailing advice is to avoid them. While this advice is generally sound, there are occasions when we need to use atomics to fully leverage the language’s capabilities. This article aims to give atomics the attention they deserve, as they have yet to be featured in an Overload article.

The subject of atomics is extensive. For a comprehensive exploration, readers are encouraged to consult books by Anthony Williams [Williams19] and Mara Bos [Bos23]. While the Bos book primarily focuses on Rust, there is still much to be learned about atomics for C++ programmers. The reader can also consider cppreference.com for a quick reference to the atomics library [cppreference-1] In this article, we will examine various memory ordering models and illustrate their usage through simplified practical examples.

C++ programmer's guide to undefined behavior: part 6 of 11

Your attention is invited to the sixth part of an e-book on undefined behavior. This is not a textbook, as it's intended for those who are already familiar with C++ programming. It's a kind of C++ programmer's guide to undefined behavior and to its most secret and exotic corners. The book was written by Dmitry Sviridkin and edited by Andrey Karpov.

C++ programmer's guide to undefined behavior: part 6 of 11

by Dmitry Sviridkin

From the article:

I/O streams have other flags that represent the state of the stream: whether there were errors, whether we reached the end. Many people know that you can check whether an operation was successful by putting a stream object into a conditional statement (or any context where it is converted to bool). Those unfamiliar with it might use the while (!iss.eof()) check that will one day lead to the infinite loop issue. This happens when the file isn't finished, but can no longer be read—say, if the file is on a network drive, and the network has gone down. Well, that's a story for another time. Let's focus on the correct way to check readability.

Temporarily Dropping a Lock: The Anti-lock Pattern -- Raymond Chen

RaymondChen_5in-150x150.jpgIn C++, it's common to use RAII types like std::lock_guard to manage synchronization primitives, ensuring a lock is acquired at object creation and released at destruction. However, a less common but useful pattern is the "anti-lock," which temporarily releases a lock and reacquires it later, useful in scenarios where you need to drop a lock while performing certain operations, like calling out to other components to avoid deadlocks.

Temporarily Dropping a Lock: The Anti-lock Pattern

by Raymond Chen

From the article:

There is a common pattern in C++ of using an RAII type to manage a synchronization primitive. There are different versions of this, but they all have the same basic pattern:

  • Creating the object from a synchronization object: Locks the synchronization object.
  • Destructing the object: Unlocks the synchronization object.

These types go by various names, like std::lock_guardstd::unique_lock, or std::coped_lock, and specific libraries may have versions for their own types, such as C++/WinRT’s winrt::slim_lock_guard and WIL’s wil::rwlock_release_exclusive_scope_exit (which you thankfully never actually write out; just use auto).

One thing that is missing from most standard libraries, however, is the anti-lock.

The idea of the anti-lock is that it counteracts an active lock.

Reflection-based JSON in C++ at Gigabytes per Second -- Daniel Lemire

portrait2018.jpgJSON is a widely-used format for data exchange, but in C++, handling JSON efficiently can be challenging. While current solutions like simdjson offer high-speed processing, upcoming features in C++26, such as powerful reflection, promise to simplify and accelerate the serialization and deserialization of JSON, making it both faster and more convenient for developers.

Reflection-based JSON in C++ at Gigabytes per Second

by Daniel Lemire

From the article:

JSON (JavaScript Object Notation) is a popular format for storing and transmitting data. It uses human-readable text to represent structured data in the form of attribute–value pairs and arrays. E.g., {"age":5, "name":"Daniel", toys:["wooden dog", "little car"]}. Ingesting and producing JSON documents can be a performance bottleneck. Thankfully, a few JSON parsers such as simdjson have shown that we can process JSON at high speeds, reaching gigabytes per second.

However, producing and ingesting JSON data can remain a chore in C++. The programmer often needs to address potential errors such as unexpected content.

Yet, often, the programmer only needs to map the content to and from a native C/C++ data structure.

User-Defined Formatting in std::format – Part 3 -- Spencer Collyer

logo.pngWe’ve seen formatting for simple classes and more complicated types. Spencer Collyer finishes his series by showing us how to apply specific formatting to existing classes.

User-Defined Formatting in std::format – Part 3

by Spencer Collyer

From the article:

In the previous articles in this series [Collyer24a], [Collyer24b] I showed how to write classes to format user-defined classes and container classes using the std::format library.

In this article I will show you how to create format wrappers, special purpose classes that allow you to apply specific formatting to objects of existing classes.

A note on the code listings: The code listings in this article have lines labelled with comments like // 1. Where these lines are referred to in the text of this article it will be as ‘line 1’ for instance, rather than ‘the line labelled // 1’.

Format wrappers

I’d now like to introduce a type of class which I call ‘format wrappers’. A format wrapper is a very simple class which wraps a value of another type. They exist purely so that we can define a formatter for the format wrapper. The idea is that the formatter will then output the wrapped value using a specific set of formatting rules. Hopefully this will become clearer when we discuss the Quoted format wrapper later.

A format wrapper is a very simple class, which normally consists of just a constructor taking an object of the wrapped type, and a public member variable holding a copy or reference to that value. They are intended to be used in the argument list of one of std::format’s formatting functions, purely as a way to select the correct formatter.

C++ programmer's guide to undefined behavior: part 5 of 11

Your attention is invited to the fifth part of an e-book on undefined behavior. This is not a textbook, as it's intended for those who are already familiar with C++ programming. It's a kind of C++ programmer's guide to undefined behavior and to its most secret and exotic corners. The book was written by Dmitry Sviridkin and edited by Andrey Karpov.

C++ programmer's guide to undefined behavior: part 5 of 11

by Dmitry Sviridkin

From the article:

However, all this fuss with removing and adding const anywhere in the code eliminates this set of optimizations. So, a repeated access by a constant reference to the same data member or member function doesn't need to be cached at all. Note. It's worth mentioning that programmers have unrealistic expectations about the compiler optimizing code when they add more const. Here's a good note on the topic: "Why const Doesn't Make C Code Faster".

Qt and Trivial Relocation (Part 5) -- Giuseppe D'Angelo

In previous posts, we've explored relocation, trivial relocation, and their use in optimizing data structures like vector-like containers. We've also examined how trivial relocation relates to move assignments, enabling further optimization of operations like swaps and algorithms such as std::sort and std::rotate.

Qt and Trivial Relocation (Part 5)

by Giuseppe D'Angelo

From the article:

Is trivial relocation allowed in Standard C++?

That’s probably a question we should have asked as soon as we started this journey. Of course, the answer is no, it is not allowed!

Remember how trivial relocation works: we use memcpy a source object(‘s representation) into some storage, and claim that operation realizes the equivalent of move-constructing the source into that storage, plus destroying the source.

The problem is that one can’t just put data into some storage and pretend that an object exists in there. This is only allowed for a specific set of types, such as trivially copyable types. (Note that if a type is trivially copyable, then Qt automatically considers it trivially relocatable.)

However, as we have discussed, many interesting types (QStringstd::vectorstd::unique_ptr, …) are not trivially copyable, but they would still benefit from trivial relocatability.

Understanding the inner workings of C++ smart pointers -- Andreas Fertig

me.pngPreviously, we explored a basic implementation of unique_ptr in "Understanding the Inner Workings of C++ Smart Pointers - The unique_ptr." Now, let's enhance that model by incorporating a custom deleter, similar to what the Standard Library provides.

Understanding the Inner Workings of C++ Smart Pointers - The Unique_ptr with Custom Deleter

by Andreas Fertig

From the article:

Let's first establish why somebody would want a custom deleter.

One example is that the object was allocated via a local heap, and such must be returned by calling the corresponding deallocation function.

Another example is fopen. This function returns a FILE* object that you are supposed to delete by calling fclose. A classic job for a unique pointer. But you cannot call delete on the FILE pointer.

Here are two examples of using a unique_ptr with a custom deleter.

void MyDeleter(Object* ptr)
{
  delete ptr;
}

unique_ptr<Object> alfred{new Object{}};
static_assert(sizeof(alfred) == sizeof(void*));

unique_ptr<Object, decltype(MyDeleter)> robin{new Object{}, &MyDeleter};
static_assert(sizeof(robin) == sizeof(void*) * 2);
 
Oh yes, the first object, alfred, doesn't provide a custom deleter. Only robin does. Behind the curtains, both do. Let's look at a modified unique_ptr implementation that handles the custom deleter case.

Opaque Pointer Pattern in C++ -- Daniel Sieger

sieger-opaquepattern.pngAfter reading this article, you should understand the basics of the opaque pointer pattern and how you can implement it using std::unique_ptr. I also gave some hints on when it is appropriate to use it and when maybe not.

Opaque Pointer Pattern in C++

by Daniel Sieger

From the article:

The basic problem is that C++ class declarations expose private details of the class. Private member functions and data members need to be declared in the header. Here’s an example for illustration:

// Point.h


class Point
{
public:
  Point(float x, float y);
  float x();
  float y();

private:
  float x_;
  float y_;
};

While users of this class don’t have direct access to the private data members x_ and y_, there is still a dependency: If you change the private implementation details of Point, all other compilation units that include Point.h know about the change and need to be re-compiled.

This only gets worse for more complex dependency chains, e.g., when there are dependencies to other classes internal to the module that need to be included. To a certain degree, this can be dealt with by using forward declarations. However, at the end of the day there is an information leak: Private implementation details are leaking to clients. This goes directly against the idea of information hiding.

The opaque pointer pattern helps to deal with this problem.

Enum Class Improvements for C++17, C++20 and C++23 -- Bartlomiej Filipek

Filipek-enumclass.pngThe evolution of the C++ language continues to bring powerful features that enhance code safety, readability, and maintainability. Among these improvements, we got changes and additions to enum class functionalities across C++17, C++20, and C++23. In this blog post, we’ll explore these advancements, focusing on initialization improvements in C++17, the introduction of the using enum keyword in C++20, and the std::to_underlying utility in C++23.

Enum Class Improvements for C++17, C++20 and C++23

by Bartlomiej Filipek

From the article:

Before diving into the enhancements, let’s briefly recap what enum class is. An enum class (scoped enumeration) provides a type-safe way of defining a set of named constants. Unlike traditional (unscoped) enums, enum class does not implicitly convert to integers or other types, preventing accidental misuse. Here’s a basic example:

#include <iostream>

enum class Color {
    Red,
    Green,
    Blue
};

int main() {   
    Color color = Color::Red;

    if (color == Color::Red)
        std::cout << "The color is red.\n";

    color = Color::Blue;

    if (color == Color::Blue)
        std::cout << "The color is blue.\n";

    // std::cout << color; // error, no matching << operator
    // int i = color;      // error: cannot convert
}