C++ Annotated: June 2021
Welcome to the June edition of C++ Annotated and its companion, the No Diagnostic Required show!
If you are already subscribed, feel free to skip to the news. If you are new, you can explore all the formats we offer. Choose to read, listen, or watch our essential digest of this month’s C++ news:
- Read the monthly digest on our blog (use the form on the right to subscribe to the whole blog).
- Subscribe to C++ Annotated emails by filling out this form.
- Watch the No Diagnostic Required show on YouTube. To be notified of 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).
June news
- Language news (guest content from Phil Nash)
- Learning
- Tools
- Events
- And finally, what can C do that C++ can’t?
Watch the June episode of No Diagnostic Required below, or keep reading for all the latest news!
Language news (guest content from Phil Nash)
June saw another virtual plenary session on the standards committee, the full working-group meeting where papers are voted on for the draft standard. Herb Sutter wrote his regular trip report providing us with highlights, and we’ll look at some of those.
Before we do that, though, it’s worth reflecting on a comment Herb makes about the process still being on the original schedule, and with the original priorities. That means that the next meeting, scheduled for this fall, will be the last one for introducing new features targeting C++23. The meeting after that should be “feature-complete”! With C++20 only recently finalized and compilers still only partially supporting it, this might sound surprising, and even more so when you consider how few features have been voted in so far. But that might be a good thing! C++20 was a big release with some far-reaching features added. We could probably do with more of a “maintenance release” to round out support for those features, fix a few problems that have sprung up around them, and get in a few smaller features if we can. C++23 may finally be the year of modules on the desktop. Or something.
That said, some small – but welcome – changes continue to make it in. Let’s have a look at a few that got in this time.
P1938: if consteval
This one can be seen as addressing an issue with a new C++20 feature. The C++20 feature is std::is_constant_evaluated()
. This is currently a “magic” constexpr
function that returns true
if it is being evaluated at compile time. It’s very useful if you need to do something different depending on how a constexpr
function is called (e.g. if an optimized version can’t be made constexpr
on its own). Unfortunately, it has two problems!
The first is that it’s very often used incorrectly – in a way that looks intuitive and compiles (possibly with a warning, since this was noted even in the C++20 timeframe). It feels natural to want to use this with if constexpr
, like so:
if constexpr (std::is_constant_evaluated()) { // slow, but pure version – only this ever gets called } else { // optimized version, but never executed! }
The trouble is, when we use if constexpr
here, is_constant_evaluated()
is always evaluated in the constant context of the if constexpr
, regardless of how the enclo
sing function is called! So, in fact, you should just use a normal, runtime if
.
That leads to the second issue. Once you’re using a runtime if
within the if
case, it will only be entered in a compile-time context, but that is still in a context that needs to be valid for runtime. That means that if you refer to an argument to the constexpr
function that you’re in, that argument will be runtime if called in a non constant-evaluated context and will fail to compile!
if consteval
fixes both these problems by being a more direct way to express the same idea, and which more obviously parallels if constexpr
. The usage and semantics are clearer, and the if
case is always an “immediate invocation” (i.e. compile-time).
P1659: starts_with and ends_with
We’ve had these as member functions of std::string
(and std::string_view
) since C++20. Frankly it’s a wonder we had to wait until then for them, but it’s good we finally have them. The other odd thing about them, though, is that they appeared as member functions. Logically they should be separate algorithms and not tied to string types at all. Presumably they were added that way for ergonomic reasons – a battle with a long history, especially for string types. This proposal adds them as range algorithms, and by doing that alone, they are now more easily composable with other algorithms (see the example usage in the paper). However, they also add the ability to specify the comparison operator to use. By default it is ranges:: equal_to
, but in some cases (such as numeric sequences) it can be useful to check that a sequence starts with or ends with values that are all greater or less than the comparison sequence.
Finally, the author proposes that changes be made to SD-8 (the Standing Document concerned with compatibility issues) to use range-based algorithms over member functions as a general practice.
P2166: A Proposal to Prohibit std::basic_string and std::basic_string_view construction from nullptr
Talking of std::string
and std::string_view
, constructing either from a null pointer is undefined behavior and can result in a crash, if you’re lucky, or worse, not do much! If that null pointer is known at compile time, then it is possible to detect such a usage at compile time and raise an error. That’s just what this paper proposes. On the one hand, that seems like a no-brainer. On the other hand, is this really something that happens much in practice? Surely you would notice? And yet the paper cites a number of examples found in highly regarded, high-profile libraries, such as LLVM, Poco, and Protobuf! Of course some of these examples are in generic code, which often obfuscates such things. So if we can rule out such bugs with one simple change, then why not? After all, it just amounts to adding a couple of deleted constructors from nullptr_t
.
Having fun with the detection idiom and macros in C++
The detection idiom detects whether certain expressions are valid or ill-formed for given types at compile time. This helps enable specialized implementations of the templated functions. Antoine Morrier presents an implementation that uses macros in the CPP Rendering blog.
While the solution via Concepts described in Sy Brand’s blog might look more elegant and readable, Morrier states that the idea was to get something working, even with C++14, while keeping boilerplate code to a minimum. You’ll find some pattern matching and a recursive macro in the approach, and polymorphic lambdas to get the final macro is_valid
that checks whether a given expression is valid. What do you think? Which solution looks more elegant and is easier to read for you?
Measurements of include times for C++ libraries
This CPP-LIT library isn’t new, but it got lots of contributions in June, which is why we paid attention to it and want to share it with you here. The repo answers the question of how much time is added to the compile time with a single inclusion of <header>
. It covers C++20 headers, including C++20 module versions of STL. It turned out that inclusion of the standard library in module form was amazingly fast.
To make the measurements accurate and reproducible, a few conditions were met:
- The lower limit for the header usage was measured, so tests were not actually using the headers, just including them. Times might become significantly longer if you try using heavy templates from some headers.
- The worst case of sub-includes was measured. That means that every sub-header is included for the first time.
- Times were measured without any caching (which means no PCH or other approaches were used).
- The measurements were taken for the
RELEASE
build with the/std:c++latest
flag passed to the compilation. - The measurements were taken with Visual Studio 2019 16.11.0 Preview 2.0.
C++ text formatting API
Victor Zverovich, one of the maintainers of the {fmt}
library, wrote a blog post on formatting approaches in various languages and in modern C++. It briefly goes through the existing options in various languages, including Python and Rust, and shows the drawbacks of the existing C and C++ approaches. The goal is to show how you can get an extensible format specification checked at compile time with an expected and clear error output in C++.
In the {fmt}
library, it’s implemented using C++20’s consteval
and doesn’t seem to significantly affect compilation speed. Similar compile-time checks for C++20 std::format
have been accepted into the C++ standard and are coming soon.
The big STL algorithms tutorial
Jonathan Boccara once did 105 STL algorithms in less than an hour at CppCon. Sandor Dargo dedicates a blog post to each of the algorithms, so it takes him much longer. The series has already covered modifying sequence operations, sorting operations (except for ranges), and binary search operations.
The June edition features merge
and inplace_merge
. You can learn that not only both of the ranges must be sorted to be merged successfully (the behavior is undefined otherwise), but also it’s the caller’s job to make sure that the output range has enough space to accommodate all the elements from the two inputs. On the contrary, inplace_merge
doesn’t require handling proper space allocation, as it uses the same input for the output.
All articles in the series are accompanied with these small but important details and basic usage examples to help you learn the algorithm.
Switching to Clang improved PVS-Studio’s performance
The PVS-Studio C++ analyzer switched to using the Clang compiler, specifically the Clang-cl compiler on Windows, instead of MSVC. This simple change improved the performance of the analyzer on a set of self tests by 13%!
The most interesting thing in the blog post is the list of things that went wrong and had to be corrected when switching to the Clang-cl compiler:
- A different set of warnings was produced by MSVC and Clang compilers, so the compilation settings had to be updated.
- The
int128
type is supported out of the box in GCC and Clang compilers, so the preprocessor definition had to be updated where used. The alignment in the structures using this type also had to be corrected to avoid segmentation fault errors. - Manually passed the library with built-ins to the linker.
This all reminds me that changing the compiler is not an easy step, especially Clang on Windows. Our team had a similar experience when we were setting up Clang with the Mingw-w64 toolchain in CLion.
CLion 2021.2 EAP comes with an enhanced debugger experience
The CLion 2021.2 Early Access Program is currently running. It started in May with CMake Presets, Lifetime Analysis, and the Cling interpreter. In June, many debugger enhancements were released and they are among our users’ favorite updates in this release cycle.
One great new feature is the ability to use Relaxed Breakpoints. This means CLion will use the base name of the source file instead of its absolute path, and can be useful in cases where path mappings are too complex to use effectively in the debug configuration. The Preview Tab was introduced for the debugger and made it possible to prevent the editor from generating too many tabs when stepping actively through the codebase. The preview tab allows you to view files in a single tab one by one without opening each file in a new tab. You can debug an arbitrary executable with the debug information in CLion with both GDB and LLDB remote debug. On Windows, CLion bundles an LLDB-based debugger implemented by the JetBrains team for the MSVC toolchain. This debugger got a massive overhaul which significantly improved the Visual Studio Natvis customization support.
ReSharper C++ 2021.2 EAP introduces an Inline Function refactoring
It’s good to have refactoring working in both directions, isn’t it? The Extract Method refactoring has been available in ReSharper C++ for a long time. In its 2021.2 EAP, the opposite action – Inline Function – also works. You might want to use it when the function body is obvious, so the code will become clearer if you minimize the number of function calls.
It’s not always possible to inline a function, and in that case ReSharper C++ will let you know with a message like “Cannot inline virtual functions”. When the refactoring is possible, you’ll benefit from a fully automated action that will perform the necessary code transformations, handle all name conflicts for used variables, and reformat the resulting code for you.
Code analysis in CLion with third-party plugins
Code analysis in CLion gives you many options and opportunities to catch various types of errors and inaccuracies. This includes basic compiler checks, Data Flow Analysis catching dangling pointers and other lifetime issues, MISRA-specific Clang-Tidy and Clazy checks, inconsistent naming issues, and more.
Even though the native abilities are strong enough, there are third-party plugins to strengthen the code quality protection. For example, the CppCheck plugin has existed in the plugin repository from the very beginning of CLion’s history. It’s now time for more tools integrated into CLion!
A free SonarLint plugin from SonarSource, the creator of the well-known SonarQube team tool for code analysis, was officially released in May and updated in June. It adds to the MISRA, C++ Core Guidelines, and other modern C++ checks.
There is a plugin for PVS-Studio for CLion open for free beta-testing (you can register and get a trial analyzer key by email). MISRA C and C++ checks, as well as general C++ checks, are available.
News from CppCon 2021
CppCon 2021 will take place October 24–29, both in-person in Aurora, Colorado, USA, and Online! The final details for the event were announced in June, and a call for submissions is now open with a July 19 deadline. If you have a topic on anything related to C++, hurry and submit it to the biggest C++ community event of the year!
Registration is now open! Bjarne Stroustrup will be opening the event and presenting live in Aurora. The talk will be dedicated to the C++20 milestone, which contains all of the features (except the unified function call syntax) that Bjarne described a quarter-century ago in his book “The Design and Evolution of C++”. Big C++20 features are finally starting to be delivered to the major compilers so C++ developers can benefit from them in practice.
A track dedicated to designing quality software components will be launched this year. Everything you want to know about scalable, readable, and maintainable C++ code will be shared there.
And finally, what can C do that C++ can’t?
In early July, a thread on reddit asked the question “After C++17, is there a single C feature that is not possible in C++?” Stable cross-platform ABI specification was the most frequently suggested answer. This is especially interesting in the context of ongoing discussions in the C++ Standardization Committee about breaking the C++ ABI.
The thread also mentions:
- Simplicity (no surprise here, as lots of current C++ language proposals are targeting simpler language).
- C bindings for many other programming languages.
- Compiler support for niche platforms, especially custom embedded compilers. This might be because the C compiler is easier to implement.
- Easier-to-achieve fast compile times (still achievable with C++ but requires more effort).
- Designated initializers were mentioned as a specific language feature (delivered in C++20).
And my favorite answer is: “You can spell the name of “c” with a single keystroke!”