December 2024

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

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

by Dmitry Sviridkin

From the article:

Developing multithreaded applications is always challenging. The problem of synchronizing access to shared data is a perennial headache. It'd be ideal if we had a well-tested, reliable library of containers, high-level primitives, and parallel algorithms that managed all invariants. It'd be ideal if static compiler checks prevented us from misusing all these things. How nice it would be... Before C++11 and the standardized memory model, we could use threads but only at our risk. Starting with C++11, there are some pretty low-level primitives in the standard library. Since C++17, there are still various parallel versions of algorithms, but we can't even fine-tune the number of threads or their priorities.

The Puzzle of Trying to Put an Object into a std::optional -- Raymond Chen

RaymondChen_5in-150x150.jpgThe std::optional<T> is a powerful tool for handling optional values, but assigning non-trivial types like Doodad to it can lead to unexpected compilation errors. This post explores why such assignments fail and unpacks the nuances of std::optional and type construction in modern C++.

The Puzzle of Trying to Put an Object into a std::optional

by Raymond Chen

From the article:

The C++ standard library template type std::optional<T> has one of two states. It could be empty (not contain anything), or it could contain a T.

Suppose you start with an empty std::optional<T>. How do you put a T into it?

One of my colleagues tried to do it in what seemed to be the most natural way: Use the assignment operator.

struct Doodad
{
    Doodad();
    ~Doodad();
    std::unique_ptr<DoodadStuff> m_stuff;
};

struct Widget
{
    std::optional<Doodad> m_doodad;

    Widget()
    {
        if (doodads_enabled()) {
            // I guess we need a Doodad too.
            Doodad d;
            m_doodad = d;
        }
    }
};

Unfortunately, the assignment failed to compile:

Implicit String Conversions to Booleans -- Sandor Dargo

SANDOR_DARGO_ROUND.JPGIn this article, we'll learn about -Wstring-conversion, something I learned from C++ Brain Teasers by Anders Schau Knatten](https://www.sandordargo.com/blog/2024/10/16/cpp-brain-teasers). Clang offers this compiler warning which fires on implicit conversions from C-strings to bools.

Implicit String Conversions to Booleans

by Sandor Dargo

From the article:

Let’s start with the first part by explaining why such an implicit conversion is possible. A string literal is an array of const chars. Arrays can be converted into pointers, something we talked about last week when we discussed why spans are so useful. This is also called decay. Furthermore, pointers can be converted into booleans. That is how a string literal can be converted into a bool.

static_assert(!!"" == true);
static_assert(static_cast<bool>("") == true);

What might be surprising though is that even an empty string literal is converted into true. The reason is that only a nullptr would be converted into false, but an empty string literal is an array of a size of one so it’s not a nullptr. As a result, "" converted to true. The possible confusion is that the one character in that array of one is the \0 terminator. But this shouldn’t really matter. You shouldn’t use such shady implicit conversions.

We could end this article right here. But life is not ideal and I tried to turn on -Wstring-conversion in a production codebase where I found a few different cases of string literals conversions.

Around the World in C++: Exploring Time Zones with std::chrono -- Bartlomiej Filipek

2024-11-21_12-55-09.pngWhile most time zones use simple hour offsets from UTC, some regions have chosen unusual time differences. In this blog post, we’ll explore how we can discover such zones using C++20’s chrono library.

Around the World in C++: Exploring Time Zones with std::chrono

by Bartlomiej Filipek

From the article:

We’ll use GCC 14.2 as it fully supports C++20 chrono and also std::print from C++23.

First Attempt: Basic Zone Iteration

C++20 introduced comprehensive time zone support through the <chrono> library. The implementation relies on the IANA Time Zone Database (also known as the “tz database” or “zoneinfo”), which is the de facto standard for time zone information used by most operating systems and programming languages.

The Time Zone Database

In C++20, the time zone database is represented by the tzdb class:

2024-11-21_12-57-47.png

Use std::span Instead of C-style Arrays -- Sandor Dargo

SANDOR_DARGO_ROUND.JPGC-style arrays are still used, mostly when you have to deal with C-libraries. They come with significant limitations, particularly when passed to functions where array decay occurs, leading to the loss of size information.

Use std::span Instead of C-style Arrays

by Sandor Dargo

From the article:

While reading the awesome book C++ Brain Teasers by Anders Schau Knatten, I realized it might be worth writing about spans.

std::span is a class template that was added to the standard library in C++20 and you’ll find it in the <span> header. A span is a non-owning object that refers to a contiguous sequence of objects with the first sequence element at position zero.

In its goal, a span is quite similar to a string_view. While a string_view is a non-owning view of string-like objects, a span is also a non-owning view for array-like objects where the stored elements occupy contiguous places in memory.

While it’s possible to use spans with vectors and arrays, most frequently it will be used with C-style arrays because a span gives you safe access to its elements and also to the size of the view, something that you don’t get with C-style arrays.

When and why does it come in handy?