GotW #98 Solution: Assertion levels (Difficulty: 5/10)

This special Guru of the Week series focuses on contracts. We covered basic assertions in GotW #97… but not all asserted conditions are created equal.

Given some assertion facility that can be used like this:

MyAssert( condition );  // expresses that ‘condition’ must be true

1. Give one example each of an asserted condition whose run-time evaluation is:

a) super cheap

Without resorting to constexpr expressions, it’s hard to find one cheaper than the one we saw in GotW #97 Example 3, which we can simplify down to this:

// Example 1(a): A dirt cheap assertion (from GotW #97 Example 3)

int min = /* some computation */;
int max = /* some other computation */;
MyAssert( min <= max );

This is always going to be dirt cheap. Not only is the integer comparison operation cheap, but min and max are already being accessed by this function and so they’re already “hot” in registers or cache.

b) arbitrarily expensive

“A condition that’s expensive? That sounds pretty easy,” you might say, and you’re right!

One commonly cited example is is_sorted. Just to emphasize how expensive it can be both in absolute terms and also relative to the program’s normal execution, let’s put it inside a well-known function… this is a precondition, but we’ll write it as an assertion in the function body for now which doesn’t affect this question: [1]

// Example 1(b): An arbitrarily expensive assertion

template <typename Iter, typename T>
bool binary_search( Iter begin, Iter end, const T& value ) {
    MyAssert( is_sorted(begin, end) );
    // ...
}

Checking that all the container’s elements are in ascending order requires visiting them all, and that O(N) linear complexity is arbitrarily expensive when the container’s size N can be arbitrarily large.

The icing on the cake: In this example, just evaluating the assertion requires doing more work than the entire function it appears in, which is only O(log N) complexity!

On a humorous note, O(N) remains O(N) no matter how hard we try to make it efficient:

MyAssert( std::is_sorted( std::execution::par, begin(s), end(s) ) );
                  // still O(N) arbitrarily expensive, but good try!

2. What does the answer to Question 1 imply for assertion checking? Explain.

We want a way to enable checking for some assertions but not others in the same code, because we may not always be able to afford expensive checks. That’s true whether we’re enabling checking at test time (e.g., on the developer’s machine) or at run time (e.g., in production).

Some conditions are so expensive that we may never check them without a good reason, even in testing. Example 1(b)’s is_sorted is a great example: You probably won’t ever enable it in production, and likely not by default in testing, except by explicitly turning it on during a bug hunt after enabling checking for cheaper assertions wasn’t enough or pointed at this data structure for deeper investigation. [2]

Other conditions are so cheap we’ll probably always check them absent a good reason not to, even in production. Example 1(a)’s min <= max is at this other end of the scale: It’s so dirt cheap to check that it’s unlikely we’ll ever have a performance reason to disable it. [2]

So it makes perfect sense that if Examples 1(a) and 1(b) appear in the same source file, the developer will want to enable checking for 1(b)’s assertion only by some kind of manual override to explicitly request it, and enable checking for 1(a)’s assertion all the time.

3. Give an example of an asserted condition that is in general impossible to evaluate, and so cannot be checked.

One common example is is_reachable for pointers or other iterators, to say that if we increment an iterator enough times we can make it equal to (refer to the same object as) a second iterator:

// Example 3: Very reasonable, but generally not checkable

auto first_iterator = /*...*/;
auto last_iterator  = /*...*/;
MyAssert( is_reachable(first_iterator, last_iterator) );
std::find( first_iterator, last_iterator, value );

In general, there’s no way to write is_reachable. You could try to increment first_iterator repeatedly until it becomes equal to last_iterator, but when the latter is not reachable that might never happen and even just trying would often be undefined behavior.

You might be tempted to test is_reachable using std::distance:

MyAssert( std::distance(first_iterator, last_iterator) >= 0 );

… but that would be horribly wrong. Can you see why?

Take a moment to think about it before continuing…

… okay. The answer is that std::distance itself requires that last_iterator is reachable from first_iterator, otherwise it’s undefined behavior. So this maybe-tempting-looking alternative actually assumes what we want to prove, and so it’s not useful for this purpose. (GotW #100 will consider in detail the general question of preconditions of contract subexpressions, which covers examples like this one.)

Can these kinds of conditions still be useful?

Yes. In practice, these kinds of conditions spell out “this is a formal comment.” Static analyzers and other tools may be able to test such a condition in a subset of cases; for example, at some call sites an analyzer may be able to infer statically that two iterators point into different containers and so one isn’t reachable from the other. Alternatively, the tools might support special pseudofunction names that they recognize when you use them in assertion expressions to give information to the tool. So the conditions can still be useful, even if they can’t generally be checked the normal way, by just evaluating them and inspecting the result.

4. How do these questions help answer:

a) what “levels” of asserted conditions we should be able to express?

