Botond Ballo: Trip Report: C++ Standards Meeting in Belfast, November 2019 |
Project | What’s in it? | Status |
C++20 | See below | On track |
Library Fundamentals TS v3 | See below | Under development |
Concepts | Constrained templates | In C++20 |
Parallelism TS v2 | Task blocks, library vector types and algorithms, and more | Published! |
Executors | Abstraction for where/how code runs in a concurrent context | Targeting C++23 |
Concurrency TS v2 | See below | Under active development |
Networking TS | Sockets library based on Boost.ASIO | Published! Not in C++20. |
Ranges | Range-based algorithms and views | In C++20 |
Coroutines | Resumable functions (generators, tasks, etc.) | In C++20 |
Modules | A component system to supersede the textual header file inclusion model | In C++20 |
Numerics TS | Various numerical facilities | Under active development |
C++ Ecosystem TR | Guidance for build systems and other tools for dealing with Modules | Under active development |
Contracts | Preconditions, postconditions, and assertions | Under active development |
Pattern matching | A match -like facility for C++ |
Under active development |
Reflection TS | Static code reflection mechanisms | Publication imminent |
Reflection v2 | A value-based constexpr formulation of the Reflection TS facilities |
Under active development |
Metaclasses | Next-generation reflection facilities | Early development |
A few links in this blog post may not resolve until the committee’s post-meeting mailing is published (expected within a few days of November 25, 2019). If you encounter such a link, please check back in a few days.
Last week I attended a meeting of the ISO C++ Standards Committee (also known as WG21) in Belfast, Northern Ireland. This was the third and last committee meeting in 2019; you can find my reports on preceding meetings here (July 2019, Cologne) and here (February 2019, Kona), and previous ones linked from those. These reports, particularly the Cologne one, provide useful context for this post.
At the last meeting, the committee approved and published the C++20 Committee Draft (CD), a feature-complete draft of the C++20 standard which includes wording for all of the new features we plan to ship in C++20. The CD was then sent out to national standards bodies for a formal ISO ballot, where they have the opportunity to file technical comments on it, called “NB (national body) comments”.
We have 10-15 national standards bodies actively participating in C++ standardization, and together they have filed several hundred comments on the CD. This meeting in Belfast was the first of two ballot resolution meetings, where the committee processes the NB comments and approves any changes to the C++20 working draft needed to address them. At the end of the next meeting, a revised draft will be published as a Draft International Standard (DIS), which will likely be the final draft of C++20.
NB comments typically ask for bug and consistency fixes related to new features added to C++20. Some of them ask for fixes to longer-standing bugs and consistency issues, and some for editorial changes such as fixes to illustrative examples. Importantly, they cannot ask for new features to be added (or at least, such comments are summarily rejected, though the boundary between bug fix and feature can sometimes be blurry).
Occasionally, NB comments ask for a newly added feature to be pulled from the working draft due to it not being ready. In this case, there were comments requesting that Modules and Coroutines (among other things) be postponed to C++23 so they can be better-baked. I’m pleased to report that no major features were pulled from C++20 at this meeting. In cases where there were specific technical issues with a feature, we worked hard to address them. In cases of general “this is not baked yet” comments, we did discuss each one (at length in some cases), but ultimately decided that waiting another 3 years was unlikely to be a net win for the community.
Altogether, over half of the NB comments have been addressed at this meeting, putting us on track to finish addressing all of them by the end of the next meeting, as per our standardization schedule.
While C++20 NB comments were prioritized above all else, some subgroups did have time to process C++23 proposals as well. No proposals were merged into the C++23 working draft at this time (in fact, a “C++23 working draft” doesn’t exist yet; it will be forked from C++20 after the C++20 DIS is published at the end of the next meeting).
A few updates to the committee’s structure and how it operates:
This blog post will be a bit different from previous ones. I was asked to chair the Evolution Working Group Incubator (EWG-I) at this meeting, which meant that (1) I was not in the Evolution Working Group (EWG) for most of the week, and thus cannot report on EWG proceedings in as much detail as before; and (2) the meeting and the surrounding time has been busier for me than usual, leaving less time for blog post writing.
As a result, in this blog post, I’ll mostly stick to summarizing what happened in EWG-I, and then briefly mention a few highlights from other groups. For a more comprehensive list of what features are in C++20, what NB comment resolutions resulted in notable changes to C++20 at this meeting, and which papers each subgroup looked at, I will refer you to the excellent collaborative Reddit trip report that fellow committee members have prepared.
EWG-I (pronounced “oogie” by popular consensus) is a relatively new subgroup, formed about a year ago, whose purpose is to give feedback on and polish proposals that include core language changes — particularly ones that are not in the purview of any of the domain-specific subgroups, such as SG2 (Modules), SG7 (Reflection), etc. — before they proceed to EWG for design review.
EWG-I met for two and a half days at this meeting, and reviewed 17 proposals. All of this was post-C++20 material.
I’ll go through the proposals that were reviewed, categorized by the review’s outcome.
The following proposals were considered ready to progress to EWG in their current state:
bool
. This proposal relaxes a recently added restriction which requires an explicit conversion from integer types to bool
in certain contexts. The motivation for the restriction was noexcept()
, to remedy the fact that it was very easy to accidentally declare a function as noexcept(f())
(which means “the function is noexcept
if f()
returns a nonzero value”) instead of noexcept(noexcept(f()))
(which means “the function is noexcept
if f()
doesn’t throw”), and this part of the restriction was kept. However, the proposal argued there was no need for the restriction to also cover static_assert
and if constexpr
.auto [...x] = f();
, where f()
is a function that returns a tuple or other decomposable object, and x
is a newly introduced pack of bindings, one for each component of the object; the pack can then be expanded as x...
just like a function parameter pack.std
(or std
followed by a number) for future standardization.reinterpret_cast
ing its address to char*
.= relocates
in places of = default
. This generates the same implementation as for a defaulted move constructor, but the programmer additionally guarantees to the compiler that it can safely optimize a move + destructing the old object, into a memcpy
of the bytes into the new location, followed by a memcpy
of the bytes of a default-constructed instance of the type into the old location. This essentially allows compilers to optimize moves of many types (such as std::shared_ptr
), as well as of arrays / vectors of such types, into memcpy
s. Currently, only types which have an explicit = relocates
move constructor declaration are considered relocatable in this way, but the proposal is compatible with future directions where the relocatability of a type is inferred from that of its members (such as in this related proposal).For the following proposals, EWG-I suggested specific revisions, or adding discussion of certain topics, but felt that an additional round of EWG-I review would not be helpful, and the revised paper should go directly to EWG:
fiber_context
– fibers without scheduler. This is the current formulation of “stackful coroutines”, or rather a primitive on top of which stackful coroutines and other related things like fibers can be built. It was seen by EWG-I so that we can brainstorm possible interactions with other language features. TLS came up, as discussed in more detail in the EWG section.operator?:
overloadable. See the paper for motivations, which include SIMD blend operations and expression template libraries. The biggest sticking point here is we don’t yet have a language mechanism for making the operands lazily evaluated, the way the built-in operator behaves. However, not all use cases want lazy evaluation; moreover, the logical operators (&&
and ||
) already have this problem. EWG-I considered several mitigations for this, but ultimately decided to prefer an unrestricted ability to overload this operator, relying on library authors to choose wisely whether or not to overload it.__builtin_assume()
and similar facilities for giving the compiler a hint it can use for optimization purposes. EWG-I expressed a preference for an attribute-based ([[assume(expr)]]
) syntax.Note that almost all of the proposals that were forwarded to EWG have been seen by EWG-I at a previous meeting, sometimes on multiple occasions, and revised since then. It’s rare for EWG-I to forward an initial draft (“R0”) of a proposal; after all, its job is to polish proposals and save time in EWG as a result.
The following proposals were forwarded to a domain-specific subgroup:
std::function
(which a type-erased wrapper for callable objects) can easily be built for any interface. EWG-I forwarded this to the Reflection Study Group (SG7) because the primary core language facility involves synthesizing a new type (the proxy / wrapper) based on an existing one (the interface). EWG-I also recommended expressing the interface as a regular type, rather than introducing a new facade
entity to the language.For the following proposals, EWG-I gave the author feedback, but did not consider it ready to forward to another subgroup. A revised proposal would come back to EWG-I.
No proposals were outright rejected at this meeting, but the nature of the feedback did vary widely, from requesting minor tweaks, to suggesting a completely different approach to solving the problem.
operator=
returns lvalue-ref on an rvalue. This attempts to rectify a long-standing inconsistency in the language, where operator=
for a class type can be called on temporaries, which is not allowed for built-in types; this can lead to accidental dangling. EWG-I agreed that it would be nice to resolve this, but asked the author to assess how much code this would break, so we can reason about its feasibility.static_assert(false)
in a dependent context fails eagerly, rather than being delayed until instantiation. The proposal introduces a new syntax, static_assert(false)
, where T
is some template parameter that’s in scope, for the delayed behaviour. EWG-I liked the goal, but not the syntax. Other approaches were discussed as well (such as making static_assert(false)
itself have the delayed behaviour, or introducing a static_fail()
operator), but did not have consensus.tuple
and variant
, as well as making many compile-time programming tasks much easier. A lot of the feedback concerned whether packs should become first-class language entities (a “language tuple” of sorts, as previously proposed), or remain closer to their current role as depenent constructs that only become language entities after expansion.eval()
like mechanism, the proposal aims to focus on the ability to instantiate some templates at runtime. EWG-I gave feedback related to syntax, error handling, restricting runtime parameters to non-types, and consulting the Reflection Study Group.Having spent time in both EWG and EWG-I, one difference that’s apparent is that EWG is the tougher crowd: features that make it successfully through EWG-I are often still shot down in EWG, sometimes on their first presentation. If EWG-I’s role is to act as a filter for EWG, it is effective in that role already, but there is probably potential for it to be more effective.
One dynamic that you often see play out in the committee is the interplay between “user enthusiasm” and “implementer skepticism”: users are typically enthusiastic about new features, while implementers will often try to curb enthusiasm and be more realistic about a feature’s implementation costs, interactions with other features, and source and ABI compatibility considerations. I’d say that EWG-I tends to skew more towards “user enthusiasm” than EWG does, hence the more permissive outcomes. I’d love for more implementers to spend time in EWG-I, though I do of course realize they’re in short supply and are needed in other subgroups.
As mentioned, I didn’t spend as much time in EWG as usual, but I’ll call out a few of the notable topics that were discussed while I was there.
As with all subgroups, EWG prioritized C++20 NB comments first.
operator==
, and instead having template argument equivalence be based on a structural identity, essentially a recursive memberwise comparison. This allows a larger category of types to be NTTPs, including unions, pointers and references to subobjects, and, notably, floating-point types. For class types, only types with public fields are allowed at this time, but future directions for opting in types with private fields are possible.std::is_constant_evaluated()
by introducing a new syntactic construct if consteval
was discussed at length but rejected. The feature may come back in C++23, but there are enough open design questions that it’s too late for C++20.constexpr
and consteval
was approved.Foo Bar
can be either a constrained type template parameter (if Foo
names a concept) or a non-type template parameter (if Foo
names a type). The compiler knows which by looking up Foo
, but a reader can’t necessarily tell just by the syntax of the declaration. A comment proposed resolving this by changing the syntax to Foo auto Bar
for the type parameter case (similar to the syntax for abbreviated function templates). There was no consensus for this change; a notable counter-argument is that the type parameter case is by far the more common one, and we don’t want to make the common case more verbose (and the non-type syntax can’t be changed because it’s pre-existing).Concept
can also mean two different things: a type constraint (which is satisifed by a type T
if Concept
is true), or an expression which evaluates Concept
applied to the single argument X
. The comment suggested disambiguating by e.g. changing the first case to Concept<, X>
, but there was no consensus for this either.Having gotten through all Evolutionary NB comments, EWG proceeded to review post-C++20 material. Most of this had previously gone through EWG-I (you might recognize a few that I mentioned above because they went through EWG-I this week).
std::fiber_context
– stackful context switching. This was discussed at some length, with at least one implementer expressing significant reservations due to the feature’s interaction with thread-local storage (TLS). Several issues related to TLS were raised, such as the fact that compilers can cache pointers to TLS across function calls, and if a function call executes a fiber switch that crosses threads (i.e. the fiber is resumed on a different OS thread), the cache becomes invalidated without the compiler having expected that; addressing this at the compiler lever would be a performance regression even for code that doesn’t use fibers, because the compiler would need to assume that any out of line function call could potentially execute a fiber switch. A possible alternative that was suggested was to have a mechanism for a user-directed kernel context switch that would allow coordinating threads of execution (ToEs) in a co-operative way without needing a distinct kind of ToE (namely, fibers).a < b
to a <=> b < 0
), and allow other kinds of rewriting, such as rewriting a += b
to a = a + b
. EWG felt any such facility should be strictly opt-in (e.g. you could give you class an operator+=(...) = default
to opt into this rewriting, but it wouldn’t happen by default), with the exception of rewriting a->b
to (*a).b
(and the less common a->*b
to (*a).*b
) which EWG felt could safely happen by default.tag_invoke
. This concerns making it easier to write robust customization points for library facilities. There was a suggestion of trying to model the desired operations more directly in the language, and EWG suggested exploring that further.template void foo(T...);
to mean “foo is a function template that takes zero or more parameters, all of the same type T
“. There were two main arguments against this. First, it would introduce a novel interpretation of template-ids (foo
no longer names a single specialization of foo
, it names a family of specializations, and there’s no way to write a template-id that names any individual specialization). The objection that seems to have played the larger role in the proposal’s rejection, however, is that C allows things like (int...)
to be an alternative way of writing (int, ...)
(meaning, an int
parameter followed by C-style variadic parameters), and, while this was never allowed in C++, compilers allow it as an extension, and apparently a lot of old C++ code uses it. The author went to some lengths to analyze a large dataset of open-source C++ code for occurrences of such use (of which there were vanishingly few), but EWG felt this wasn’t representative of the majority of C++ code out there, most of which is proprietary.constexpr_report
, you could just use printf
(or whatever you’d use for runtime output). The Circle programming language was pointed to as prior art in this area. SG7 did not make a decision about this paradigm shift, just encouraged exploration of it.web_view
was discussed in SG13 (I/O), and I relayed some of Mozilla’s more recent feedback; the proposal continues to enjoy support in this subgroup. The Library Evolution Incubator did not get a chance to look at it this week.The next meeting of the Committee will be in Prague, Czech Republic, the week of February 10th, 2020.
This was an eventful and productive meeting, as usual, with the primary accomplishment being improving the quality of C++20 by addressing national body comments. While C++20 is feature-complete and thus no new features were added at this meeting, we did solidify the status of recently added features such as Modules and class types as non-type template parameters, greatly increasing the chances of these features remaining in the draft and shipping as part of C++20.
There is a lot I didn’t cover in this post; if you’re curious about something I didn’t mention, please feel free to ask in a comment.
In addition to the collaborative Reddit report which I linked to earlier, here are some other trip reports of the Belfast meeting that you could check out:
Комментировать | « Пред. запись — К дневнику — След. запись » | Страницы: [1] [Новые] |