It’s not all good in C++ land. Over the past two posts, we’ve seen some of the great new features being added in next year’s update to the standard, but there are a few things that just didn’t make the cut. For some, that might be good. For others, it’s a shame.
Concepts
Concepts have been a hot topic among C++ insiders for over a decade. At their core, they’re a kind of addition to the template system that would allow a programmer to specify that a template parameter must meet certain conditions. For example, a parameter must be a type that is comparable or iterable, because the function of the template depends on such behaviors.
The STL already uses concepts behind the scenes, but only as a prosaic description; adding support for them to the language proper has been a goal that keeps receding into the future, like strong AI or fusion power. Some had hoped they’d be ready for C++11, but that obviously didn’t happen. A few held out for C++14, but that came and went, too. And now C++17 has shattered the concept dream yet again. Mostly, that’s because nobody can quite agree on what they should look like and how they should work under the hood. As integral as they will be, these are no small disagreements.
Modules
Most “modern” languages have some sort of module system. In Python, for instance, you can say import numpy
, and then NumPy is right there, ready to be used. Java, C#, JavaScript, and many others have similar functionality, often with near-identical syntax.
But C++ doesn’t. It inherited C’s “module” system: header files and the #include
directive. But #include
relies on the preprocessor, and a lot of people don’t like that. They want something better, not because it’s the hip thing to do, but because it has legitimate benefits over the older method. (Basically, if the C preprocessor would just go away, everyone would be a lot better off. Alas, there are technical reasons why it can’t…yet.)
Modules were to be something like in other languages. The reason they haven’t made the cut for C++17 is because there are two main proposals, neither truly compatible with the other, but both with their supporters. It’s almost a partisan thing, except that the C++ Standards Committee is far more professional than Congress. But until they get their differences sorted out, modules are off the table, and the preprocessor lives (or limps) on.
Coroutines and future.then
These fit together a bit, because they both tie in with the increased focus on concurrency. With multicore systems everywhere, threading and IPC are both more and less important than ever. A system with multiple cores can run more than one bit of code at a time, and that can give us a tremendous boost in speed. But that’s at the cost of increased complexity, as anyone who’s ever tried programming a threaded application can tell you.
C++, since its 2011 Great Leap Forward, has support for concurrency. And, as usual, it gives you more than one way to do it. You have the traditional thread-based approach in std::thread
, mutex
, etc., but then there’s also the fancier asynchronous set of promise
, future
, and async
.
One thing C++ doesn’t have, however, is the coroutine. A function can’t just pause in the middle and resume where it left off, as done by Python’s yield
keyword. But that doesn’t mean there aren’t proposals. Yet again, it’s the case that two varieties exist, and we’re waiting for a consensus. Maybe in 2020.
Related to coroutines is the continuation, something familiar to programmers of Lisp and Scheme. The C++ way to support these is with future.then()
, a method on a std::future
object that invokes a given function once the future
is “ready”, i.e., when it’s done doing whatever it had been created to do. More calls to then()
can then (sorry!) be added, creating a whole chain of actions that are done sequentially yet asynchronously.
Why didn’t then()
make it? It’s a little hard to say, but it seems that the prevailing opinion is that it needs to be added in the company of other concurrency-related features, possibly including coroutines or Microsoft’s await
.
Unified call syntax
From what I’ve read, this one might be the most controversial addition to C++, so it’s no surprise that it was passed over for inclusion in C++17. Right now, there are two ways to call a function in the language. If it’s a free function or some callable object, you write something like f(a, b, c)
, just like you always have. But member functions are different. With them, the syntax is o.f(a, b, c)
for references, o->f(a, b, c)
for pointers. But that makes it hard to write generic code that doesn’t care about this distinction.
One option is to extend the member function syntax so that o.f()
can fall back on f(o)
if the object o
doesn’t have a method f
. The converse is to let f(o)
instead try to call o.f()
.
The latter form is more familiar to C++ coders. It’s basically how Modern C++’s std::begin
and end
work. The former, however, is a close match to how languages like Python define methods. Problem is, the two are mutually incompatible, so we have to pick one if we want a unified call syntax.
But do we? The arguments against both proposals make some good points. Either option will make parsing (both by the compiler and in the programmer’s head) much more complex. Argument-dependent lookup is already a difficult problem; this only makes it worse. And the more I think about it, the less I’m sure that we need it.
Reflection
This, on the other hand, would be a godsend. Reflection in Java and C# lets you peer into an object at run-time, dynamically accessing its methods and generally poking around. In C++, that’s pretty much impossible. Thanks to templates, copy elision, proxy classes, binders, and a host of other things, run-time reflection simply cannot be done. That’s unfortunate, but it’s the price we pay for the unrivaled speed and power of a native language.
We could, however, get reflection in the compile-time stage. That’s not beyond the realm of possibility, and it’s far from useless, thanks to template metaprogramming. So a few people have submitted proposals to add compile-time reflection capabilities to C++. None of them made the cut for C++17, though. Granted, they’re still in the early stages, and there are a lot of wrinkles that need ironing out. Well, they’ve got three (or maybe just two) years to do it, so here’s hoping.
And that’s all
C++17 may not be as earth-shattering as C++11 was, but it is a major update to the world’s biggest programming language. (Biggest in sheer size and scope, mind you, not in usage.) And with the new, faster release schedule, it sets the stage for an exciting future. Of course, we’ll have to wait for “C++Next” to see how that holds up, but we’re off to a great start.