There’s a wide spectrum of “expensiveness” of assertion conditions, ranging from cheap to arbitrarily high to even impossible. In the post-C++20 contracts proposal at [3], this is partly captured by the proposed basic levels of default, audit, and axiom, roughly intended to represent “cheap,” “expensive,” and “impossible” respectively.

Because we need to check these with different frequencies (or not at all), we need a way to enable and disable checking for subsets of assertions independently, even when they’re in the same piece of code.

GUIDELINE: Distinguish between (at least) “cheap,” “expensive,” and “impossible” to evaluate assertions. If you develop your own assertion system for in-house use, support enabling/disabling at least these kinds of assertions independently. [1] I say “at least” because what’s “expensive” is subjective and will vary from program to program, from team to team… and even within a program from your code to your third-party library’s code that you’re calling. Having just two preset “cheap” and “expensive” levels is minimal, but useful.

b) why the assertions we can “practically” write are a subset of all the ones we might “ideally” like to write?

It can be useful to distinguish between all ideal assertions, meaning everything that has to be true at various points in the program for it to run correctly, and the practical assertions, meaning the subset of those that can be reasonably expressed as a C++ expression and checked. In GotW #97 question 3, part of the solution says that “if an assertion fails” then…

there is a program bug, possibly in the assertion itself. The first place to look for the bug is in this same function, because if prior contracts were well tested then likely this function created the first unexpected state.

If we could write all ideal assertions, and exercise all control flow and data flow during testing, then a failed assertion would definitely mean a bug in the same function where it was written. Because we realistically can’t write and exercise them all, though, we could be observing a secondary effect from a bug that happened earlier. Still, this function is the first place to start looking for the bug.

Notes

[1] Upcoming GotWs will cover preconditions and violation handling. For handlers, we’ll cover additional distinctions such as categories of violations (e.g., to distinguish safety-related checks vs. other checks).

[2] As always, any checks left on in production would often install a different handler, such as a log-and-continue handler rather than a terminating handler; see GotW #97 Question 4, and note [1].

[3] G. Dos Reis, J. D. Garcia, J. Lakos, A. Meredith, N. Myers, and B. Stroustrup. “P0542: Support for contract based programming in C++” (WG21 paper, June 2018).

Acknowledgments

Thank you to the following for their feedback on this material: Joshua Berne, Guy Davidson, J. Daniel Garcia, Gábor Horváth, Maciej J., Andrzej Krzemieński.

GotW #98: Assertion levels (Difficulty: 5/10)

This special Guru of the Week series focuses on contracts. We covered basic assertions in GotW #97… but not all asserted conditions are created equal.

JG Questions

Given some assertion syntax:

SomeAssert( condition );  // expresses that ‘condition’ must be true

1. Give one example each of an asserted condition whose run-time evaluation is:

a) super cheap

b) arbitrarily expensive

Guru Questions

2. What does the answer to Question 1 imply for assertion checking? Explain.

3. Give an example of an asserted condition that is in general impossible to evaluate, and so cannot be checked. Can these kinds of conditions still be useful?

4. How do these questions help answer:

a) what “levels” of asserted conditions we should be able to express?

b) why the assertions we can “practically” write are a subset of all the ones we might “ideally” like to write?

GotW #97 Solution: Assertions (Difficulty: 4/10)

Assertions have been a foundational tool for writing understandable computer code since we could write computer code… far older than C’s assert() macro, they go back to at least John von Neumann and Herman Goldstine (1947) and Alan Turing (1949). [1,2] How well do we understand them… exactly?

1. What is an assertion, and what is it used for?

An assertion documents the expected state of specific program variables at the point where the assertion is written, in a testable way so that we can find program bugs — logic errors that have led to corrupted program state. An assertion is always about finding bugs, because something the programmer thought would always be true was found to actually be false (oops).

For example, this line states that the program does not expect min to exceed max, and if it does the code has a bug somewhere:

// Example 1: A sample assertion

assert( min <= max );

If in this example min did exceed max, that would mean we have found a bug and we need to go fix this code.

