Articles & Books

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!

 

Code Generation in Rust vs C++26 -- Barry Revzin

TPWGDVRj_400x400.jpgExploring how different languages solve the same problem often reveals interesting contrasts, especially when it comes to implementing powerful features like reflection. While C++26 aims to introduce introspection and code generation via P2996 and P3294, Rust’s approach using its derive macros offers a mature solution for code generation, even without introspection, highlighting different philosophies in language design and their practical applications.

Code Generation in Rust vs C++26

by Barry Revzin

From the article:

One of the things I like to do is compare how different languages solve the same problem — especially when they end up having very different approaches. It’s always educational. In this case, a bunch of us have been working hard on trying to get reflection — a really transformative language feature — into C++26. Fundamentally, reflection itself can be divided into two pieces:

  1. Introspection — the ability to ask questions about your program during compilation
  2. Code Generation — the ability to have your code write new code

P2996 (Reflection for C++26) is the (huge) core proposal that fundamentally deals with the first problem, along with setting the foundation for being able to extend this feature in lots of different directions in the future, including generation (for which our design is P3294). But introspection, while valuable, is only half of the piece. Andrei Alexandrescu went so far as to claim in his CppCon talk that introspection without generation is useless.

Synchronization Primitives in C++20 -- Shivam Kunwar

El5Swmxm_400x400.jpgIn C++20, the standard library introduced new synchronization primitives: std::latch and std::barrier. These are the utilities designed to coordinate between concurrent threads.

Synchronization Primitives in C++20

by Shivam Kunwar

From the article:

What is a synchronization primitive?

In concurrent programming, synchronization primitives are the fundamental tools that help in managing the coordination, execution order, and data safety of multiple threads or processes that run concurrently.

Briefly said, they ensure that:

  • multiple threads don’t simultaneously execute some specific segment of code (a “critical section”)
  • the program and the data remain in a consistent state
  • deadlocks (where threads wait indefinitely for resources) and race conditions (where the outcome depends on the timing of accessing the shared data by a thread) are prevented or managed

There are multiple synchronization primitives in C++; for example, mutual exclusion, condition variables, atomic operations, locking mechanisms, etc.

In C++20, we have two additional synchronization primitives: latches and barriers.

Let’s discuss both of them.

std::latch

A std::latch is a synchronization primitive that permits a certain number of count_down operations (decrements) before allowing one or more threads to pass the wait point. A latch cannot be reused once its internal counter reaches zero.

How do we use a latch?

  • A std::latch object is created with an initial count.
  • Multiple threads can decrement this count using the count_down method.
  • Threads can call wait, which block until the internal count of the latch reaches zero.

 

Write Modern Code with Features of C++17 and C++20 -- Andreas Fertig

me.pngWhen you transition from older C++ standards like C++11 or C++14 to the latest C++17 and C++20 it can be a tough journey. It's essential for writing clean and easy-to-maintain code, but many developers find the process challenging.

Write Modern Code with Features of C++17 and C++20

by Andreas Fertig

From the article:

Here are three common hurdles:

1. Variety of New Features

Adapting to C++17 and C++20 can be intimidating. As the latest standards offer a wide range of new features and syntax changes. One such feature is the introduction of structured bindings in C++17, which represents a shift from how variables were traditionally declared and accessed.

Old Way (C++14 and Earlier):

Before C++17, when you needed to unpack values from a pair or a tuple, you would typically do something like this:

fertigmodencode.png

Here, you need to use std::get<>() to access each element of the tuple, which is both verbose and less intuitive, especially when dealing with more complex data structures.

C++ Compile-Time Programming -- Yongwei Wu

<img alt="ACCU" data-cke-saved-src="https://isocpp.org/files/img/logo.png" src="https://isocpp.org/files/img/logo.png" 225px;="" height:="" 60px;="" float:="" right;"="" style="float: right;">Programming at compile time has been possible in C++ for a long time. Yongwei Wu considers its past, present, and future.

C++ Compile-Time Programming

by Yongwei Wu

From the article:

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.

 

How To Convert Unicode Strings to Lower Case and Upper Case in C++ -- Giovanni Dicanio

