November 2024

Implementing Trivial Relocation in Library -- Barry Revzin

TPWGDVRj_400x400.jpgOne of the reasons that I’m excited for Reflection in C++ is that it can permit you to implement, as a library, many things that previously required language features. In this post, I’m going to walk through implementing P2786R8 (“Trivial Relocatability For C++26”).

Implementing Trivial Relocation in Library

by Barry Revzin

From the article:

The goal here is not to say that the design is right or wrong (although the syntax certainly is suspect), but rather to show the kinds of things that reflection can solve.

We’ll just go straight to the wording and translate it into code as we go:

Trivially Relocatable Types

Scalar types, trivially relocatable class types (11.2 [class.prop]), arrays of such types, and cv-qualified versions of these types are collectively called trivially relocatable types.

This sure sounds like a type trait! Except in the world of reflection, those are just functions. How would we implement such a thing? We could start by doing this:

consteval auto is_trivially_relocatable(std::meta::info type)
    -> bool
{
    type = type_remove_cv(type);

    return type_is_scalar(type)
        or (type_is_array(type)
            and is_trivially_relocatable(
                type_remove_all_extents(type)
            ))
        or is_trivially_relocatable_class_type(type);
}

This is a fairly literal translation, where is_trivially_relocatable_class_type is something to be written shortly. But one interesting thing about the type_remove_all_extents type trait (i.e. std::remove_all_extents) is that it also works for non-array types, just returning back the same type. 

The Publish Pattern -- Lucian Radu Teodorescu

logo.pngHow do you minimise locking between producers and consumers? Lucian Radu Teodorescu describes a common, but currently undocumented, design pattern. Design patterns can help us reason about code. They are like algorithms that are vaguely defined in the code. Once we recognise a pattern, we can easily draw conclusions about the behaviour of the code without looking at all the parts. Patterns also help us when designing software; they are known solutions to common problems.

In this article, we describe a concurrency pattern that can’t be found directly in any listing of concurrency patterns, and yet, it appears (in one way or another) in many codebases. It is useful when we have producers and consumers that run continuously, and we want to minimise the locking between them.

The Publish Pattern

by Lucian Radu Teodorescu

From the article:

Let’s say we have an open-world game. As the player walks through the world, we load the data corresponding to the regions around the player. We have two types of workloads in our scenario: one for loading the data and another for displaying the loaded data. For the sake of our discussion, let’s say that each of these two activities is bound to a thread.

The problem we are trying to solve is how to structure the passing of data (let’s call this document) from the loading thread (producer) to the rendering thread (consumer). This is analogous to the classical producer-consumer problem [Wikipedia-1], but it has some interesting twists. Let’s try to outline the requirements of the problem:

  • (R1) The producer is constantly producing new versions of the document.
  • (R2) The consumer is constantly consuming document data.
  • (R3) The consumer will use the latest version of the document.

Please note that we are discussing multiple versions of the document. In our example, the loading thread will produce different documents depending on the position of the player, and the rendering thread will display the latest document, corresponding to the player’s most recent position.

 

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

Your attention is invited to the eighth 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 8 of 11

by Dmitry Sviridkin

From the article:

To avoid issues, use conditional noexcept always and everywhere and carefully check every function you use. Or don't use noexcept at all. In the second case, however, it's worth remembering that both move operations and swap should be marked as noexcept (and really be noexcept!) to effectively work with standard containers. Don't forget to write negative tests. You may miss a false noexcept and get std::terminate in the release build without them.