Articles & Books

C++ Compile-Time Programming -- Wu Yongwei

logo.pngProgramming at compile time has been possible in C++ for a long time. Wu Yongwei considers its past, present and future.

Compile-time programming is a key feature of C++. It enables writing high-performance code often unattainable in other languages. This article explores its past, present, and future applications, highlighting the diverse possibilities in C++. We’ll briefly cover template metaprogramming, constexpr, variadic templates, static reflection, and more.

C++ Compile-Time Programming

by Wu Yongwei

From the article:

Compile-time programming is vastly different from run-time programming. The code runs during compilation, but the results can be used at run time.

Some believe compile-time programming is merely a trick, unused in real-world engineering. To them, I ask: do you use the C++ Standard Library? The mainstream implementations rely heavily on various programming techniques, including compile-time programming.

‘I don’t write the standard library’ – this might be a possible response. But consider this: the standard library is just one tool, a standard weapon. Is it enough to use only standard tools? That’s the real question.

The abundance of excellent open-source libraries suggests otherwise. A skilled programmer crafts tools for themselves and their team. If your work feels tedious, perhaps it’s time to build a bulldozer to tackle problems.

Compile-time programming offers a way to build such tools.

if constexpr requires requires { requires } -- Jonathan Müller

thinkcelllogo.pngProbably the two most useful features added to C++20 are requires and requires. They make it so much easier to control overload resolution, and when combined with if constexpr in C++17, they allow basic reflection-based optimizations in templates. While requires requires has gotten a lot of (negative?!) press for controlling overload resolution, its cousin requires { requires } is a bit overlooked.

if constexpr requires requires { requires }

by Jonathan Müller

From the article:

C++20 added requires, a way to enable or disable a function overload based on some compile-time condition. For example, consider a facility for producing debug output of types for error reporting:
thinkcellconst.png

The two overloads with the requires clause are only enabled for integers or floating point types, respectively, and are not considered otherwise. Additionally, overload resolution is smart: It knows that we want the overload with the most specific requirements, and it will only pick the first function when no other overload matches. This is also where concept comes in: A concept is simply a way to name a group of requirements that affects the search for more specific requirements. The technical term for that is subsumption. Because creating named requirements with concept also comes with additional syntax sugar, you don't need requires—so this blog post is gonna ignore concept. In general, if you would use a concept in only one place, it is too early to introduce it.

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

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

by Dmitry Sviridkin

From the article:

And you could write it only in C++. In C, however, this mess has been forbidden (see 6.5.3.2, note 104). Also, you can't use the dereference operator on invalid and null pointers anywhere in C. Meanwhile, C++ has its own, special way of doing things. These weird examples were built in constexpr context (let me remind you that UB is forbidden there, and the compiler checks for it).

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.

Smart Pointers and the Pointer to Implementation Idiom -- Andreas Fertig

me.pngA post I wrote back in 2023 When an empty destructor is required resulted in feedback that I'd like to address in today's post.

Smart Pointers and the Pointer to Implementation Idiom

by Andreas Fertig

From the article:

In the 2023 post, I briefly mentioned PImpl idiom. I did not intend to make it the theme of the post. However, I got various questions about PImpl and smart pointers.

The goal of PImpl is to hide implementation details from clients. Since you can declare a pointer of an unknown class, you can shift the entire implementation of such an anonymous class into a .cpp file. That way, no client can see any details. Another benefit is that changes to that .cpp file result in only minor recompiles. Maintaining the same in a header file would cause all .cpp files, including this header, to recompile. At least the speed-up part is since C++20's modules are no longer necessary. And as long as you don't want to hide classified implementation in the .cpp file modules, it also gives you the ability to mark constructs as private.

 

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

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

by Dmitry Sviridkin

From the article:

In the early '70s, Ken Thompson, Dennis Ritchie, and Brian Kernighan worked on the first versions of C and Unix. They made a decision that resonates with pain, suffering, bugs, and inefficiency today, 50 years later. They decided that developers were to write strings—variable-length data—in a sequence that terminated with a null character. Assembly has it, and C should have it too, if people call it "high-level assembly"! After all, the poor old PDP has limited memory: it's better to have one extra byte per string than 2, 4, or even all 8 bytes (depending on the platform) to store the size... Nah, it's better to have a byte at the end! But even other languages store a size, a reference, or a pointer to the data...

The Definitive Guide to std::expected in C++ -- John Farrier

std::expected is a powerful feature introduced in C++23 that offers a modern, type-safe alternative to traditional error-handling methods.  std::expected allows developers to represent a value or an error in a single object, simplifying the handling of success and failure scenarios in a clean and readable way.

The Definitive Guide to std::expected in C++

John Farrier

From the article:

std::expected is an addition to the C++ standard library that provides a way to return and propagate errors without using exceptions. It represents a value or an error and is similar to the Result type in Rust. This guide covers its use, benefits, and comparison with other error handling techniques, helping developers to write more robust and readable code.

Passing C++ STL Strings vs. String Views at the Windows C API Boundary -- Giovanni Dicanio

A common question that is often asked is whether C++ STL string objects (like std::string/wstring) or string views (e.g. std::wstring_view) should be passed as string parameters at Windows C-interface API boundaries. The following articles tries to shed some light on this subject, answering that question:

Passing C++ STL Strings vs. String Views as Input Parameters at the Windows C API Boundary

by Giovanni Dicanio

From the article:

Passing STL std::[w]string objects at Win32 API boundaries is common for C++ code that calls into Win32 C-interface APIs. When is it safe to pass *string views* instead?

(...) the code will work fine. In fact, the wstring::c_str() method is guaranteed to return a null-terminated C-style string pointer.

On the other hand, if you pass a string view like std::wstring_view in that context, you’ll likely get some subtle bugs! Try experimenting with the above API and something like “Connie is learning C++” and string views!