Articles & Books

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.

 

Trip report: C++ On Sea 2024 -- Sandor Dargo

logo.pngC++ On Sea took place in Folkestone again in February this year. Sandor Dargo shares an overview of his favourite talks and some emergent ideas.

Trip report: C++ On Sea 2024

by Sandor Dargo

From the article:

Last week, between the 3rd and 5th of July, I had the privilege of attending and presenting at C++ on Sea 2024 [CPPoS-1] for the 5th time in a row! I’m grateful that the organizers accepted me not simply as a speaker, but that they granted me a double slot to deliver a half-day workshop about how to reduce the size of a binary. I’m also thankful for my management that they gave me the time to go to Folkestone and share our knowledge on binary size. Last but not least, great thanks goes to my wife, who took care of the kids alone that week.

Let me share with you a few thoughts about the conference.

First, I’m going to write about the 3 talks that I liked the most during the 3 days, then I’m going to share 3 interesting ideas I heard about and then I’m going to share some personal impressions about the conference.

std::array in C++ isn't slower than array in C

In my previous article on arrays, some readers expressed concern that std::array might be slower than the built-in C array. Several sources of truth exist on this matter, and today we'll go through each of them. Let's first find out what the standard states about it, then look at the std::array implementations in libc++ and libstdc++, and finally look at the assembler of some operations on these objects. Oh, and we'll top it off with benchmarking, of course.

std::array in C++ isn't slower than array in C

by Anton Tretyakov

From the article:

Let's get to the bottom of this. LLVM has a hardening mechanism called _LIBCPP_HARDENING_MODE. We can use it to enable additional checks depending on the mechanism level, which has a total of four levels. Enabling the weakest one removes the checks from the code. In other cases, there may or may not be a check, depending on the check and the level of the mode. We'll prove it. To understand what expands to what, we need to look at the source code. There, we see that depending on the given value of _LIBCPPP_HARDENING_MODE, _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS may expand to _LIBCPPP_ASSERT.

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.