Articles & Books

The Difference Between Undefined Behavior and Ill-formed C++ Programs -- Raymond Chen

RaymondChen_5in-150x150.jpgThe C++ language has two large categories of “don’t do that” known as undefined behavior and ill-formed program. What’s the difference?

The Difference Between Undefined Behavior and Ill-formed C++ Programs

by Raymond Chen

From the article:

The C++ language has two large categories of “don’t do that” known as undefined behavior and ill-formed program. What’s the difference?

Undefined behavior (commonly abbreviated UB) is a runtime concept. If a program does something which the language specified as “a program isn’t allowed to do that”, then the behavior at runtime is undefined: The program is permitted by the standard to do anything it wants. Furthermore, the effect of undefined behavior can go backward in time and invalidate operations that occurred prior to the undefined behavior. It can do things like execute dead code. However, if your program avoids the code paths which trigger undefined behavior, then you are safe.

Once More About the Rule of 5 -- Sandor Dargo

SANDOR_DARGO_ROUND.JPGIn a recent talk at C++OnSea, Arne Mertz highlighted common misuses of guidelines, including the Rule of Five. This discussion prompted me to reflect on a recurring pattern I've observed in C++ classes that explicitly default constructors and destructors, leading to unexpected behaviors with move semantics.

Once More About the Rule of 5

by Sandor Dargo

From the article:

Let’s first repeat what the rule of 5 says.

The Rule of Five tells us that if we need to define any of a copy constructor, copy assignment operator, move constructor, move assignment operator or destructor then we usually need to define all five.

Fair enough.

Have you ever seen classes where the default constructor and destructor are explicitly defaulted? Like this?

class SomeClass {
public:
    SomeClass() = default;
    ~SomeClass() = default;

    void foo();
private:
    int m_num{42};
};

First of all, that’s not the best idea. You can simply remove them. But let’s assume that you cannot remove the user-provided destructor for some reason. Maybe it’s not defaulted and it does something.

What does the famous Hinnant table tell us?

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

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

by Dmitry Sviridkin

From the article:

In the C++98, the committee made a terrible decision that seemed reasonable at the time. They created a specialization for std::vector<bool>. Normally, sizeof(bool) == sizeof(char), but one bit is enough for bool. However, 99.99% of all possible platforms can't address memory one bit at a time. Let's pack bits in vector<bool> and store CHAR_BIT (usually 8) boolean values in one byte (char) for more efficient memory utilization. As a result, one needs to work with std::vector<bool> in a very special way...

What's so hard about constexpr allocation? -- Barry Revzin

Before C++20, constant evaluation couldn't handle allocations, causing any such attempts to fail. This changed with C++20, which introduced the ability to allocate memory during constant evaluation, although with strict limitations requiring deallocation during the same evaluation period. Despite these advancements, certain operations, such as declaring a constexpr std::vector, remain impossible, with the goal of this blog post being to explore why these limitations persist.

What's so hard about constexpr allocation?

by Barry Revzin

From the article:

Before C++20, we couldn’t have any allocation during constant evaluation at all. Any attempt to do so would fail the evaluation — it would no longer be constant.

In C++20, as a result of P0784R7, that changed. Finally we could do allocation during constant evaluation. However, this evaluation was extremely limited. Specifically, any allocation that happens must be deallocated during that constant evaluation.

This opened the door to a wide range of operations that weren’t possible before. We can now have local std::strings and std::vector<T>s! Those just work!

But we still cannot just declare a constexpr std::vector:

#include <vector> 
constexpr std::vector<int> v = {1, 2, 3}; // error 

And we cannot even declare a local constexpr std::vector in a consteval function:

#include <vector> 
consteval auto f() -> int { constexpr std::vector<int> v = {4, 5, 6}; // still error 
return v.size(); 
} 

This limitation still remains in C++23 and could very well still remain in C++26. The goal of this blog post is to explain why we haven’t just solved this problem already.

What to do if you don't want a default constructor? -- Sandor Dargo

SANDOR_DARGO_ROUND.JPGDo we need a default constructor? What does it mean to have a default constructor? What happens if we don’t have one? Those are the questions we are going after in this article.

What to do if you don't want a default constructor?

by Sandor Dargo

From the article:

A default constructor is a constructor that takes no arguments and initializes - hopefully - all the members with some default values. If you define no constructors at all, it’ll even be generated for you.

Do we need default constructors?

It really depends. Let’s first approach this question from a design point of view. Does it make sense to represent an object where the members are default initialized?

If you represent a tachograph, it probably makes sense to have such a default state where all the counters are initialized to zero.

