If you have kept up to date with the latest developments of C++, for sure you have noticed how convoluted and byzantine constructs and semantics have become.
The original idea of a C with Classes at the root of language is long lost and the fabled smaller and cleaner language hidden inside C++ is ever more difficult to spot and use.
The combo – backward-compatibility latch and committee-driven approval/refusal of proposals, make the language evolution spin around. Missing or late additions to the language are sitting ducks, and the lack of networking in the standard library, for a language that is 40, is enough to tell how poorly the evolution of the language is handled.
I don’t blame the committee, at least not entirely, for this abysmal state, personally, I reckon the people in the committee are utterly smart and dedicated and committed to the language success, but I also think that maybe a committee is not the best way to drive the evolution. The decisional process takes too much is too fragmented, and has a high risk to be driven by politics rather than technical and market reasons. This is evident IMO by comparing C++ to other languages, whose development is led by a smaller group backed by large communities.
To be completely honest other languages have a single vendor, the one that usually drives the language evolution, while C++ must support multiple vendors offering more freedom to the user to choose the implementation that best suits them.
But that’s it, in 2022, C++ is in a bad shape, the library is made of patchy and heterogeneous pieces, and the language is extremely complex, verbose, and easy to misuse. Most of the defaults are contrary to what is expected from a modern language (for backward compatibility), the syntax gets involved and complex even for simple concepts, and you can get cryptic endless diagnostic messages if you get something template-related wrong.
Also, C++ lacks standard tools for building and for handling dependencies. There are some, but they are not part of the language, and not being universally adopted you may find yourself struggling to get different components to work together.
This has not gone unnoticed by developers and eventually, a niche for alternative or successor languages opened up. There are many reasons to stick with C++: it is a very mature language, with industrial-grade tools and compilers, and it is likely to stay around for a good while. But the cost of using C++ is getting higher and higher to the point that alternatives become viable.
First came rust. Never claimed to be a C++ successor, but it is a system language that aims to solve the same class of problems the C++ does, offering comparable (sometimes even better) performance. Additionally, rust comes with the notable property that valid programs do not leak. I.e. if you can compile rust code, you won’t have memory leaks or buffer overflows. This is not entirely true, since you can force the language to do unsafe things, but it is a good approximation since usually, you don’t need to be unsafe and when you need it, then you have to be very intentional about this.
Rust is 12 years old, it got quite solid and stable, and it is being used in Linux and Windows source code. The learning curve may be not very smooth, but rust is getting really widespread.
This year two announcements rocked the C++ kingdom – Carbon and cppfront (AKA C++ syntax 2).
Carbon Language
Carbon was announced at CppNorth 2022 by Google. The language is still early in its infancy, presented as an experiment, and has the ultimate goal to be a successor of C++. There is no compiler (just an interpreter) and significant areas (notably memory safety) have not been designed yet.
Nonetheless, Google thoroughly analyzed the problems of C++ evolution and made sure to put the language on an alternate course:
- The language evolution is driven by a triumvirate taking more important decisions and an open community;
- Avoid stable ABI and backward compatibility thanks to a set of tools that automate translation between different versions of the languages.
From the syntactical point of view, Carbon takes the pattern: variable name followed by type, same for functions – the return type is at the end of the argument list. Set this aside the language doesn’t seem to offer a more direct or brief way to achieve the same results, yielding a verbosity only slightly lighter than C++. But it may be too early to have a definitive opinion.
Possibly the most notable innovation is about the handling of generics. C++ generic programming is based on Duck typing – i.e. the type is ignored, as long as it provides the features used in the template. This may be fine, but there is no guarantee that, even if the syntax fits, the semantics are what the template expects. Just to exemplify – a template may require an object of type T to expose a fire()
. This poses no requirements on type T, but for the presence of the method. The code may compile both passing an instance of Employee or an instance of Weapon, with clearly different and dramatic results. C++ concepts also don’t help, since they just define syntactical constraints.
Carbon gets around this limit, by enabling the programmer to require a base class, or an interface for the generic type to give a semantic context. This check can be performed at compile time.
Still, in the generic programming domain, there is no SFINAE, but templates are enabled via if statements that more conveniently express the conditions under which this template has to be used.
Another welcome innovation regards function arguments. These are const by default and they are passed by reference or by copy according to which is more performant, relieving the programmer from this choice. You may force copying of parameters. Reference to parameters, if needed, must be explicitly required via pointer use.
The last two remarkable additions are pattern matching and tagged unions (choice). Pattern matching is like a switch case on steroids where instead of literal constant you can specify patterns (e.g. types, tuples, expressions). Tagged unions are like std::variant
(sum types) but integrated into the language, very close to Rust’s enums.
So far, so good, but the reality is that the language is not yet there. There is no compiler, just an interpreter, and some quite important aspects of the language (e.g. memory handling) are still to be discussed and defined. But this effort is backed by Google, so it is wise to take this seriously and check back in a few years.
You can try Carbon on compiler explorer.
Cppfront (AKA C++ syntax 2)
Last September, at cppcon 2022, it was the time for Herb Sutter, a long-time C++ veteran, to unveil cppfront a new syntax for C++.
The reasons cited for creating a new language were basically the same, but the approach is quite different and interesting.
The guiding principle in designing the new language is to address the shortcomings of C++, turning proper idioms, and modern usage of the language into default syntax and making it impossible to use problematic or obsolete constructs. The goal for Herb was to measurably reduce the effort of programming correctly in C++ and the effort needed to teach the language. Basically, cppfront is a shell that wraps around C++ and lets you use only the safe part of the language in a modern and rational syntax.
Rather than implementing a compiler, Herb opted for a transpiler, so that Cppfront source is translated into C++. This approach has two additional benefits – first integration with C++ code is straightforward since C++ is what is generated by the transpiler, and, second, cppfront may be used to produce working and editable C++ code if the original cppfront code is not needed (or welcomed, or the experiment fails).
Whereas Carbon has a rich website with structured documentation (even if mostly filled with TBD equivalent), cppfront has no documentation and Herb suggests interested readers to browse the unit tests. It can be done, but it is quite an ineffective way to find features.
Many of the cppfront characteristics are based on proposals that Herb made in the last years (and this left me for a while with a slightly disturbing feeling – were the proposals made for improving C++, or for easing the transition to cppfront?)
Cppfront syntax is modern, very uniform, and consistent – variables, constants, and functions are all declared as:
name: type = value.
Arguments are passed by using specifiers such as in, inout,
In both cases, the semantic, or, if you prefer, the executing machine, is still the same, but the C++ syntax is deemed broken, bloated, and too complex justifying the effort to design a new front end.
Compiler Explorer offers a way to experiment with cppfront as well.
Circle
Circle is worth mentioning here because it takes an alternative way to reach the same goal – be the next C++.
First, Circle is a C++ compiler, developed by a single programmer (!) – Sean Baxter, and designed to be easily extendible. This allowed the author to explore many additions to C++ that, while remaining in the C++ language framework, would make the C++ programmer’s life easier.
Slice, pattern matching, choices, interfaces, and so on.
The idea behind Circle is to work within the language while providing a number of powerful extensions, that very slightly break backward compatibility. Circle could somehow lead the language evolution (committee willing), or lock in developers on this very compiler.
From this point of view, it is relevant that Circle is the only one of these projects that is not open source. It is free (as in beer), but someday the author may decide to capitalize on his investment and propose paid support or subscriptions, or whatever.
Of course, compiler explorer lets you experiment with Circle.
Wrap it up
These stories tell a clear truth – the C++ era is ended. Far from me to say that this language will soon be abandoned. It would take years, but this is the time when it started.
It is hard to say who will be the winner and even if there will be a single winner, or if the development community will split over several different languages.
Also, other contenders will likely enter the arena fighting to get programmers’ attention and trying to build the largest community.
As of today, the road taken by cppfront seems, IMO, to be the most promising – the C++ interaction is perfect and the effort needed to implement the new language is reduced. It seems a viable solution to replace and evolve incrementally your C++ codebase into the new language. The greater weakness, with the current implementation (aside from the utter lack of documentation and the bad name), is the chance of stumbling in an error reported by the C++ compiler that is difficult to map to the original cppfront code.
Circle may help to take C++ programmers to new constructs, but still paying the tens of years of technical debt, the more Circle will pay this debt off, the more the language will be detached from C++, eventually losing the advantage of being C++.
Carbon looks less promising than cppfront and, considering the firepower of Google, also seems a bit neglected, compared to Go and Dart.
What is clear to me is that if you are sick of C++ today, you don’t have to wait for anything else, just turn to Rust. A language that is mature, widely available, and popular. Interoperability with C++ may be not as seamless as cppfront, but it is possible and well-tested. Moreover, none of the C++ successors is bound to give you the same safety that Rust provides out of the box.
Ps: Besides the original presentations, I found very interesting the following podcast episodes, guest Sean Baxter, comparing the new languages:
One thought on “Nothing lasts forever, but Cobol, Fortran, and C++”