C++ Annotated: July 2021
Welcome to the July 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).
July news
- Language news
- Learning
- The C++ ecosystem in 2021
- Be Wise, Sanitize – Keeping Your C++ Code Free From Bugs
- C++20’s concepts are structural. What, why, and how to change it?
- Counting in Iteration Models
- An iterator IS-NOT-A const_iterator
- Performance of the Parallel STL Algorithms
- How to Return Several Values from a Function in C++
- Releases
- And finally, Bjarne Stroustrup on the future of programming
Watch the July episode of No Diagnostic Required below, or keep reading for all the latest news!
Language news
P2300: std::execution
Executors have had a long history in the standardization pipeline – from the first executors paper back in 2012, through several alternate branches, a unification in 2016, and 14 revisions of that paper alone – all building on or reacting to earlier language features like std::future
and std::async
. And now we have another new paper.
Except this is not an executors paper. While this new proposal, authored by many of the same people, aims to replace the unified executors proposal and cover most of the same ground, it does so using a sender/receiver model instead of executors. Along the way it addresses most, if not all, of the issues with the previous proposal raised by SG1, and it drops some things that were considered unwanted or unnecessary for an initial design. That doesn’t mean it’s smaller, though. It weighs in at over 22 thousand words, vs 15.5 thousand for P0443R14.
I’m not familiar enough with the details to be able to comment on any of this. All I can say is that a design in this space is critical, both for the evolution of the language and its libraries, but also for making full use of our modern multi-core and increasingly heterogeneous computing world. It’s been 16 years since Herb Sutter declared that “The Free Lunch Is Over”. It’s about time we paid for that lunch before someone else eats it all.
P2388: Abort-only contract support
Since contracts were (in)famously removed from C++20, after initially being merged into the working paper, SG21 was formed and has been working to establish a consensus on as much of the groundwork as possible. We’ve commented on some of this work before. We’re now seeing design proposals coming out of that (first was P2182, a precursor to this one). Billed as an MVP, P2388 contains just three almost-attributes and two modes.
The attributes-that-aren’t-attributes (slightly different syntax, for reasons given in the paper) are the minimal form of what was in P0542R5: pre
, post
, and assert
. They simply take predicates to check, with no assertion modes.
The two modes are Ignore
(parsed for validity, but generating no code – or time-travelling UB) and Check_and_abort
(which does what it says on the tin). No continuation modes. No throwing violation handlers or even custom violation handlers.
The defining limitation of this proposal is the “abort-only” part (hence its presence in the title). Continuation or exception throwing approaches were central to previous disagreements (although there were other issues), so the all-or-nothing approach of this paper, while limiting, is aimed at getting us something, hopefully in time for C++23, which can be built on, possibly even within the C++23 timeframe (although that’s looking less likely).
P2392: Pattern Matching using is and as
Another big feature we had hoped to see for C++23, but which has been looking less likely, is Pattern Matching (P1371). The proposal, which is actually a unification of proposals led by Michael Park and David Sankel, has been ticking along nicely for a few years now, though it has perhaps slowed a bit during the pandemic. Now we have an entirely new paper from Herb Sutter! Is that going to set us back even more?
Well, Herb’s paper, while complete in itself, builds on P1371, but with an important change in direction. Herb recognized, correctly, that the underlying facilities of pattern matching are not limited to inspect
statements (or event inspect
expressions), which is the new keyword for introducing a pattern match with multiple alternatives. Therefore it would be a shame if we special-cased the syntax just for use in that context. In particular he identifies if
statements (and other conditionals), structured bindings, casts, and even requires
clauses. Additionally, one of the frequent sticking points of the existing proposal is determining how to distinguish between identifiers that introduce new names (name bindings) and references to existing variables. From a controversial start, using a ^
prefix, to alternately trying case
for references or let
for bindings, this issue never seems fully settled. Herb sidesteps this by separating the part of the syntax that does one from the part that does the other, and in a way that makes it obvious which is which.
He does all this with just two keywords, perhaps not surprisingly, given the title: is
and as
. These are more general and more powerful than they might first appear, especially if you’re familiar with similar keywords from languages like C#. Unlike in those languages, these are not just synonyms for applications of dynamic_cast
(although they do subsume those in a nice way). They are overloadable operators that, even before overloading, have well defined (if a little complicated to enumerate) semantics that make sense of a whole range of things you can do with them, fairly intuitively. In a pattern match they solve the binding problem by having the introduced names on the left, and references to names and values in a pattern on the right.
But they can be used outside of inspect
statements, too. For example, testing whether a variant
holds an int
could be written as:
if( v is int ) { /* … */ }
Which is much nicer and easier to read than
if( std::holds_alternative<int>(v) ) { /* … */ }
In some cases Herb’s syntax is shorter, but in general it tends to be slightly more verbose, introducing inspect
alternates with is
and splitting the binding and matching patterns with as
. If that’s the cost of being able to generalize through the language, then that might be a price worth paying. But perhaps we can still do both? It will be interesting to see how this goes. It’s not clear whether this will slow us down from getting something into C++23, if that’s even possible at this point. But I think it’s worth exploring.
P2409: Requirements for Usage of C++ Modules at Bloomberg
We have modules in C++20, and before they were adopted there was plenty of discussion about the potential for problems with tooling around them that was resolved to most parties’ satisfaction. So what is this paper all about?
Well, part of that resolution was a proposal for the Tooling study group (SG15) to produce a C++ Ecosystem Technical Report on modules (P1688). This new Bloomberg paper also proposes a TR. At first I was confused by this, so I contacted the author who confirmed that this is a different TR, much more focused on the set of requirements laid out in the paper. Despite the name, the requirements are not really specific to Bloomberg, but potentially common to many projects with inter-dependencies which are not using a mono-repo. Most of the requirements are about module discoverability; in fact, three of them start with the words, “Module discoverability should…”, and three others have the word “discover” or “discoverable” in them. The remaining of the seven requirements is about testing for the existence of a module.
So it’s good to see SG15 still moving forward with recommendations for how modules should be used in real-world cases and how the tooling around them will need to account for that. Whether this TR, the original one, or some combination ends up getting out, we’re inching ever closer to a modules world.
The C++ ecosystem in 2021
This month we finally published the Developer Ecosystem 2021 report with infographics based on the survey data we collected during the first months of 2021. There are many interesting observations for C++, which we discussed in this blog post with Matt Godbolt, creator of Compiler Explorer, and Andreas Kling, creator of SerenityOS.
Nearly 1 out of every 5 C++ developers is already using C++20, even though major compilers have been very slow to adopt this big C++ release. 48% plan to start using Modules in the next 12 months. Concepts are close behind, and are a choice for 46%.
We still see a third of developers not writing any unit tests at all, and I agree with Matt that this looks disappointing. Many competing solutions are present for the package management problem. But manually bringing the library source code into the project is still trending for 26% of the respondents.
The top three project models are still CMake, Makefile, and Visual Studio, but the last two have swapped spots.
Check out the full report and the accompanying blog post for other interesting facts.
Be Wise, Sanitize – Keeping Your C++ Code Free From Bugs
If you are still not using sanitizers, this might be a good article to get started with. It describes Address Sanitizer (ASan), Memory Sanitizer (MSan), and Undefined Behavior Sanitizer (UBSan), with short explanations of how they all work and what issues they are capable of catching. The article also provides estimates of the CPU and memory overhead.
For ASan, there’s a nice visualization of so-called red zones around each set of bytes allocated with the malloc
function. These red zones are added by a run-time library and help detect use-after-free, double-free, buffer overflows, underflows, and other issues.
For MSan, you may want to read this Google paper to understand the algorithm in detail. MSan helps detect the use of uninitialized memory. The approach uses bit-to-bit shadow mapping to avoid false positive reports on the copying of uninitialized memory. All newly allocated memory is “poisoned”, that is, corresponding shadow memory is filled with 0xFF
, signifying that all bits of this memory are uninitialized. Warnings are raised when uninitialized memory is incorrectly accessed.
For UBSan, you can learn about the flags for many specific UB cases that the sanitizer can catch, like integer-divide-by-zero, signed-integer-overflow, and others (the flag names speak for themselves).
There are still some downsides for using the sanitizers discussed in the article, but it’s to know about your options.
C++20’s concepts are structural. What, why, and how to change it?
C++20’s concepts are now considered one of the major benefits of the new language standard, and there are many guidelines promoting Concepts. Jonathan Müller wrote a blog post comparing nominal and structural concepts. The most interesting outcome is that, “if you want to distinguish between identical syntax with different semantics, you need to introduce some syntax to distinguish it.”
In the case of structural typing, a type models the concept if it has the same structure as the one required by the concept. This also means that naming becomes really important – names and structure are taken into account, not the semantics. The problem is illustrated by the example with the TShirt
class from the article, which passes the semantic checks for std::vector
. To avoid such situations, techniques like a dummy typedef or inheriting from a tag type are used.
In other languages in which Concepts were introduced at the very beginning, a developer has to state the concept explicitly to model it. Well, that doesn’t help with 30-year-old C++ code bases and many library dependencies that are added manually! Check out the article for many self-explanatory examples.
Counting in Iteration Models
Barry Revzin, who gave a talk about iteration models of several different languages at this year’s C++Now conference, wrote a blog post in which he ungrouped the C++/D and C# models. The actual issue is highlighted in P2406 and is about counted_iterator
, which increments its internal iterator even when it reaches its own end. In C# and Rust, access to the underlying enumerator, iterator, and so on is provided based on the count
check. In C++ and D, on the contrary, the underlying iterator is incremented even if we decrement count
to 0. Let’s see what the committee says about the proposal and which option will be used to address the case.
An iterator IS-NOT-A const_iterator
Arthur O’Dwyer made an interesting discovery about iterators. In the STL, it is guaranteed that C::iterator
is convertible to C::const_iterator
. This implicit conversion is implemented differently in libc++/libstdc++ and in Microsoft’s STL. The former uses a converting constructor, while the latter uses a base class conversion. That makes a difference when an implicit conversion sequence is used. Arthur also notes that public inheritance should be used only for IS-A relationships. Microsoft’s implementation seems to violate that principle. Check out the blog post for more details.
Performance of the Parallel STL Algorithms
Rainer Grimm wrote a couple of posts recently about the parallel algorithms of the Standard Template Library. In addition to describing the basics about sequential, parallel, and vectorized execution policies, he also measured their performance to examine the benefits. Rainer measured the test with Microsoft Compiler 19.16 on Windows and the GCC 11.1 On Linux. The results were just as expected:
- On Windows with 8 cores, parallel execution is faster than sequential by a factor of more than 10, while parallel and vectorized execution take comparable time. The latter result is reasonable, as Visual C++ implements the parallel and parallel unsequenced policies the same way.
- On Linux with 4 cores, parallel execution is faster than sequential execution by a factor of about 4, while parallel and vectorized execution are again comparable.
How to Return Several Values From a Function in C++
In yet another practical article, Jonathan Boccara talks about how to return several values from a function while making sure no redundant extra copies are made, RVO is applied, and the code looks nice and readable. The article is focused on improving these parameters for a solution that returns a bundle, like an std::pair
or std::tuple
. On the call site, the values are retrieved using structured bindings (C++17) or std::tie
(C++11).
Another question is how to avoid the typical errors when returning values of the same type. If you mix up their order, you might never even notice it. To be on the safe side, use a proper naming scheme, or maybe a struct, or even strong types.
Qt Creator 5.0 Beta
Qt announced the Qt Creator 5.0 Beta build, which includes a very interesting change. For some time now, Qt Creator has relied on the libclang-based code model for providing smart features in C and C++. Now it has moved to Clangd. There is a difference between the two approaches. Libclang is a library, so the language model runs inside the actual IDE application. Clangd, on the other hand, is a daemon which communicates with the IDE via the Language Server Protocol.
One more update in this build is Qt’s support for building and running applications in Docker containers. With it you can create a Docker device in the Device settings and set it as a “Build device” and a “Device” in a Kit.
CMake 3.21 is released
While many of us are still adopting CMake 3.20, which brought many cool things like CMake Presets, Kitware released CMake 3.21. This version works with the MSYS runtime environment, adds a generator for Visual Studio 17 2022, and supports the C17 and C23 standards. Another interesting change is that messages printed to the terminal may now be colored by message type. And finally, CMake Presets has reached version 3 in CMake 3.21.
Hot Reload in Visual Studio 2022
A new preview of Visual Studio 2022 provides Hot Reload for .NET and C++. When you are in a constant loop of editing the code, debugging the resulting app, and editing it again, the speed with which your code changes are applied is important. Normally you stop the debugging process, introduce changes, recompile, and launch the debugger back. With Hot Reload, the loop becomes shortened, as new code changes are applied to the application under the debugger without stopping it. This saves you time on restarting the app, building, and waiting for the breakpoint to be hit again. Hot Reload injects the changes into the app dynamically and doesn’t trigger a full rebuild of the app. The feature is available only for MSBuild C++ projects and the Visual Studio debugger at this time.
CLion 2021.2 is out
The CLion 2021.2 release is aligned with the most recent changes in the C++ ecosystem. It adds static analysis to catch object lifetimes issues (meaning you don’t even need to wait for Herb Sutter’s proposal to be implemented in the compilers), and it introduces support for the brand-new CMake Presets feature. This feature from CMake 3.20 simplifies the app build configuration process. To reach feature-parity across many toolchains and setups, profiling is enabled for remote mode and WSL. Plenty of debugger enhancements have been implemented. And finally, for those who are learning the C++ language or prototyping actively, CLion integrates the Cling interpreter. The integration includes not only Cling sessions built into the editor, but also many code intention actions to work with these sessions and send the corresponding code to them.
And finally, Bjarne Stroustrup on the future of programming
Bjarne Stroustrup, the creator of C++, gave an interview to Yegor Bugayenko. The 1-hour-long YouTube video covers many deep and global questions touching on the goals and evolution of programming languages. Bjarne also talks a lot about his vision for C++. Let us share just a few highlights from the insightful video before you go and watch the whole thing:
- How do you make a language popular? Solve a problem, instead of trying to make a statement. And care about the language in the long term.
- Why is a language committee needed to add features to a language? Because we need a broader view. We need to listen to everyone, but still follow the original goal and defend it.
- The NoCode trend and AI for code improvements and refactorings: will these things replace human beings in the future? AI is good at searching for and identifying abnormal behavior, but it’s still local and limited. Someone has to think about the overall structure and architecture. Also, with automated refactorings, strong guarantees are required. We need to verify that the performance criteria is taken into account and also prove that the code is actually the same after refactoring.
- Is it important to design simpler languages that are still powerful? The solution is not necessarily in the language. The answer for C++ is the C++ Core Guidelines, which can guide you in writing good C++ code. So it’s guidelines, plus a minimum set of libraries to support that, plus static code analysis to enforce the guidelines.
About the authors
Anastasia Kazakova (@anastasiak2512) As a C/C++ software developer in Telecom and Embedded systems, Anastasia was involved in research estimating the broadband capacity of home networks and participated in launching 4G networks. She is now the Product Marketing Manager for JetBrains C++ tools. |
|
Phil Nash (@phil_nash) Phil is the original author of the C++ test framework Catch2. As Developer Advocate at SonarSource he’s involved with SonarQube, SonarLint, and SonarCloud, particularly in the context of C++. He’s also the organizer of C++ London and C++ on Sea, as well as co-host and producer of the cpp.chat podcast. More generally, Phil is an advocate for good testing practices, TDD, and using the type system and functional techniques to reduce complexity and increase correctness. He’s previously worked in Finance and Mobile and offers training and coaching in TDD for C++. |