ISO C++ Committee – Rapperswil 2018 trip report
From the 4th to the 9th of June 2018, Phil Nash and I attended the ISO C++ Committee meeting in beautiful Rapperswil, Switzerland, representing JetBrains. We are continuing our active involvement in developing and standardizing C++ (please read the last trip report for details).
We are now past the middle of the release cycle for C++20. The next meeting in San Diego in November 2018 will mark the feature freeze for new language features – there is not much time left. A lot has happened in this meeting, so let me (with some help from Phil) update you on the most important bits:
- Modules
- Concepts
- Coroutines
- Contracts
- Zero-overhead deterministic exceptions
- Aggregate initialization
- 2D Graphics
- Tooling Study Group
Modules
At the last meeting, we discussed a counter-proposal to the Modules TS: Google’s Atom proposal. Since then, the main authors of the two competing proposals, Gabriel Dos Reis from Microsoft and Richard Smith from Google, joined forces and produced a “merged” compromise proposal, which they now presented here in Rapperswil. This is really promising work towards a solution that might work for most users. However there are still open questions and lack of implementation experience with this new, third version of modules (The original Modules TS being the first and Google’s Atom the second), and so the committee decided that it is not ready yet to be merged into C++. As the time window for new features is closing, it is now very improbable that modules will still make it to C++20, because there are still several unresolved issues and not everyone is happy with the current compromise. We will most likely see an updated Modules TS instead, and have to wait until C++23 to get modules into the standard.
Concepts
Concepts as a language feature will definitely be in C++20. However, there is still a lot of controversy over a potential “terse syntax”. Over the last year, many people have tried (and failed) to find a solution that could gain consensus. In this iteration, we reviewed two competing proposals for such a syntax, Herb Sutter’s P0745 and Bjarne Stroustrup’s P1079. It looks like the committee could live with either, as long as we get some terse syntax, but cannot decide which one is the better. I expect that there will be an updated compromise proposal in San Diego in an attempt to resolve the current stalemate. In the worst case, we will get concepts without a “terse syntax”, which isn’t really that bad after all.
Regardless of the syntax debate, there are some other really good news for concepts: The standard library is now fully conceptified! There’s a new header `<concepts>` which includes a comprehensive concepts library with dozens of concepts such as `Assignable`, `Invocable`, `DefaultConstructible`, `Lockable`, and many others, ready for use. Finally, we can express all those things directly in C++ code, rather than just via wording or type traits metafunctions. It is noteworthy that the concepts all have `PascalCase` names, moving away from the `snake_case` that was consistently used for identifiers in the standard library until now.
Coroutines
Merging the Coroutines TS into C++20 was up for vote at this meeting. While most people want this functionality in C++20, it is still not clear whether the existing TS (also known as “Gorroutines” after its author Gor Nishanov from Microsoft) really is the best technical approach. Geoff Romer from Google presented the new Core Coroutines counter-proposal, which addresses some of the shortcomings of Gorroutines: it better exposes the underlying primitive objects that are close to the hardware and provides more explicit control over heap allocations. However this new proposal is not mature yet: it lacks implementation experience, there are some open design questions, and many were vehemently opposed to the syntax. Forwarding this new proposal did not get the support of, which instead decided to put merging the existing Coroutines TS into C++20 up for vote despite some objections. Unsurprisingly, that vote then failed to get the required consensus in plenary. So after some drama we are now left without coroutines for the time being. I expect more work to happen before Coroutines will be put to the vote again in San Diego.
Contracts
The great news is that Contracts made the mark: we voted the feature into C++20. This feature allows you to annotate functions with pre- and post-conditions, for example like this:
void f(int x, int y) [[expects: x>0]] [[expects: y!=0]] [[ensures result: result > x+y]];
This is not only great for better code documentation, more stable APIs, and finding bugs, but also to give hints to the compiler to generate better optimized code. It is up to the user to either assert on contract failures and diagnose them, let the compiler assume that the conditions are met, or even opt to terminate the program if they are not. This is very flexible and will benefit a wide range of C++ users.
We initially found some technical problems when adding contracts to virtual functions. What do you do if a function overrides another one which has a contract? What if that one comes from a private base? Or an ambiguous base? In the end, we produced wording that works for many cases. Unambiguous and accessible base classes are now only required if the contract condition odr-uses `*this` (so for example postconditions on the return value will not require that the class containing the method being overridden is public). There might be further tweaks to this logic before contracts ship in C++20.
Zero-overhead deterministic exceptions
Over to Phil:
p0709r0 was the main motivation for me attending this particular meeting, as I was involved in the SG14 discussion prior to it becoming a proposal and has been a long term interest of mine. Sadly the full proposal was not discussed this time as it is not targeting C++20 and this week was dedicated to that. A part of it was presented to LEWG (the Library Evolution Working Group), though (since it will impact library design in the future). The results of that were surprising, as we’ll see.
So what is the feature? It’s quite a large, and far-reaching, proposal, so hard to summarize and do it justice. But I’ll try anyway!
The premise is that many developers avoid exceptions. The 2018 C++ Foundation Developers Survey “Lite” found that 52% of those surveyed had exceptions disabled for all or some of their codebases! They typically cite performance, determinism, use of RTTI and use of the heap. This proposal claim to address every one of those concerns in a way that is at least as good as the current alternatives (e.g. return codes or ADT-based error handling) – but built into the language in such a way that they look, and work, similarly to our current (dynamic) exceptions. If that sounds interesting at all, do take the time to read the proposal!
So while the full formal discussion has been pushed back to (hopefully) San Diego, now, there is a lot of community discussion – and a generally positive feeling towards the approach (at least by those that have familiarised themselves with it).
The parts that were presented to LEWG were section 4.2 (true “logic errors” should never be an exception – but rather expressed as a narrow contract and 4.3 (“Treat heap exhaustion specially” – make the currently throwing version of `new` a fatal error instead – this would allow most of the std library to become `noexcept`!). The room voted strongly in favor (almost perfectly unanimous) of both these ideas. That was the part that was surprising.
Relatedly, there was another proposal, from Simon Brand, to add monadic sequencing methods to `std::optional` and `std::expected`. These would allow nicer expression of code that uses ADT-based error handling currently. This also got strong approval for moving forward. I’m in favor of the proposal, but do wonder if it would be necessary if we had static exceptions. ADT-based error handling also came up (again) in the context of coroutines. In fact one of the objections to Gor’s coroutines proposal is the way it bakes the `co_await` name into the language for a feature that could be used more generally than just for async code.
Back to Timur…
Aggregate initialization
Initialization in C++ is confusing and hard. In particular, aggregate initialization suffers from various issues, two of which we tackled this week.
The first is the fact that in C++11/14/17, an aggregate can have user-declared constructors, as long as they are =deleted or =defaulted. This rule is a historic artifact rather than an intentional design, and has some very surprising consequences:
struct X { X() = delete; // don’t instantiate! } int main() { X x{}; // compiles anyway, and instantiates an X }
Confusing code examples like this have been blogged and tweeted many times. But this week, we adopted P1008 for C++20, which finally simplifies the rules and makes the above code illegal: an aggregate now cannot have any user-declared constructors. Yes, this will break some code, but that’s well-justified by the new rules being easier to learn and teach.
The second improvement that we looked at is proposal P0960: allowing aggregate initialization from an argument list in parentheses (currently, it only works with curly braces). The idea is to make the following well-formed:
struct Foo { int i, j, k; } Foo foo(1, 2, 3);
This would be awesome because it would make things like `make_shared` and `emplace` work for aggregates. A majority of the Evolution working group (EWG) is in favor of allowing this; however, at this meeting we could not quite figure out how to specify this new feature correctly without breaking anything. I expect that we will finalize this feature and vote it into C++20 in San Diego.
Other developments
Here are some of the other features that were voted into C++20 at the plenary session on Saturday:
- P0806 Implicit capture of `this` via `[=]` is now deprecated
- P0732 Using class types in non-type template parameters, already approved by EWG in Jacksonville, is now merged into C++20. This one is very exciting as it opens the door to compile-time strings and other metaprogramming techniques.
- P1064 Virtual function calls are now allowed in a constexpr context.
- P0941 Feature test macros are now incorporated into the standard.
- P0892 `explicit(bool)` makes constructors for types like `pair`, `tuple`, and `optional` a bit more sane
- P0887 `std::type_identity` is a small but very useful utility for creating a non-deduced context
- P1023 `constexpr` comparison operators for `std::array`
- P0769 new standard algorithms: `std::shift_left` and `std::shift_right`
- P0556 new numeric tools including a utility `std::ispow2` which efficiently checks whether an integral is a power of two
- P0019 `std::atomic_ref`, allowing atomic operations on non-atomic objects.
- P0458 added a member function `contains(element)` to `map` and `set`, as well as their `multi_` and `unordered_` variants, so you’ll never again have to write this boilerplate:
`if (some_set.find(element) != some_set.end()) …`
2D Graphics
We had an evening session dedicated to the proposal to create a Graphics TS containing a 2D graphics library. Michael McLaughlin and Guy Davidson have been working for years on this library and produced over 100 pages of high-quality wording, complete with a working open-source reference implementation that runs on all major desktop platforms and many working example apps. Some pieces like text rendering and windowing are still missing from that library, but overall it is a very comprehensive and impressive piece of work. The committee was initially very divided on the issue – some advocating to add it, as it would be a great asset to draw more users into C++, others being vehemently opposed because they thought such a TS would eat up too much committee time that was needed elsewhere, and that graphics would not “belong” into the standard. Ultimately, there was no consensus to proceed with the graphics proposal. We might revisit this decision in a few years, but for now, work on this topic seems to be shelved, and it looks like C++ will not see any graphics functionality in the foreseeable future. I understand the arguments made, but overall I believe that this is a loss, and I feel sorry for the authors who invested so much time in the project – we should have had this conversation much earlier.
SG15: Tooling Study Group
Over to Phil again:
Both Timur and I were very interested in the new study group for Tooling, SG15 (given that JetBrains is almost exclusively about tooling). However, while not entirely irrelevant, the current direction of SG15 doesn’t yet have a lot for us to say. The main focus right now, and to which the evening session on Friday was dedicated, is on package management – and how involved the standards bodies should be in that. There have been increasingly vocal calls for a standards “blessed” package manager – whether defined by the standard itself, or just following a standard specification or API. The reasons for that are many, but are centered on just how complicated and difficult it is, currently, to get anything that has any sort of third party dependency up and running in C++ – especially when compared to most other modern programming language ecosystems.
Right now there are a few well known package managers, all with their own takes on the problem, but with a lot of overlap too (both in functionality and content). We had representation at the meeting from build2 and Microsoft’s vcpkg. We also had members of the Conan team on call, and some input from them.
During the session we had a demo from build2, showing how much simpler their tools make the problem – a taste of how it could be. I thought this went quite well, although it was pointed out that the real test is with second level dependencies, and this demo only showed one level.
The concluding sentiment was that the standard should not be seeking to dictate a particular package manager, nor even specify how an implementation should look or work. However, the problem was recognized, and the community leadership of those in the room called upon to help work out a cohesive story within the community. Package manager maintainers were encouraged to work together to interoperate, converge on common functionality and interfaces, and present a more viable way forward from the outside. Some were left scratching their heads with this result, but I think this was probably the most we could hope for. From here it’s up to us (the community) to move things forward.
Conclusion
As always, it was a very intense and exhausting meeting, but also very productive and interesting. And we did have one evening off, with a dinner at Rapperswil castle – and some of us even found a piano!
Even though modules will probably not make it into C++20, the rest is coming together rather nicely. Oh, and did I mention that we’ll get Ranges, too? Overall, C++20 will be the biggest release in terms of new features since C++11 came out.
Stay tuned for the next report from San Diego, California, in November!