Converting Unicode strings to lower and upper cases should be an easy task to accomplish. Unfortunately, that is not the case with C++. Even the so common approach of iterating "char-by-char" in the input string and invoking std::tolower/toupper is wrong. Let's discuss a possible solution to this problem in this article:

How To Convert Unicode Strings to Lower and Upper Case in C++

by Giovanni Dicanio

From the article:

[...] A possible solution to properly convert Unicode strings to lower and upper cases in Windows C++ code is to use the LCMapStringEx Windows API. This is a low-level C interface API.

I wrapped it in higher-level convenient reusable C++ code, available here on GitHub. I organized that code as a header-only library: you can simply include the library header, and invoke the ToStringLower and ToStringUpper helper functions.

 

What is std::ref? -- Sandor Dargo

SANDOR_DARGO_ROUND.JPGWhen working with C++ standard containers and functions, handling references can sometimes lead to unexpected behavior, particularly with copy semantics. This is where std::ref and std::cref come into play, allowing you to store references in containers and pass them safely to template functions like std::bind or std::thread.

What is std::ref?

by Sandor Dargo

From the article:

Have you heard about std::ref and std::cref? The helper functions that generate objects of type std::reference_wrapper? The answer is probably yes. In that case, this article is probably not for you. But if you haven’t heard about them, or the only usage of std::reference_wrapper you faced was storing references in a vector, then probably it’s worth reading on.

This article is inspired by some failing tests that needed me to use std::ref in order to pass them.

What does reference_wrapper do?

A reference of an object T (T&) is not copy assignable. On the other hand, std::reference_wrapper<T> which emulates T& it both copy-constructible and copy-assignable. It’s even trivially copyable, so copying can take place on a byte level which makes it very efficient.

So when should we use such a wrapper?

Constructing Nodes of a Hand-made Linked List, How Hard Can it Be? -- Raymond Chen

RaymondChen_5in-150x150.jpgWhen designing a circular doubly-linked list, the initial challenge is determining how to manage the construction of new nodes in relation to existing ones. While constructors seem like a natural fit for placing nodes before or after a given node, overloading them can lead to ambiguity and poor design choices. Instead, using distinct tag types or factory methods provides clearer intent, ensuring flexibility while respecting the constraints of guaranteed copy elision for node addresses.

Constructing Nodes of a Hand-made Linked List, How Hard Can it Be?

by Raymond Chen

From the article:

Suppose you are writing your own circular doubly-linked list structure.

struct node
{
    node* prev;
    node* next;
};

A natural choice for the default constructor is to make the node the sole element of a circular doubly-linked list.

struct node
{
    node* prev = this;
    node* next = this;
};

What if you also want to add a node after an existing node? Well, we could add a constructor for that.

struct node
{
    node* prev = this;
    node* next = this;

    node() = default;

    // Construct a node after a specific node
    node(node* other) : 
     prev(other), 
     next(other->next) 
    { 
     prev->next = this; 
     next->prev = this; 
    } 
};

(Note that the “construct after another node” constructor takes the other node by pointer, rather than by reference, so that it won’t be mistaken for a copy constructor.)

But maybe you also want to have a “before” constructor that inserts the new node before an existing node...

What's So Hard About Class Types as Non-type Template Parameters? -- Barry Revzin

revzin200930.pngPreviously, I tried to answer the question: what’s so hard about constexpr allocation?. Today, we continue what will become a series of posts about attempting to explain the issues behind a bunch of hard problems we’re trying to solve. The next problem: class types as non-type template parameters.

What's So Hard About Class Types as Non-type Template Parameters?

by Barry Revzin

From the article:

Before C++20, the only types you could use as non-type template parameters were scalar types (like int and enums, but not floating point), pointers (including pointers-to-members), and references. Notably, not class types.

Floating point types were also added in C++20.

The hard question about handling class types is: how do you determine template argument equivalence? Class types can define their own equality. But you don’t really want to rely on that, since now matching specializations becomes a quadratic problem — you can’t really do much when determining the instantiation for some template f<x> other than looping through every other value vi to see if x == vi.

The other problem is we need == to really be strong enough. One very easy problem to run into in this space is violating the One Definition Rule (ODR). One essential, core requirement for templates is that instantiating the same template with the same arguments has to yield the same code.