The tachograph is the device that records driving times and rest periods as well as periods of other work and availability taken by the driver of a heavy vehicle.

On the other hand, if you represent a person or a task identifier - which inspired me to write this article - it doesn’t. A person with an empty name or a task ID with an empty ID doesn’t make much sense.

Well, that’s the case from a design point of view. What about the technical aspects? What happens if we don’t have a default constructor?

22 Common Filesystem Tasks in C++20 -- Bartlomiej Filipek

Filipek-22common.pngWorking with the filesystem can be a daunting task, but it doesn’t have to be. In this post, I’ll walk you through some of the most common filesystem operations using the powerful features introduced in C++17, as well as some new enhancements in C++20/23. Whether you’re creating directories, copying files, or managing permissions, these examples will help you understand and efficiently utilize the std::filesystem library.

22 Common Filesystem Tasks in C++20

by Bartlomiej Filipek

From the article:

Creating directories is a basic yet essential operation. The std::filesystem library makes this straightforward with the create_directory function.

Filipek-22common2.png

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

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

by Dmitry Sviridkin

From the article:

This program, built by GCC 10.1, -std=c++20 -O3, doesn't crash, but it doesn't output anything either. If we take GCC 14.1 and the same keys, we suddenly get "helloworld" in the output. It's old but gold undefined behavior.

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

SANDOR_DARGO_ROUND.JPGI had the privilege to attend and present at C++ on Sea 2024 for the 5th time in a row!

Trip Report: C++ On Sea 2024

by Sandor Dargo

From the article:

The conference had a very strong start. Right after the keynote by Dave Abrahams, 4 incredible speakers were on stage at the same time on the 4 different tracks. Jason Turner, Walter E Brown, Nico Josuttis, Mateusz Pusz…

 

If a conference could have these people throughout the whole program, it would already be a strong conference. C++ On Sea proposed such a strong line-up that these people could be scheduled at the same time. It didn’t make my decision easier, so I chose based on the topic, and I wanted to grow my knowledge on constexpr so I stayed in the main() room.

What’s the point of std::monostate? You can’t do anything with it! -- Raymond Chen

RaymondChen_5in-150x150.jpgC++17 introduced std::monostate, a dummy type with no members and trivial functions, primarily used when no action is needed. Despite its simplicity, std::monostate plays a crucial role in scenarios like coroutines and std::variant, where a default-constructible placeholder type is required.

What’s the point of std::monostate? You can’t do anything with it!

by Raymond Chen

From the article:

C++17 introduced std::monostate, and I used it as a placeholder to represent the results of a coroutine that produces nothing. In the comments, Neil Rashbrook asked what you are expected to do with a std::monostate, seeing as has no members and only trivial member functions.

The answer is “nothing”.

The purpose of std::monostate is to be a dummy type that does nothing. All instances are considered equal to each other. It is basically this:

struct monostate {};
// plus relational operators and a hash specialization

You can see it in libcxx (LLVM/clang)libstdc++ (gcc), and stl (msvc).

Concurrency: From Theory to Practice -- Lucian Radu Teodorescu

concurrencyteodorescu.pngConcurrency is a complicated topic. Lucian Radu Teodorescu provides a simple theory of concurrency which is easy to reason about and apply.

Concurrency: From Theory to Practice

by Lucian Radu Teodorescu

From the article:

One of the big challenges with concurrency is the misalignment between theory and practice. This includes the goals of concurrency (e.g., improving the performance of the application) and the means we use to achieve that goal (e.g., blocking primitives that slow down the program). The theory of concurrency is simple and elegant. In practice, concurrency is often messy and strays from the good practices of enabling local reasoning and using structured programming.

We present a concurrency model that starts from the theory of concurrency, enables local reasoning, and adheres to the ideas of structured programming. We show that the model can be put into practice and that it yields good results.

Most of the ideas presented here are implemented in a C++ library called concore2full [concore2full]. The library is still a work in progress. The original goal for this model and for this library was its inclusion in the Hylo programming language [Hylo]. For Hylo, we want a concurrency model that allows local reasoning and adheres to the structured programming paradigm. We also wanted a model in which there is no function colouring [Nystrom15], in which concurrency doesn’t require a different programming paradigm.

This article is based on a talk I gave at the ACCU 2024 conference [Teodorescu24]. The conference was great! The programme selection was great; there was always something of interest to me. With many passionate C++ engineers and speakers, the exchange of information between participants was excellent; as they say, the best track was the hallway track. I highly encourage all C++ enthusiasts (and not just C++) to participate in future ACCU conferences.