GUIDELINE: Assert liberally. [3] The more sure you are that an assertion can’t be false, the more valuable it is if it is ever found to be false. And in addition to finding bugs today, assertions verify that what you believe is “obviously true” and wrote correctly today actually stays true as the code is maintained in the future.

GUIDELINE: Asserted conditions should never have side effects on normal execution. Assertions are only about finding bugs, not doing program work. And asserted conditions only evaluated if they’re enabled, so any side effects won’t happen when they’re not enabled; they might sometimes perform local side effects, such as to do logging or allocate memory, but the program should never rely on them happening or not happening. For example, adding an assertion to your code should never make a logically “pure” function into an impure function. (Note that “no side effects on normal execution” is always automatically true for violation handlers even when an assertion system such as proposed in [4] allows arbitrary custom violation handlers to be installed, because those are executed only if we discover that we’re in a corrupted state and so are already outside of normal execution. [5] For conditions, it’s up to us to make sure it’s true.)

2. C++20 supports two main assertion facilities… For each one, briefly summarize how it works, when it is evaluated, and whether it is possible for the programmer to specify a message to be displayed if the assertion fails.

assert

The C-style assert is a macro that is evaluated at execution time (hopefully enabled at least during your unit testing! see question 4) if NDEBUG is not set. The condition we pass it must be a compilable boolean expression, something that can be evaluated and converted to bool.

It doesn’t directly support a separate message string, but implementations will print the failed condition itself, and so a common technique is to embed the information in the condition itself in a way that doesn’t affect the result. For example, a common idiom is to append &&"message"  (a fancy way of saying &&true):

// Example 2(a): A sample assert() with a message

assert( min <= max
        && "BUG: argh, miscalculated min and max again" );

static_assert

The C++11 static_assert is evaluated at compile time, and so the condition has to be a “boolean constant expression” that only refers to compile-time known values. For example:

// Example 2(b): A sample static_assert() with a message

static_assert( sizeof(int) >= 4,
               "UNSUPPORTED PLATFORM: int must be at least 4 bytes" );

It has always supported a message string, and C++17 made the message optional.

