C++ Annotated November 2021: Discussions on C++ Ecosystem, a Book on C++ Core Guidelines, Designated Initializers, and Recent Releases
We have all the November news for you in our latest monthly C++ Annotated digest and its companion, the No Diagnostic Required show.
If you have already subscribed, feel free to skip ahead straight to the news. If this is your first time here, let us quickly take you through all the formats we offer. You can choose to read, listen, or watch our essential digest of the month’s C++ news:
- Read the monthly digest on our blog (use the form on the right to subscribe to the blog).
- Subscribe to C++ Annotated emails by filling out this form.
- Watch the No Diagnostic Required show on YouTube. To make sure you hear about new episodes, follow us on Twitter.
- Listen to our podcast – just search for “No Diagnostic Required” in your favorite podcast player (see the list of supported players).
This month we’re going to focus on a set of contracts-related proposals, starting with a couple we’ve seen before.
P2388R4 “Minimum Contract Support: either No_eval or Eval_and_abort”
The ongoing “MVP” of contracts papers gets another update in the November mailing. This revision includes a few interesting changes.
To start, there’s a new section on the risk of infinite recursion. If a contract is specified in terms of a call to something which itself has a contract, then there is potential for cycles to occur, leading to infinite recursion. A couple of approaches to dealing with this are discussed.
The section on syntax choices now includes references to the closure-based syntax paper we discussed last month, and a new one on the attribute-like syntax, which we’ll discuss shortly.
There are also some clarifications on the models for predicate elision during contract checking, including how sub-expressions may be elided. This can be useful if the same expression, or sub-expression, is evaluated multiple times throughout the call stack.
But perhaps the most telling change is that all references to C++23 as a target have been updated to C++26!
P2461R1 “Closure-Based Syntax for Contracts”
Last month we discussed the R0 of this paper, which lays out one of the alternate syntaxes for contracts that the syntax section in P2388R4 refers to. This new revision removes some of the specific syntax options that didn’t get much support from the committee, clarifies that its intended purpose is about opening up future extension possibilities, and includes a section on predicate elision, in a similar vein to the new section in P2388R4. Here, though, a line is drawn by specifying all-or-nothing elision – eliding sub-expressions is ruled out.
There are many other small tweaks and updates based on the feedback and review that R0 got. It’s all part of the process, but it does indicate that there is a lot of interest in this proposal.
P2487R0 “Attribute-like syntax for contract annotations”
This new paper rounds out the balance between the two competing syntax options by exploring the attribute-like syntax in more depth. In particular it digs into some of the specific terminology where it has been noted that not everyone has the same understanding. As we’ve seen before with contracts, misalignments here can be at the root of big, ongoing misunderstandings – and even arguments!
One of the most interesting sections is a discussion of what the term “Ignorable” means. This is significant in context as there are certain expectations associated with attributes – that even a merely attribute-like syntax evokes – that turn out to be not as well-defined as we might have thought. Does it mean, for example, that expressions within an attribute must be compilable, even if “ignored”?
P1774R4 “Portable assumptions”
This paper is not new, but it has been dormant for a while. Part of the fallout from C++20’s contracts being removed from the working draft centered on the ability to use contracts to hint to the compiler where it could make “assumptions” that could be optimized around. There was a realization that not everyone agreed that this belonged as part of a feature for specifying correctness. There certainly seems to be a lot in common in how they should be expressed (although contracts may get much more complex) – and maybe even in intent (if you state “this should never happen in a bug-free program”, then it seems to follow that the compiler should be able to optimize around that). But conflating the two also introduces some very tricky issues, including time-travel UB, as we discussed back in episode 4. Just introducing a new form of UB at all, in the attempt to make your programs better specified, is a no-go for some people.
Disentangling these things again seems like a sensible approach, and that’s what this paper does. There are already implementation-specific extensions to express assumptions, so there is prior art and working experience. This paper proposes a standard feature to do the same thing – using actual attribute syntax (as opposed to the attribute-like syntax of P2487).
There are some minor quibbles around some details within the committee, but no major roadblocks. At this point it’s still feasible for this feature to make it into C++23, but it’s too early to make any assumptions.
Three reasons to pass std::string_view by value
A new article by Arthur O’Dwyer discusses three performance benefits of using pass-by-value over pass-by-(const)-reference on a
std::string_view use case. It’s kind of known for cheap types like
std::pair<int, int>, and it should be applied to types like C++17
span, and C++2b
function_ref, which are explicitly designed to occupy the same category as
- Pass-by-value makes it unnecessary to load from memory trivial types that are passed in registers and not via the stack.
- Pass-by-reference works via the address, so the caller has to spill it onto the stack, whereas pass-by-value can sometimes eliminate the need for a stack frame in the caller.
- Pass-by-value provides the callee with a greater number of optimization options, whereas the reference to the unknown object forces the compiler to behave more conservatively.
Meanwhile, if the performance cost of making a copy outweighs all of these benefits, then pass-by-const-reference should be used.
There was an interesting follow-up discussion raised in response to Arthur’s post, which started from a note that Microsoft’s x86-64 ABI does not pass
std::string_view in registers. But it is still better to use pass-by-value for the purpose of expanding to other architectures and in universal code across platforms.
Virtual roundtable about C++ ecosystems
At the end of November, I (Anastasia) took part in the virtual roundtable that was hosted by Incredibuild. Together with Cameron DaCamara (Microsoft), Diego Rodriguez-Losada (Conan/JFrog), and Amir Kirsh (Incredibuild), we had a great discussion about how C++ developers select a tool for certain domains and which criteria are more and less important to them. It was especially interesting to consider if the aspect of open sourcing matters more than other aspects. Each of us shared our favorite tools in a list of must-haves and ones that we use daily.
Finally, we discussed the past and the future of the C++ ecosystem. We started with the improvements we have seen in the C++ ecosystem in the past five years. We then shared our thoughts on where we think the C++ ecosystem will go in the next decade and how the language itself and the tools will evolve.
Talking to SerenityOS contributors
SerenityOS is an open source graphical Unix-like operating system for desktop computers. Started in 2018 as a single-developer pet project, it has since grown into a much bigger collaborative project with many developers now involved with it. Interestingly, it’s built from scratch in a single repository with no third-party runtime dependencies. It uses modern C++20 with a custom standard library (not STL) used in both the kernel and user space. Because of the design choice, SerenityOS is a “developer’s playground” where people can learn how software works by building it inside a monolithic environment.
In this interview, we talked to two of the main contributors to the project – Andreas Kling (also a founder) and Linus Groh. We discussed the tech stack and the move from Makefile to CMake, learning C++ with SerenityOS, the user experience of using CLion for the project, and more.
Using Catch2 v3 in CMake with DDS and PMM
This super-short, practical blog post demonstrates how to use Catch2 v3 with CMake. Catch2 has been known as a header-only unit testing framework for a long time. But it was announced that the framework is moving away from this approach in v3. Many users were worried that with these changes the simplicity of attaching Catch2 to the project will be lost. The post addresses the issue with an elegant solution.
The author shows how to attach Catch2 to the project easily with the help of the Drop-Dead-Simple (DDS) tool and the Package Manager Manager (PMM) CMake module. PMM is actually just a small CMake script that manages external packaging tools (right now, it supports Conan, vcpkg, CMakeCM, and DDS). PMM automatically downloads, installs, and controls package managers from within your CMake project. It’s used here for Catch2 and DDS.
The Catch2 DDS package has some limitations but as a general proof of concept it works.
Designated initializers in C++20
In the past there were nice talks by Timur Doumler and Nico Josuttis about the many ways C++ has to initialize even simple things. Now C++20 brings a new one! Designated initializers is a new way to initialize data members.
Designated initializers increase the code readability, especially when compared to using initializer lists, as it explicitly names the data member. This can reduce the number of errors that are introduced from initializer values being swapped or reordered by mistake. Another benefit is its similarity with the C99 approach and the flexibility it provides, as some data members can be skipped in this approach.
The blog post lists the rules that apply to designated initializers. It also discusses the benefits we mentioned above. The example has a link to Compiler Explorer, so you can have a play with it yourself.
Another interesting benefit we’d like to mention here is that we’ve worked on automatic generation of C++20 designated initializers in ReSharper C++ 2021.3. It works when completion is triggered and the “designated initialization” item is selected. ReSharper C++ will then automatically insert the designators for all the class fields and all you have to do is provide the initial values.
Beautiful C++: 30 Core Guidelines for Writing Clean, Safe, and Fast Code
Kate Gregory and Guy Davidson have announced a new book on C++ Core Guidelines. It will be published around the end of December. It is available for pre-order now in both printed and electronic formats.
It all began with Kate’s CppCon 2017 talk titled “10 Core Guidelines you can’t miss”. Guy then expanded this to 30, bringing more examples and detailed explanations to the practical use cases. The C++ Core Guidelines editors, Bjarne Stroustrup and Herb Sutter, reviewed the book and provided a foreword and an afterword for it. Specifically, Bjarne says the book provides “an emphasis on what benefits can be obtained from following the rules and what nightmares can result from ignoring them.”
If you don’t know where to start with C++ Core Guidelines, start with this book.
Visual Studio 2022 release
Microsoft announced the general availability of Visual Studio 2022. First and foremost, Visual Studio v2022 is now 64-bit, which means more resources are allocated to the IDE so it can handle large code bases better. Another big thing is that Hot Reload gives you the opportunity to update your code and see changes immediately. It is available to native C++ applications that are running under the debugger. We talked about this in the July 2021 edition of our C++ Annotated.
Other highlights for C++ developers include:
- You can now build and debug natively on WSL 2 without establishing an SSH connection.
- Enhanced support for CMake Presets.
- Remote debugging with LLDB from Visual Studio.
- Support for libfuzzer under the switch
- Improved code analysis and many IntelliSense actions.
New remote development options by JetBrains
There were some huge announcements from JetBrains last week that should benefit software developers around the globe. In the new remote reality we have found ourselves in these last few years, remote functions, distributed architectures, and online collaboration have all become essential needs we can’t work without. We have been working on this functionality for a while now, and last week, we opened the preview and beta versions to the general public.
If you are already a user of our IntelliJ-based tools (including CLion, IntelliJ IDEA, PyCharm, and others), you can benefit from the brand-new Remote Development support in these IDEs. You can host your source code, toolchain, and IDE backend on a remote server, and use a local thin client based on the IntelliJ Platform to write, navigate, refactor, run, debug, and test your project. The experience is like working with a project on your JetBrains IDE locally. This is in beta now. Please note there are some limitations specific to CLion.
If you are open to trying new tools, the Fleet announcement might be something you are interested in. What is Fleet? Fleet is a polyglot lightweight editor, which you can turn into a fully functional IDE with a single click of the mouse. It comes with a completely reimagined UI, it is built with a distributed architecture, and it has been developed using JetBrains’ 20+ years of experience of creating IDEs and other developer tools. We opened the Early Preview and within just a couple of days had over 130,000 requests to join it. So for now, the registration to the Preview is closed, but you can subscribe for updates and check out the public preview announcement at jetbrains.com/fleet. Don’t be too upset as C and C++ support is still not yet there. However, our team is working on it and we hope to deliver it in one of the Fleet previews in 2022.
CLion 2021.3 release
The third major release of the CLion IDE this year, CLion 2021.3, is compatible with the new remote development capabilities. This is not the only improvement in the CLion toolchains. The new update adds more flexibility to the current toolchains and adds new ones:
- CLion adds the ability to configure the toolchain environment with a script by specifying it in the toolchain’s settings.
- CLion bundled MinGW on Windows and Ninja on all platforms. For newly created or opened projects, the Ninja generator in CMake is now the default (for local toolchains and CMake 3.20 and later).
- The Custom Compiler option for toolchains, which allows using any compiler not natively supported by CLion. The CLion team has started a GitHub repository with sample config files in Yaml format for some of these compilers. Users are encouraged to send pull requests as well.
- A new Docker toolchain was introduced in CLion to work with local containers.
As for the editor, CLion 2021.3 now helps with the C++ types that may not be immediately obvious, such as those hidden behind
auto specifiers, in structured bindings, or as lambda return types. The new type hints show the deduced types in such cases. There are also many improvements to the debugger: for example, CLion now renders types for variables and in functions, modules, and threads. There are also new settings that allow users to configure the view they consider helpful. You can find more about these and other new goodies in our blog post.
Qt Creator and clangd
The article on the Qt blog from late October, is interesting and wasn’t covered by our October edition of C++ Annotated. It discusses the evolution of the C++ language engines in Qt Creator. It moved from its own implementation to libclang soon after Qt Creator contributors realized that it’s difficult to keep pace with the evolution of modern C++. With the LLVM developers focusing on Clangd and leaving libclang without much attention, Qt Creator then moved to Clangd, a standalone Clang-based daemon with which you can communicate using Language Server Protocol.
Qt Creator now also utilizes a clangd index, which stores information about the whole codebase. It’s used for features like Find References where the AST of the current file doesn’t have enough information – full indexing takes time but the update is incremental.
An interesting side effect of this migration to clangd is that files changed by refactoring actions are auto-saved. This is a direct consequence of the fact that clangd considers only the on-disk state of included header files when parsing a source file.