Interesting project. I await the eventual showdown between C++2 and Carbon.
What I’d really like to see is a new language that is well designed, and also highly interoperable with both C++ and Rust. Because I’d like to write apps using best of breed libraries chosen from both the C++ and Rust worlds, without creating a shim layer around a library before I can use it. I don’t have high hopes that this will exist, because of the difficulty, and also because it’s probably heresy to language purists in the Rust and C++ camps.
I’m familiar with the difficulty of using C++ from other languages, but what’s the issue using Rust? I’m guessing the borrow checker is the biggest hurdle?
Rust explicitly doesn’t conform to any external ABI. This means no binary compatibility with anything that isn’t built with the same exact version of the compiler; in practice it’s not quite as strict as that, but generally if you want to link a Rust library into a Rust program you must start by building the library from source.
You CAN write Rust code that explicitly uses the C ABI, so you can create static or dynamic libs that are callable from other languages. But it’s basically as much work as writing an FFI wrapper layer, and there’s many Rust features you can’t use in it such as generics, traits/trait objects, etc.
where Language and Analysis are traits. To use this library directly from another language without creating a wrapper, the language needs to understand Rust traits. (Also, what @icefox said.)
To get some idea of feasibility, let’s consider how Rust would need to change to allow C++ interop without a shim layer. (Rust in highly interoperable with itself.)
To be able to use any C++ library, you’d need to be able to use overloads and instantiate templates. For completeness, that requires matching C++’s notion of what known-size integers are aliases of (long etc.) and the distinvtion between e.g. char16_t and uint16_t. Can a language be well-designed and fit C++ overload resolution shimlessly simultaneously?
10x simpler than C++, type-safe and memory-safe by construction. You still have seamless interoperability with all C++ code via module import, but not via #include.
Assuming this is possible, this is the kind of language that I’d like to extend with seamless Rust interoperability via module import.
In my version of such a language, the core language would be significantly simpler than either C++ or Rust. However, some additional C++ specific complexity would be exposed when referencing members of a C++ module, and likewise for Rust.
I am not a fan of C++ overload semantics, but these would need to be used when compiling a call to a function M::f, where M is a C++ module. So maybe you would pay the cost of dealing with C++ overload semantics only if you import a C++ module.
The whole point is that this is C++. And C++ has a global object called std::cout, which has a left shift operator. Are you suggesting that a new syntax should have made it impossible to call the left shift operator on that object..?
C++23 is about to introduce a better print function (std::print). You can use fmtlib to get that print function today. But improving the standard library is completely orthogonal to introducing a new syntax.
Haha, I was unaware of std::print but more importantly I was unaware of std::format - habituated to *printf(…) I hadn’t been paying attention to all of the new APIs getting added to stdlib. On the downside I work in llvm/clang land so I only get c++14 :(
Operators can mean different things in different contexts. Many languages use + for string concatenation. Even in math, multiplication has a different meaning for matrices than for real numbers. Would it have been better to define some other sequence of punctuation marks for writing to a stream?
I agree that overloading the bit shift operator isn’t worse than + for concatenation, in and of itself. But iostream has so many problems: https://www.moria.us/articles/iostream-is-hopelessly-broken/. So like olliej I was disappointed seeing std::cout << "Hello" in the example code.
Oh, I agree about iostream. But cppfront is a parser / transpiler, fully compatible with existing C++20, so implementing a whole new IO library would be rather out-of-scope, n’est-ce pas?
I would have preferred adoption of something similar to template strings as we did in JS. JS engines have to deal with (parsing, compilation, and execution) far more random code than any C++ code and were able to handle the addition of template strings without a problem. I think it’s also more powerful than std::format as you can provide a function that actually has the job of construction, which is only possible because the syntax is part of the language.
I immensely dislike this overload as I do not see anything about << or >> that makes sense. Whereas + meaning concatenation makes sense as a concatenation.
And no I’m not proposing the addition of new operators for streams, I’m proposing not using overloaded operators. I don’t think stream.write(…) is perfectly reasonable, and I personally would expect it to be less characters as well (because many people measure character to count as being inversely proportional to “goodness”). The problems with the “streaming” APIs is that they are super verbose, they break up the actual text (in the case that you’re dealing with text obviously), and that things like formatting behavior are modal so that things in the old format string APIs are obnoxious to perform.
I remember Herb showing me his draft spec for this a few years ago, super exciting to see that it’s now public. A lot of things in the C++ standard made more sense after reading it, they were all groundwork for a backwards compatible language evolution. Modules were the biggest win here: if the consumer and the provider of an interface are just sharing an AST for the interface, rather than a token stream, then it’s easy to add a new syntax.
Interesting project. I await the eventual showdown between C++2 and Carbon.
What I’d really like to see is a new language that is well designed, and also highly interoperable with both C++ and Rust. Because I’d like to write apps using best of breed libraries chosen from both the C++ and Rust worlds, without creating a shim layer around a library before I can use it. I don’t have high hopes that this will exist, because of the difficulty, and also because it’s probably heresy to language purists in the Rust and C++ camps.
I’m familiar with the difficulty of using C++ from other languages, but what’s the issue using Rust? I’m guessing the borrow checker is the biggest hurdle?
Rust explicitly doesn’t conform to any external ABI. This means no binary compatibility with anything that isn’t built with the same exact version of the compiler; in practice it’s not quite as strict as that, but generally if you want to link a Rust library into a Rust program you must start by building the library from source.
You CAN write Rust code that explicitly uses the C ABI, so you can create static or dynamic libs that are callable from other languages. But it’s basically as much work as writing an FFI wrapper layer, and there’s many Rust features you can’t use in it such as generics, traits/trait objects, etc.
For a concrete example, I’d like to use the
egg
library in a C++ program. It has interfaces like this:where
Language
andAnalysis
are traits. To use this library directly from another language without creating a wrapper, the language needs to understand Rust traits. (Also, what @icefox said.)To get some idea of feasibility, let’s consider how Rust would need to change to allow C++ interop without a shim layer. (Rust in highly interoperable with itself.)
To be able to use any C++ library, you’d need to be able to use overloads and instantiate templates. For completeness, that requires matching C++’s notion of what known-size integers are aliases of (long etc.) and the distinvtion between e.g. char16_t and uint16_t. Can a language be well-designed and fit C++ overload resolution shimlessly simultaneously?
Herb Sutter says that C++2 will be
Assuming this is possible, this is the kind of language that I’d like to extend with seamless Rust interoperability via module import.
In my version of such a language, the core language would be significantly simpler than either C++ or Rust. However, some additional C++ specific complexity would be exposed when referencing members of a C++ module, and likewise for Rust.
I am not a fan of C++ overload semantics, but these would need to be used when compiling a call to a function M::f, where M is a C++ module. So maybe you would pay the cost of dealing with C++ overload semantics only if you import a C++ module.
This doesn’t answer the most important question: is the
.cpp2
file extension fixed or can it be.cxx2
,.cc2
,.C2
, and.c++2
?Edit: On a serious note, here is a video (starts at a bit after 4h mark) of Herb’s talk at CppCon. It’s pretty good, IMO.
My vote is for
.c++++
or.cpp++
.I got it,
.Cⵌ
Oh…
Nah, .c*
Sigh, and yet it retains the horrific abuse of shift operators for text formatting.
The whole point is that this is C++. And C++ has a global object called std::cout, which has a left shift operator. Are you suggesting that a new syntax should have made it impossible to call the left shift operator on that object..?
C++23 is about to introduce a better print function (
std::print
). You can use fmtlib to get that print function today. But improving the standard library is completely orthogonal to introducing a new syntax.Haha, I was unaware of std::print but more importantly I was unaware of std::format - habituated to *printf(…) I hadn’t been paying attention to all of the new APIs getting added to stdlib. On the downside I work in llvm/clang land so I only get c++14 :(
You can write a fmt-alike in low hundreds of lines of code with C++11 vardiadic templates or pre-C++11 1 to N overloads, or you can use ggformat
Clang has had full C++17 support since Clang 5, and it supports most of C++20 (https://clang.llvm.org/cxx_status.html#cxx20) and even a decent chunk of what will become C++23 (https://clang.llvm.org/cxx_status.html#cxx23). Things may not be as dire as you think :)
It seems like libc++ also supports std::format since version 14: https://libcxx.llvm.org/Status/Format.html#misc-items-and-todos
Haha you misunderstand, I work on them :D Clang and llvm code bases are essentially c++14. It’s super annoying :)
Operators can mean different things in different contexts. Many languages use + for string concatenation. Even in math, multiplication has a different meaning for matrices than for real numbers. Would it have been better to define some other sequence of punctuation marks for writing to a stream?
I agree that overloading the bit shift operator isn’t worse than + for concatenation, in and of itself. But iostream has so many problems: https://www.moria.us/articles/iostream-is-hopelessly-broken/. So like olliej I was disappointed seeing
std::cout << "Hello"
in the example code.Oh, I agree about iostream. But cppfront is a parser / transpiler, fully compatible with existing C++20, so implementing a whole new IO library would be rather out-of-scope, n’est-ce pas?
Isn’t that out-of-scope? The goal is to evolve C++ syntax, not its libraries.
It is also explicitly not a new language, so it makes sense to use the standard, if “horrific”, hello-world as a first example:
I would have preferred adoption of something similar to template strings as we did in JS. JS engines have to deal with (parsing, compilation, and execution) far more random code than any C++ code and were able to handle the addition of template strings without a problem. I think it’s also more powerful than std::format as you can provide a function that actually has the job of construction, which is only possible because the syntax is part of the language.
You know what? It’s already there… https://github.com/hsutter/cppfront/blob/main/regression-tests/mixed-string-interpolation.cpp2
I immensely dislike this overload as I do not see anything about << or >> that makes sense. Whereas + meaning concatenation makes sense as a concatenation.
And no I’m not proposing the addition of new operators for streams, I’m proposing not using overloaded operators. I don’t think stream.write(…) is perfectly reasonable, and I personally would expect it to be less characters as well (because many people measure character to count as being inversely proportional to “goodness”). The problems with the “streaming” APIs is that they are super verbose, they break up the actual text (in the case that you’re dealing with text obviously), and that things like formatting behavior are modal so that things in the old format string APIs are obnoxious to perform.
This is similar to Rust’s editions in that it maintains link compatibility while fixing things that can’t be done with source compatibility.
I remember Herb showing me his draft spec for this a few years ago, super exciting to see that it’s now public. A lot of things in the C++ standard made more sense after reading it, they were all groundwork for a backwards compatible language evolution. Modules were the biggest win here: if the consumer and the provider of an interface are just sharing an AST for the interface, rather than a token stream, then it’s easy to add a new syntax.