Bonus: [[assert: ?

Looking forward, a proposed post-C++20 syntax for assertions would support it as a language feature, which has a number advantages including that it’s not a macro. [4] This version would be evaluated at execution time if checking is enabled. Currently that proposal does not have an explicit provision for a message and so programmers would use the && "message" idiom to add a message. For example:

// Example 2(c): An assertion along the lines proposed in [4]

[[assert( min <= max
          && "BUG: argh, miscalculated min and max again" )]] ;

3. If an assertion fails, what does that indicate, and who is responsible for fixing the failure?

A failed assertion means that we checked and found the tested variables to be in an unexpected state, which means at least that part of the program’s state is corrupt. Because the program should never have been able to reach that state, two things are true:

  • There is a program bug, possibly in the assertion itself. The first place to look for the bug is in this same function, because if prior contracts were well tested then likely this function created the first unexpected state. [5]
  • The program cannot recover programmatically by reporting a run-time error to the calling code (via an exception, error code, or similar), because by definition the program is in a state it was not designed to handle, so the calling code isn’t ready for that state. It’s time to terminate and restart the program. (There are advanced techniques that involve dumping and restarting an isolated portion of possibly tainted state, but that’s a system-level recovery strategy for an impossible-to-handle fault, not a handling strategy for run-time error.) Instead, the bug should be reported to the human developer who can fix the bug.

GUIDELINE: Don’t use assertions to report run-time errors. Run-time errors should be reported using exceptions, error codes, or similar. For example, don’t use an assertion to check that a remote host is available, or that the user types valid input. Yes, std::logic_error was originally created to report bugs (logic errors) using an exception, but this is now widely understood to be a mistake; don’t follow that pattern.

Referring to this example:

// Example 3

void f() {
    int min = /* some computation */;
    int max = /* some other computation */;

    // still yawn more yawn computation

    assert( min <= max );         // A

    // ...
}

In this code, if the assertion at line A is false, that means what the function actually did before the assertion doesn’t match what the assertion condition expected, so there is a bug somewhere in this function — either before or within the assertion.

This demonstrates why assertions are primarily about eliminating bugs, which is why we test…

4. Are assertions primarily about checking at compile time, at test time, or at run time? Explain.

Assertions are primarily about finding bugs at test time. But assertions can also be useful at other times because of some well-known adages: “the sooner the better,” “better late than never,” and “never [will] I ever.”

Bonus points for pointing out that there is also a fourth time in the development cycle I didn’t list in the question, when assertions can be profitably checked. Here they are:

Of course this can be even more nuanced. For example, you might make different decisions about enabling assertions if your “run time” is an end user’s machine, or a server farm, or a honeypot. Also, checking isn’t free and so you may enable run-time checking for severe classes of bugs but not others, such as that an operating system component may require checking in production for all out-of-bounds violations and other potential security bugs, but not non-security classes of bugs.

First, “the sooner the better”: It’s always legal and useful to find bugs as early as possible. If we can find a bug even before actually executing a compiled test, then that’s wonderful. This is a form of shift-left. We love shift-left. There are two of these times in the graphic:

  • (Earliest, best) Edit time: By using a static analysis tool that is aware of assert and can detect some violations statically, you can get some diagnostics as you’re writing your code, even before you try to compile it! Note that to recognize the assert macro, you want to run the static analyzer in debug mode; analyzers that run after macro substitution won’t see an assert condition when the code is set to make release builds since the macro will expand to nothing. Also, usually this kind of diagnostic uses heuristics and works on a best-effort basis that catches some mistakes while not diagnosing others that look similar. But it does shift some diagnostics pretty much all the way left to the point where you’re actually writing the code, which is great when it works… and you still always have the next three assertion checking times available as a safety net.
  • (Early) Compile time: If a bug that depends only on compile-time information can be detected at compile time even before actually executing a compiled test, then that’s wonderful. This is one reason static_assert exists: so that we can express tests that are guaranteed to be performed at compile time.

Next, the primary target:

  • Test time: This is the main time tests are executed. It can be during developer-machine unit testing, regression testing, build-lab integration testing, or any other flavor of testing. The point is to find bugs before they escape into production, and inform the programmer so they can fix their bug.

Finally, “better late than never” (safety net) or “never [will] I ever” (intolerable severe condition):

  • (Late) Run time: Even after we release our code, it can be useful to have a way to enable checking at run time to at least log-and-continue (e.g., using facilities such as [6] or [7]). One motivation is to know if a bug made it through testing and out into the field and get better late-debug diagnostics; this is sometimes called shift-right but I think of it as much as being about belt-and-suspenders. Another motivation is to ensure that severe classes of bugs ensure execution will halt outright if we cannot tolerate continuing after such a fault is detected.

Importantly, in all cases the motivation is still debugging: Findings bugs early is still debugging, just better (sooner and less expensive). And finding bugs late that escaped into production is still debugging, just worse (later and more expensive). Each of these times is a successive safety net for bugs that make it past the earlier times.

Because at run time we may want to log a failed assertion, our assertion violation handler should be able to USE-A logging system, but the relationship really is USES-A. An assertion violation handling system IS-NOT-A general-purpose logging system, and so a contracts language feature shouldn’t be designed around such a goal. [5]

Finally, speaking of run time: Note that it can be useful to write an assertion, and also write code that does some handling if the assertion is false. Here’s an example from [8]:

// Example 4: Defense in depth

int DoSomething(int x) {

    assert( x != 0 && "x should be nonzero" ); // finds bug, if checked
    if( x == 0 ) {
        return INVALID_COOKIE; // robustness fallback, if not checked
    }

    // do useful work

}

You might see this pattern written interleaved as follows to avoid duplicating the condition, and this is one of the major patterns that leads to writing assert(!"message"):

    if( x == 0 ) {
        assert( !"x should be nonzero" ); // finds bug, if checked
        return INVALID_COOKIE; // robustness fallback, if not checked
    }

At first this may look like it’s conflating the distinct “bug” and “error” categories we saw in Question 3’s table. But that’s not the case at all, it’s actually deliberately using both categories to implement “defense in depth”: We assert something in testing to minimize actual occurrences, but then in production still provide fallback handling for robustness in case a bug does slip through, for example if our test datasets didn’t exercise the bug but in production we hit some live data that does.

Notes

With thanks to Wikipedia for the first two references.

[1] H. H. Goldstine and J. von Neumann. “Planning and Coding of problems for an Electronic Computing Instrument” (Report on the Mathematical and Logical Aspects of an Electronic Computing Instrument, Part II, Volume I, p. 12; Institute for Advanced Study, April 1947).

[2] Alan Turing. “Checking a Large Routine” (Report of a Conference on High Speed Automatic Calculating Machines, pp. 67-9, June 1949).

[3] H. Sutter and A. Alexandrescu. C++ Coding Standards (Addison-Wesley, 2004). Item 68, “Assert liberally to document internal assumptions and invariants.”

[4] G. Dos Reis, J. D. Garcia, J. Lakos, A. Meredith, N. Myers, and B. Stroustrup. “P0542: Support for contract based programming in C++” (WG21 paper, June 2018). To keep it as legal compilable (if unenforced) C++20 for this article I modified the syntax from : to ( ). That’s not a statement of preference, it’s just so the examples can compile today to make them easier to check.

[5] Upcoming GotWs will cover postconditions, preconditions, invariants, and violation handling.

[6] G. Melman. spdlog: Fast C++ logging library (GitHub).

[7] Event Tracing for Windows (ETW) (Microsoft, 2018).

[8] H. Sutter. “P2064: Assumptions” (WG21 paper, 2020).

Acknowledgments

Thank you to the following for their comments on drafts of this article: Joshua Berne, Gábor Horváth, Andrzej Krzemieński, Andrew Sutton. Thanks also to Reddit user “evaned” and Anton Dyachenko for additional feedback.

GotW #97: Assertions (Difficulty: 4/10)

Assertions have been a foundational tool for writing understandable computer code since we could write computer code… far older than C’s assert() macro, they go back to at least John von Neumann and Herman Goldstine (1947) and Alan Turing (1949). [1,2] How well do we understand them… exactly?

[Update: On second thought, I’ll break the “assertions” and “postconditions” into two GotWs. This GotW has the assertion questions, slightly reordered for flow, and GotW #99 will cover postconditions.]

JG Questions

1. What is an assertion, and what is it used for?

2. C++20 supports two main assertion facilities:

  • assert
  • static_assert

For each one, briefly summarize how it works, when it is evaluated, and whether it is possible for the programmer to specify a message to be displayed if the assertion fails.

Guru Questions

3. If an assertion fails, what does that indicate, and who is responsible for fixing the failure? Refer to the following example assertion code in your answer.

void f() {
    int min = /* some computation */;
    int max = /* some other computation */;

    // still yawn more yawn computation

    assert (min <= max);         // A

    // ...
}

4. Are assertions primarily about checking at compile time, at test time, or at run time? Explain.

Notes

Thanks to Wikipedia for pointing out these references.

[1] H. H. Goldstine and J. von Neumann. “Planning and Coding of problems for an Electronic Computing Instrument” (Report on the Mathematical and Logical Aspects of an Electronic Computing Instrument, Part II, Volume I, p. 12; Institute for Advanced Study, April 1947.)

[2] Alan Turing. “Checking a Large Routine” (Report of a Conference on High Speed Automatic Calculating Machines, pp. 67-9, June 1949).

Firsts in 2020 (or, A little dose of good news)

2020 has been mostly terrible. That includes for the C++ committee and many of our communities, where just this month we lost Beman Dawes. Beman was one of the most important and influential C++ experts in the world, and made his many contributions mostly behind the scenes. I and everyone else who has ever benefited from any of the standardized STL, Boost, C++Now, std::filesystem, C++98/11/14/17, and more — so, really, most people who have ever used C++ — all owe Beman a debt of gratitude. We miss him greatly.

To end the year with a little dose of good news, I thought I’d mention a just few positive C++ accomplishments that did happen for 2020, and were happier “first-ever” achievements.

First, the big one…

C++20 is the first ever “D&E-complete” release of C++. In February, we completed C++20, which is the first release of Standard C++ that includes every feature that Bjarne Stroustrup envisioned for C++’s evolution in his 1994 book The Design and Evolution of C++ (aka D&E), including concepts, coroutines, modules, and more, except only for one minor feature (unified function call syntax). Thank you to Bjarne for sticking with it until we got there, and personally doing the heavy lifting to drive important features like concepts into Standard C++!

C++20 is the first release of C++ that added a feature that made the standard smaller. When I talk about the importance of simplifying C++ by judiciously adding features that let programmers express their intent directly, some people legitimately object that adding a feature makes C++ bigger and more complex. I reply “but it makes C++ code simpler” and “if it replaces something more complex then we can teach a simpler C++ for new code,” but those effects have been hard to measure concretely. Now in C++20 for the first time we added a new feature that made the standard smaller: We added the C++20 spaceship operator to the language, but we also applied it throughout the C++ standard library and that made the library specification nearly 20 pages shorter — a net reduction. So for the first time we can measure that, yes, adding a feature to C++ can make C++ smaller. Thank you to everyone who helped me with that proposal and who are listed in the Acknowledgements in the link, and especially to Walter E. Brown, Jens Maurer, Barry Revzin, and David Stone!

First year for all-virtual standards meetings, including EWG and LEWG. Since March, for the first time all major subgroups including the two main design subgroups of EWG (language) and LEWG (library) have been having virtual meetings by telecon or Zoom and making progress in between face-to-face meetings. We’ve also had a record number of nearly 20 virtual subgroup meetings on average per month. It’s great to see that, despite the pandemic, the committee has continued work on C++23 and other long-pole features, and in November we were able to formally adopt the first C++23 features into our brand-new C++23 working draft. Thank you once again to JF Bastien (EWG), Bryce Lelbach (LEWG), and their assistants, and all the other subgroup chairs and participants for patiently supporting these changes that we had to invent and transition to at short notice, and as we continue to work out the kinks as we go!

Many first virtual conferences. And of course 2020 saw many of our C++ conferences hold their first virtual events (and create new ones like Pure Virtual C++), in the face of huge uncertainties and technical challenges with bleeding-edge technologies, to make it all work far more smoothly than we really would have had any right to expect on such short notice. Thank you to the organizers for working so busily behind the scenes to make it possible to have a facsimile of our face-to-face conferences until we can meet again in person!

Here’s hoping that by this time next year we will all be doing better in every way, and have a happier 2021 to reflect upon. Thank you again, everyone, for your interest in C++ and support for our many C++ events, forums, and other communities large and small, and best wishes for a great 2021.

Trip report: Autumn ISO C++ standards meeting (virtual)

On Monday, the ISO C++ committee completed its final full-committee (plenary) meeting of 2020 and adopted the first changes to the C++23 working draft, including a few new features.

This was a first in several ways: It was our first-ever virtual plenary, held online via Zoom. It was also our first-ever plenary meeting that wasn’t held at the end of a long around-the-clock week of intensive subgroup meetings; instead, it was held at the end of nearly nine months of virtual subgroup meetings.

Our virtual 2020

The pandemic was just getting started when we held our February meeting in Prague, Czech Republic. Since then it has of course been impossible to meet in person; as I mentioned before, our ISO C++ meetings are virtual until further notice, but we continue to have the same priorities and the same schedule for C++23.

So since the pandemic began, WG21 subgroups have been meeting virtually via Zoom. Some subgroups had already been having virtual meetings for years, but this was a major change for other groups including our two main design groups – the language and library evolution working groups (EWG and LEWG).

In all, since Prague we have held about 150 virtual meetings. When lots of subgroups are meeting, some of them weekly, those meetings add up!

This week: First C++23 features adopted

On Monday we formally adopted the first features of C++23, including the first C++23 language feature, as well as a number of bug fixes.

First up was P0330 by JeanHeyd Meneide, which adds a literal suffix for (signed) size_t, so in C++23 we will be able to write literals like 100uz. (I wonder whether uz will be pronounced Uzi.) See the many excellent side-by-side examples in JeanHeyd’s paper for how this helps make uses of size_t safer and more convenient especially in naked for loops iterating over containers. Congratulations to JeanHeyd for C++23’s first language extension, and also for his persistence with this paper – the adopted version is revision 8, and that number and the paper’s change history indicates the level of rigor that can be required to get a feature into C++. Many thanks!

P1679 by Wim Leflere and Paul Fee add a basic_string::contains function so we can write code like if (str.contains(substr)) std::cout << “found!\n”; … I can already hear the chorus of “finally!”

P0881 by Alexey Gorgurov and Antony Polukhin add a stacktrace library to C++23. This is a much-anticipated extension based on Boost.Stacktrace that will enable much easier-to-debug portable diagnostic messages.

The charmingly numbered P1048 by Juan Alday gives us an is_scoped_enum type trait to detect when an enumeration is defined using the new-style (C++11, but well it’s still “new”!) enum class. As the paper points out, this is particularly useful as a migration aid, including to write code that detects and measures the adoption of “enum class” over plain old “enum.”

Finally, P0943 by Hans Boehm supports C atomics (spelled _Atomic) in C++ where the two did not already overlap, which helps write headers that work in both C and C++. (The adopted version is R6 which should be published in the next few weeks.) This is one example of the ongoing extra coordination we’ve had lately between the C and C++ committees, which leads to the next thing we did…

New SG22: C/C++ liaison

We appointed a new study group, SG22, for C/C++ liaison. This is a unique study group, because it is shared jointly by both the C and C++ committees, and it continues the tradition of closer coordination between the two committees. Thank you to WG14 (C) and its chair David Keaton for their continued interest in coordinating the two languages, to Aaron Ballman for agreeing to chair this new group, and for our WG14 and WG21 project editors Thomas Köppe, JeanHeyd Meneide, and Richard Smith to serve as assistant chairs. Thank you all for being willing to step up!

Other updates

Thank you to Richard Smith for his work for many years as project editor for the C++ standard, and completing C++20 this month! Thank you also to the many of you who have helped Richard and shared the editing workload by providing PRs and proofreading to apply plenary resolutions; that has been very much appreciated by Richard and by all of us especially given that C++20 is a “big” release with many new features, all of which have created an unusually high amount of editing work for this release.

Starting now, as we begin C++23, Thomas Köppe has graciously agreed to step up to be our primary project editor for the standard, with Richard as backup project editor. Thank you Thomas, and thank you again Richard and to all who have helped with the editing for the C++ IS!

Next steps

While we are meeting virtually until further notice, we will continue to have virtual plenaries like the one we had this week to formally adopt new features as they progress through subgroups. Our next virtual plenary will be in February, on the Monday of what would have been the Kona meeting.

Progress during this time will be slower than when we can meet face-to-face, and we’ll doubtless defer some topics that really need in-person discussion until we can meet again safely, but in the meantime we’ll make what progress we can and we’ll ship C++23 on time.

Thank you again to the hundreds of people who are working tirelessly on C++, even in our current altered world. Your flexibility and willingness to adjust are much appreciated by all of us in the committee and by all the C++ communities! Thank you, and see you on Zoom.

My plans at CppCon

It’s hard to believe CppCon 2020 is nearly here… in fact, pre-conference tutorials are already in progress.

I’ll be at the conference throughout the week in the hallways and session rooms. Here are some of the times I’ll be participating on the actual program:

  • Sunday 1300 MDT: Organizer’s Panel. In the middle of the Welcome Reception, we’re holding an Organizer’s Panel where several of us organizers will be talking about what to expect in the week ahead and available for extensive Q&A to answer any questions you may have. (We will have live music during the whole Welcome Reception, so be sure to watch the chat window for how to join that stream if you want to listen to our CppCon house band leader, Jim Basnight, perform for us.)
  • Monday 1500 MDT: My AMA. I’ll be available for an “ask me anything” session. Attendees can ask questions across the board from C++20 features, to how the committee is working during the pandemic on C++23, to specific C++ features I’ve designed or contributed to like concurrency or structured bindings or the spaceship operator, or other things you may think of.
  • Tuesday 0730 MDT: Committee Fireside Chat Panel (moderator). This year, I won’t be a panelist myself, so I don’t plan to answer any questions. Instead, I’ll be the panel moderator, and we have a great slate of panelists again this year: Bjarne Stroustrup (of course), Bryce Adelstein Lelbach (library evolution subgroup chair), Hana Dusíková (compile-time programming subgroup chair), Inbal Levi (Israel national chair), JC Van Winkel (Netherlands national chair and teaching subgroup chair), JF Bastien (language evolution subgroup chair), Michael Wong (low-latency/gaming/embedded subgroup chair and AI subgroup chair), and Tony Van Eerd (expert in many subgroups and popular speaker). I can’t wait!
  • Wednesday 0730 MDT: Bjarne Stroustrup AMA (moderator). This is Bjarne’s AMA, I’m just the moderator.
  • (will be recorded) Friday 1330 MDT: Empirically Measuring, and Reducing, C++’s Accidental Complexity. This is my one actual talk, and it’s the last talk of the conference. It will be a major update of the talk I’ve given publicly one time before in Prague earlier this year, which after a broader intro focused specifically on parameter passing as an example of where we could dramatically simplify C++. This time I’ll include lots of updates, including that I hope to demo a working compiler implementation of the proposal.

Notes:

  • Denver time zone (MDT) is the default, which is where the physical CppCon usually happens. The Sched.org view lets you show the schedule in your own time zone.
  • The conference starts Sunday. You don’t have to wait for Bjarne’s opening keynote on Monday… if you’re attending, be sure to make use of the Open House from 0900-1200 MDT where you can wander around, and especially the Welcome Reception from 1200-1430 MDT which includes the first panel (see above).

I look forward to seeing many of you there!

C++20 approved, C++23 meetings and schedule update

A couple of interesting things happened in the ISO C++ world this week…

C++20 passed unanimously, on track to publish later this year

On Friday September 4, C++20’s DIS (Draft International Standard) ballot ended, and it passed unanimously. This means that C++20 has now received final technical approval and is done with ISO balloting, and we expect it to be formally published toward the end of 2020 after we finish a final round of ISO editorial work.

As always, we are not counting on ISO’s publication speed to call it C++20, it’s C++20 because WG21 completed technical work in February. If for some reason ISO needs until January to get it out the door and assigns it a 2021 publication date, the standard will still be referred to as C++20. That is already its industry name, and 300,000+ search hits can’t be (retroactively made) wrong!

ISO C++ meetings are virtual until further notice, Kona postponed

A month ago, I notified the committee that our face-to-face meetings will be postponed until further notice. We still need to plan for face-to-face meetings so that we’re ready to resume when that’s possible and safe, but for now all currently planned meetings should be viewed as “tentative.”

Among other constraints such as national and corporate travel restrictions, we are subject to face-to-face meeting bans from several parent organizations. Two of those extended or enacted face-to-face meeting bans this week, on Tuesday September 1:

  • INCITS, the U.S. standards body, extended its face-to-face meeting ban through March 31, 2021. This means that our Kona meeting planned for February is now formally postponed to an unspecified future date.
  • ISO SC22, our corner of the international organization for standardization that handles programming languages, resolved to ban face-to-face meetings of more than 100 people until further notice. Since our meetings lately have regularly seen over 200 attendees, we’re currently evaluating how this affects future post-Kona tentative meeting plans.

All of these bans are subject to further extension, and we won’t meet in person again until it’s safe to do so. As of this writing, our next tentative face-to-face meeting would be the rescheduled Varna meeting, in the first week of June 2021, but that should be viewed as the earliest possible resumption of meetings. As the pandemic develops and INCITS and ISO meeting bans and other restrictions are extended, it’s certainly possible that we may not be able to meet again in 2021 at all. We’ll see.

In the meantime, though, we’re still making progress on our work: For several years, we have already been holding regular virtual meetings for some of our subgroups, including study groups (SGs) and CWG and LWG (language and library specification wording). Since the pandemic started, EWG and LEWG (language and library evolution, our primary design subgroups) have also begun meeting virtually, and we are continuing to adjust our process for how to approve design changes to progress proposals while not meeting in person. And starting in November, we will begin having virtual plenary (whole-group) meetings to formally approve changes, including potentially new features, to the C++23 working paper…

C++23 schedule and priorities

The C++23 schedule (P1000R4) and C++23 priorities (P0592R4) are unaffected by the pandemic. You may find this surprising, but that’s because the committee is on a “train model” that focuses on schedule and priorities for each release, instead of a specific feature set. One of the benefits of the train model is that it is very resilient, and can handle even major disruptions without change. We have already been in the mode of working on features all the time, including long-pole features that take many years, and each regular release train includes “whatever’s ready” with the next train opening up as soon as the previous one ships. So, that is unchanged.

What has changed, of course, is the speed at which we can work on features during the coming period. The pandemic disruptions have impacted all our lives, and reduced the time and energy WG21 participants have for standards work as well as our capacity to make progress face to face three times a year, and this has slowed down development of features we’re working on now that will land in { C++23, C++26, C++29 } . No virtual process will fully compensate for the lack of intense week-long face-to-face meetings, but as usual we’ll continue to make progress on baking features according to the P0592R4 priorities, including issue resolutions and an emphasis on completing C++20, and as usual we’ll load each feature into the currently loading train as the feature becomes ready. So progress continues, and the trains will continue to run on time to ship everything that’s ready.

Of course, the ISO C++ committee isn’t the only part of the C++ world that has “gone virtual” this year. We’ve been enjoying many virtual conferences, and just a week from now we’ll start the biggest C++ conference of the year: CppCon 2020, all online. I look forward to seeing many of you there, including literally seeing you at the video chat tables and in my AMA Q&A session early in the week, and the Committee Fireside Chat panel on Tuesday.

Thanks for your interest in C++ and C++ standardization! Be safe, everyone.