Hacker News new | past | comments | ask | show | jobs | submit login
C++20, How Hard Could It Be (docs.google.com)
330 points by pyler on Sept 25, 2022 | hide | past | favorite | 429 comments



Many are of the listed items are merely being deprecated. These shouldn’t really qualify as breaking changes. In fact, the ability to migrate away incrementally is precisely the point of deprecating instead of removing a feature.

What surprises me is that a lot of the deprecated functionality seems really recent — C++14 or newer. Compatibility is C++’s big thing, that’s historically why it kept almost all of C as a sublanguage. I know organizations where migrating to C++11 is still an ongoing process. It’s not great news if features become obsolete faster than many users can adopt them.


Mostly deprecations mean something was deemed to be a bad idea, which means it's at the very least worth taking a moment to evaluate whether somehow they were a good idea when you did them.

For example should I have a method whose parameters are volatile? Well, why did I do that? It didn't do anything in C++ 17 and it still doesn't do anything in C++ 20 but now it's deprecated. Programs are written first and foremost to be read by other humans, but what was I trying to communicate by writing "volatile" on a parameter ?

The deprecation of composite operators on volatiles is an even better example. Those do something, but what they do might surprise programmers. Rewriting and then reading the rewritten example is an opportunity to discover that your code was broken anyway because now it's obvious. This obviousness is why deprecating the composites happened (and why it's sad that C++ 23 un-deprecates some of them).


> Mostly deprecations mean something was deemed to be a bad idea

"Most" maybe by count, but I'm not sure if it's "most" by usage frequency. There have been & will be lots of nonsensical deprecations that are much more common in existing code than "putting volatile on an object argument" (which I've honestly never even seen anyone do in my life)... like static (which was undeprecated, thankfully, but how were people supposed to know this ahead of time?), std::aligned_storage, std::bind1st, std::iterator. They're frustrating as heck, have questionable merits to begin with, and working around the deprecations in existing code provides practically zero business value.


So as I understand this, you don't agree that std::iterator was deemed to be a bad idea?

I mean, I personally would feel comfortable defending the idea that is a bad idea, but that's not even what I'm saying, I'm only saying it was deemed to be a bad idea, which is exactly what the proposal for deprecating it explains.


No, all I'm saying is that, as I see it, (a) it wasn't causing enough harm (if any at all) to warrant deprecation, and (b) that removing it doesn't deliver any worthwhile benefit either.


The volatile parameters deprecation is one of the stranger decisions, though, since there are plenty of constructions that don’t do anything but are still allowed by the language. I see this as a matter of style that might be useful to warn about, but shouldn’t be policed as an error by the compiler.


FWIW it participates in overloading. Did you ever try to overload all combinations of teplate specializations? With volatile is even worse.


Deprecated features are compiler warnings, not compiler errors, unless you use -Werror or the like.


> Programs are written first and foremost to be read by other humans

extremely bold assumption. plenty of write-only or use-once code in the wild.


Fair, lets modify that to just "humans" not "other humans" and say instead that programs should be written first and foremost with this in mind.

I have never discovered a reliable way to discern whether I will re-use some code. Of course I could just delete it after first use and declare it "use-once" that way, but that's cheating, if I just have to type in the same program tomorrow we should admit that deleting it and rewriting it was a pessimisation.

In the specific case of volatile parameters I can't figure out whether writing this in use-once code is more stupid or less stupid. On the one hand, you aren't misleading anybody else because the compiler knows perfectly well this doesn't do anything, on the other hand with the only human participant being yourself maybe you think it does something and whatever that is you're wrong so that's very bad.


Other humans is fine if you include "yourself, in the future" as an "other human" in the ship of Theseus sense


I think if write-only code were the norm, then APL would be more popular.


But there are APL, k, j and other array lang programs. You can't do as if those don't exist when talking about programming as a whole. Perl used to be pretty popular too.


I didn't say array programming doesn't exist, I said it's not popular. How many people actually use those languages compared to e.g. Python, where readability is one of its main features.

> Perl used to be pretty popular too

And now it’s a meme due to how illegible it is.

I’ll put it this way, there’s a reason Brainfuck is a joke lang instead of something people seriously use.

Also, reading and writing are not orthogonal skills. One has to read to be able to write. I read my programs constantly as I'm writing them.


and I am not writing about popularity, just saying that there are many in an absolute sense (which is honestly the only thing that matters in practice, relying on popularity of things relative to each other in a state of abundance is absolutely stupid)


Then I don’t understand your point. The initial claim was that programs are written for human consumption first and foremost. To me, you seem to be replying that programming languages exist that are write only. In that case, agreed. But I don’t see how that is meant to counter the claim that programs are written for humans.


I disagree with the "first and foremost" part - more precisely, I don't think it makes any sense to speak about this. There are people whose entire career will be only APL-ish or whatever funky ML-derived dialect, writing programs in a team of 1 and basically converting their thought processes directly into code, and their experience matters exactly as much as the experience of the X million developers in easier / more team/read-oriented languages matters.


K is very niche, but in its niche is a extremely popular.


The real solution, which decades from now will be the eventual common-place, but no one dares to imaging the possibility today is that when languages deprecate a feature, the compilers deliver code-mods that migrate your code-base perfectly.

But people are afraid of going that route because of the bad press around Python 2 / 3. We need a new generation of developers that don't remember that.

Today, there's already static-code analysis tools that automatically migrate entire code-bases to use new language constructs. The technology is there. It's the people who lack imagination to make it happen.

Once it becomes commonplace, a large % of developers will care very little if some obscure feature of C is dropped. They'll just enjoy having a more streamlined language with all the legacy crap gone.


Transformations are difficult.

Rust provides best effort transformation for its Editions (which only touch syntax) in cargo fix --edition, but even that's only best effort. If a proc macro does something ludicrous (see Mara's whichever-compiles! macro for example) how can the transformation hope to keep up?

C++ versions are in much deeper, they not only change the language syntax, but also semantics of existing features. Sometimes the intended effect is null (but intentions may not match reality) and sometimes it is not.

C++ also substantially changes the standard library. Rust's standard library grows but doesn't get redefined in new Editions, so if you call a deprecated Rust function it's not going anywhere, whereas a deprecated C++ method might actually go away in a future version.

It's a shame that the best effort tools aren't provided with C++ compilers, but even if they were provided on a large codebase it's only the beginning for large projects.


clang-tidy is such a tool, provided with a C++ compiler - although incomplete


UB throws a huge wrench into all of this. The behavior of a C++ program is defined by a combination of the code, the compiler, the compiler flags, and the target archecture. You can't write a source to source translator that preserves the semantics of a program with UB without that extra information about how it was compiled. It's a very hairy problem.


Swift is another one that has had a really bad migration between major versions, I can’t really remember now but it was either 2->3 or 3->4. They had a static migrator and it barely helped, it would get into loops where it would e.g. flip flop between two different uncompileable fixups.

Seems like this is a really hard problem, what is an example of a platform that has solved it perfectly or is even close? Swift was a brand new language, it should be easiest to do it there vs any system with legacy (although it was designed to interoperate with ObjC, so… maybe legacy remains).


It was 2-3. My take at the time was that they built an add-on to crash xcode.


Doesn't Google Abseil, the standard library replacement, do this?

https://abseil.io/


It is not a replacement, it is a staging ground and annex.


It's a branding problem. They should probably be viewed as different flavors. Using say herbs or colors instead of numbers would help.

If every time they're going to add things, remove things and break things then we're in practice talking different strands.

Imagine some preprocessor where you can mix them like

#flavor(ginger)

Instead of say c++11 and then proceed with whatever flavor as necessary.

I know you can do that at the linker and with makefiles and compile flags, this is about a more sane presentation.


> It's a branding problem. They should probably be viewed as different flavors.

They are already different language versions. They're specified in entirely different standards. I don't see what's left to be confused about. At most, perhaps the C++ standard committee could be criticized for repeatedly going out of their way to maximize backward compatibility.

> If every time they're going to add things, remove things and break things then we're in practice talking different strands.

They are already different standard. What's there to miss?

> I know you can do that at the linker and with makefiles and compile flags, this is about a more sane presentation.

This take doesn't make sense. The C++ version being used in a project is a property of the project, not of the translation unit or individual files. A project is comprised of multiple declarations and corresponding definitions, which are spread around and reused and make sense as a whole. It would not make sense to, say, have a translation unit comprised of X C++11 definitions mixed with Y C++20 definitions.


There's a lot there.

Let's get the technical part done first.

Historically after you create object files the linker doesn't care what c++ standard the source was. So you could carefully combine different standards. I guess I have to establish I'm talking about the GNU toolchain here and that it's been a few years since I've done this. I'll try it again when I get home, maybe that all blows up now.

Now about the other parts. I totally agree with you. However we're dealing with humans and if they see incrementing numbers then the word "upgrade" and "deprecated" and "unsupported", maybe even "inefficient" gets bandied about just because we're using numbers.

We have to go back to the core lesson of Perl 6, it shouldn't have been called Perl 6 because it suggests a hierarchical comparison and relationship that isn't an accurate depiction of reality.

I wish everyone was sincere and competent but there's a natural tenancy to act based on the context that a structure affords.

Churchill stated it as "we shape our buildings and afterwards our buildings shape us".

So if the standards are better understood as siblings of each other then we need to brand them accordingly and not through a structure that suggests hierarchy, quantity of features, and degrees of relevance.

Thanks for your response. I enjoyed reading it


> Historically after you create object files the linker doesn't care what c++ standard the source was.

Mechanically this is true, but just because we can link object files together doesn't mean the resulting program makes sense.

Suppose I have an object file I made with GCC's copy-on-write C++ 98 strings and then I linked that to an object file I made with GCC's modern C++ 11 short string optimised strings. If these objects think they're talking about the same string then the resulting executable is nonsense and will probably crash or misbehave badly.

It might be helpful to think of WG21's attitude to compatibility as occupying a sort of "strategic ambiguity" akin to the US stance on Taiwan. For example when it comes to ABI changes, the committee voted that they shouldn't happen... yet. Not that they will happen within some horizon, but nor that they won't happen.


Maybe you have a deeper understanding of the GNU GCC toolchain than I do but I'm able to link together languages with far more dramatic divergences than that.

For instance, Go and C : https://go.dev/doc/install/gccgo (it's pretty far down, let me quote: "The name of Go functions accessed from C is subject to change. At present the name of a Go function that does not have a receiver is prefix.package.Functionname. The prefix is set by the -fgo-prefix option used when the package is compiled; if the option is not used, the default is go. To call the function from C you must set the name using a GCC extension.")

I just had a small C program call a fmt.Println from a go library at my console to confirm. extern the declaration in C to match the calling convention of go, compile them to objects, then ld with the appropriate libs.

Of course you can break the friendship they can have, this is programming, that's easy to do.

This can be demonstrated with different C++ versions as well. The C/Go example was meant to show how extremely different languages can interact when you're careful enough in your build process.


When gcc broke the string ABI they made sure that code that uses one version wouldn't link with code that uses the other one.


Only for the most common case. Try e.g.

  struct muhahaha {
    std::string member;
  };
library.cpp98:

  oops(muhahaha param);
program.cpp20:

  int main() {
    oops({"this will link just fine and there will be nothing warning you about your fate"});
  }


Not only it is possible, it is routinely done. At $WORK we have libraries that are use std=20 features in its implementation and only expose a c++=14 interface because that's what expected by our clients.

It is mildly painful, bit not more painful than restricting to c++14.


> At $WORK we have libraries that are use std=20 features (...)

That's specified at the package/project level. You're inadvertently proving my point.

> (...) and only expose a c++=14 interface because that's what expected by our clients.

That's also configured at the package level, because the interface headers need to be included in translation units from projecta configured tu be C++14.

Again, you're also inadvertently supporting the point I made.


Chromium compiles with -Werror, so deprecation warnings must be fixed before we can upgrade.


Wait, so the words "concepts" and "requires" were newly made keywords, and this breaks code, but the words "yield" and "await" were determined too important and too common to standards members that they needed to be renamed to the horrifically ugly "co_await" and "co_yield"?

Also, last time I actually tried to use C++20 none of the standard library implementations had std::format; has this changed now?


One day in the far away future the standard C++ hello world will use 'import std;' and 'std::print' and it will be glorious.


When I was going to university the computer science department's primary system had two compilers available, GCC 2.96[0] and TenDRA. GCC was generally more popular but it didn't have std::string and any code that threw an exception would crash, so adventurous students would use TenDRA for development.

One of the stranger behaviors of TenDRA was that it put all standard library symbols, including the C headers, into namespace std. A hello world in TenDRA looked like this:

  #include <cstdio> // no <stdio.h> available

  int main() {
    std::printf("Hello, world!\n");
    return 0;
  }
It was not glorious. If anything ... the opposite. When TenDRA deigned to compile your program it would generally work (excepting bugs in the code itself), but getting it to accept any sort of third-party code was impossible because of the std namespace thing.

I ended up writing a bunch of utilities for strings, including a unit testing library that spawned each test as a subprocess (to avoid exceptions) just so I could use GCC instead.

[0] There is no such version recorded on the GNU project website.


C symbols are supposed to be in std if you use the cstdio style headers. It’s just that for a very long time compilers put them into the global namespace too.


GCC, Clang, and MSVC all still duplicate c* header definitions into the global namespace by default as far as I'm aware.

In fact, the last time I checked, the MSVC cstdio header was implemented more or less like this:

    namespace std {
        #include <stdio.h>
    }

    using namespace std;
It's kind of crazy how major compilers have been ignoring the standard for so long that people consider a standard compliant compiler to be weird and broken.


> It's kind of crazy how major compilers have been ignoring the standard for so long that people consider a standard compliant compiler to be weird and broken.

I had absolutely no idea this wasn't even supposed to be the case (although I was aware of the duplication in std::). Guess it makes sense that the standard would shy away from global namespace pollution - I suppose one of the compilers perhaps did this for long enough that the others ended up duplicating it to increase code compatibility?


That the as actually not conforming. It was made conforming in c++11 because it was pointless to have the standard say something that all major implementations would ignore.


>There is no such version recorded on the GNU project website.

It's sort-of recorded. It's not in the version list since there wasn't a release, just distros making a mess:

https://gcc.gnu.org/gcc-2.96.html


GCC 2.96 was a development version that incorrectly got released by Redhat on Redhat 7 (and possibly other distros).

https://gcc.gnu.org/legacy-ml/gcc-announce/2000/msg00003.htm...


This was on a DEC Tru64 system, very far from any sort of Linux.

My personal suspicion is that someone (student or faculty) got their hands on a CVS snapshot and installed it system wide. That's the only explanation I can think of for it being so broken.


Fairly sure some PS2 SDKs also shipped GCC 2.96, which is why I know this tidbit of info.


2.96 exists, at least in the RedHat world.

    $ rpm -qi gcc
    Name        : gcc                          Relocations: (not relocateable)
    Version     : 2.96                              Vendor: Red Hat, Inc.
    Release     : 110                           Build Date: Fri 12 Apr 2002 10:30:47 PM UTC
    Install date: Thu 20 Jan 2011 03:34:36 AM UTC      Build Host: daffy.perf.redhat.com
    Group       : Development/Languages         Source RPM: gcc-2.96-110.src.rpm
    Size        : 8389509                          License: GPL
    Packager    : Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>
    URL         : http://gcc.gnu.org
    Summary     : The GNU cc and gcc C compilers.
    Description :
    The gcc package includes the cc and gcc GNU compilers for compiling C
    code.
    $ cat /etc/redhat-release
    Red Hat Linux release 7.3 (Valhalla)



That will be in cpp2 already: https://github.com/hsutter/cppfront


Already available in C++23.


The future tense seems better here. This will (almost certainly) be available in C++ 23.

In the context of Chromium, this slide deck, that means in about 2026.

So if you're starting a three year degree next week, and you already know Google will hire you for the Chromium team straight after because you're the perfect person for that, you won't be writing std::print() calls in that codebase yet 'cos that's too early.


Well, they could convince their management to restart doing clang contributions for ISO C++ compliance, or maybe allocate 20% of their time for that.


The far away future? You mean next year?


I don't mean available as in the committee has published their final version of the standard that allows it. I mean available in production.


Oh, that'll probably be sooner, then; STL is already working on it. Also if you want to use it now, fmt has an implementation.


`std::this_thread::yield` already existed at the time, and “yield” was presumed to also be a common identifier in financial and agricultural contexts.

I’m nevertheless surprised that new noncontextual and previously-unreserved-identifier keywords were added at all. In earlier times, the approach would have been to use a reserved identifier like “_Yield” for the keyword, and to provide a standard opt-in header that would `#define yield _Yield`. Maybe they don’t want to add dependencies on the preprocessor anymore.


I’m not sure C++ has ever used the `_Yield` hack, except for C compatibility? C on the other hand uses it all the time.


It could be that I'm remembering it from C.


It's super common in C because C doesn't have namespaces and templates which means stuff C++ implements as namespaced library classes C has to implement as language keywords.

A good example is atomics. C++11 introduced `std::atomic`, C11 introduced the keyword `_Atomic`


Such a weak argument, you can fix this in your entire code base with a single call to sed. There is no reason to introduce a language feature with some weird prefix instead of changing existing code and having them prefix their variable names.


> last time I actually tried to use C++20 none of the standard library implementations had std::format; has this changed now?

MSVC is the only major implementation that has std::format for now.


It's so funny to me that MSVC has become the cutting edge of C++ standards implementation. I remember starting out when it was a joke compared to Clang - although I was a novice, so who knows how complete my knowledge was at the time.

In any case, it's a really impressive effort from STL and the rest of their library team. STL has some incredible CPPCon talks, too.


One way you could interpret this (definitely not the only way) would be that Microsoft - or some group within Microsoft - sees C++ as a possible legacy system, with an opportunity to make a lot of money by judging correctly what customers want and how much income you need to justify that support as existing offerings rust out (so to speak).

Do you have any particular CPPCon talks to recommend ?

My favourite is "Abseil's Open Source Hashtables: 2 Years In" by Matt Kulukundis, Matt's a fine speaker but what makes it so fun is that Hyrum Wright is in the audience yelling interjections as a result of Hyrum's law (this is scripted). For example Matt explains a significant size optimisation for people who only have a few things in their map, it's just smaller with no other consequences - right? Hyrum points out that now rehash happens earlier, so if you depend on it not to invalidate references during the first 15 insertions you are screwed. Guess whether any real Google code did that...


I really love this talk in particular: https://www.youtube.com/watch?v=4P_kbF0EbZM (On charconv)

But any of the ones I've seen from Stephan have been fantastic, I think, although I'm not actually sure if I've seen more. He seems to have a lot of talks on very specific subjects, which can be really fun.

> Hyrum Wright is in the audience yelling interjections as a result of Hyrum's law

This sounds absolutely hilarious, I'll have to take a look.


I'm glad you liked my charconv talk! Here's a complete list of my recorded conference talks:

BoostCon/C++Now 2012: Regex In C++11 And Boost: https://youtu.be/mUZL-PRWMeg

GoingNative 2012: STL11: Magic && Secrets: https://docs.microsoft.com/en-us/events/goingnative-2012/stl...

GoingNative 2013: Don't Help The Compiler: https://docs.microsoft.com/en-us/events/goingnative-2013/don...

GoingNative 2013: rand() Considered Harmful: https://docs.microsoft.com/en-us/events/goingnative-2013/ran...

CppCon 2014: STL Features And Implementation Techniques: https://youtu.be/dTeKf5Oek2c

CppCon 2015: functional: What's New, And Proper Usage: https://youtu.be/zt7ThwVfap0

CppCon 2016: tuple: What's New, And How It Works: https://youtu.be/JhgWFYfdIho

CppCon 2018: Class Template Argument Deduction for Everyone: https://youtu.be/-H-ut6j1BYU

CppCon 2019: Floating-Point charconv: Making Your Code 10x Faster With C++17's Final Boss: https://youtu.be/4P_kbF0EbZM

CppCon 2020: C++20 STL Features: 1 Year of Development on GitHub: https://youtu.be/8kjRx8vo6y4

Pure Virtual C++ 2022: MSVC C++20/23 Update: https://youtu.be/DAl37n2XOwk


That's a way better list than what I had! Thanks! Think I'll watch the rand() talk tomorrow, I love (to hate on) rand.


I love the idea of placing a gadfly in the crowd to provide a dialogue.


> "Abseil's Open Source Hashtables: 2 Years In"

URL seems to be https://www.youtube.com/watch?v=JZE3_0qvrMg ?


std::format is available, with all C++20 Defect Reports implemented, in VS 2019 16.11.14 (and all later 16.11.x) and VS 2022 17.2 (and all later 17.x).


The east coast fintech folks are the reason we couldn’t have yield as a keyword. Too much of their code uses the variable name yield in the trading sense of the word, and so we’re stuck with this instead. This is what someone on the standards committee told me on the cpp slack a few years ago, at least.


Just a note but I'm fairly certain this is purely from the perspective of Google Chrome, meaning it excludes google3 (Google's repo for nearly all Google services). I don't know this for a fact but I suspect it's the case. I only bring it up because google3 C++ is (or was; it's now been years since I've done this directly) a very different beast. It was notionally compliant with recent standards but a very restrictive subset of features were allowed.

The C++ standard currently sits at >1800 pages. Looking through these examples, I'm honestly horrified. The semantics around moving and copying and the many ways you can initialize a variable [1] and how you can mess that up so it's actually a copy instead of a move is just mind-bending.

Can we also talk about how in 2022 we're still talking about and getting wrong const-correctness? I guess we're going ever further now because this presentation touches on constexpr correctness (as in const vs constexpr).

Another thought: problems like comparisons between base and derived problems shouldn't even be problems (IMHO) because you've already messed up by wanting that behaviour.

The change about not doing arithmetic on enum values is a good one but pretty late.

TIL this is a valid way to cast:

    size_t{expanded_size}
[1]: https://en.cppreference.com/w/cpp/language/initialization


> The C++ standard currently sits at >1800 pages. Looking through these examples, I'm honestly horrified.

I feel you're embelishing too much your personal feeling of horror. The C++20 standard doc is a hair smaller than 1900 pages, but the complete core language is specified in the first 460 pages, of which around 100 are dedicated to templates.

Thus around 1400 pages of a 1900page doc are dedicated to specify libraries that throughout the years have been adopted by the standard. We're talking about stuff that was released with Boost and since then was deemed appropriate to make it standard.

Focusing on the 460 pages that specify the core language, most of this content has not been changed since C++98. The C++14 doc covered the core language with around 420 pages. Thus it makes zero sense to claim than suddenly C++ became horrifying because of the extra 20 sheets of paper you need to print out.


You could focus on those 460 pages but I'll raise 2 points:

1. There's still a lot of complexity and ambiguity you can fit in 460 pages. This presentation notes one example of decrement operators on volatile variables being deprecated because the behaviour was undefined; and

2. Can you really separate the standard library from the language at this point? Things like move semantics depend on std. Does anyone actually use C++ without any of the standard library?


1. No, that's wrong. Incrementing a volatile isn't undefined. The problem is that some people are using volatile when they actually want atomic variables, so the behavior of the program becomes undefined when you have two threads incrementing the same volatile variable at the same time. The compiler might or might not compile volatile_variable++ into an atomic operation, but some people wrote code under the misunderstanding of the language that it always would.

2. Sure. While it's true that using certain features of the language technically requires parts of the standard library, those parts are very few and very simple. What is there? std::move(), std::pair and std::tuple, <typeinfo>, and perhaps a couple other things?


std::move is just syntax sugar over static_cast<T&&>(t) ; the language feature that needs library support afaik are:

- Overloading some of the "new" operators (need #include <new>)

- <initializer_list>

- typeid which needs <typeinfo> as you said

I don't see which parts of the language need pair and tuple at all?


I was mistaken. I thought you needed std::tuple for structured binding, but it seems the language can also destructure other types.


it's not that you need it, it's that when destructuring, std::tuple_size / std::tuple_element are implicitly checked to see how destructuring can be made to work if you have a type with some custom destructuring. (example: https://gcc.godbolt.org/z/5jq61oox7)

If they are not available or just not overloaded (e.g. when destructuring some basic struct) it just falls back to the destructuring one expects. So you need <tuple> for the specific case of implementing custom destructuring, that is one more case to add to the list :)


This whole thread is why c++ is..bad. It just leaves me with a sense of hopelessness.


> This whole thread is why c++ is..bad. It just leaves me with a sense of hopelessness.

You've tried to force your personal misconceptions as some kind of gotchas that justify you irrational dislike for a programming language, and once each and every misconception you had was dispelled and debunked, your reaction was to double-down on your irrational belief.

This does not flag failures in a language.


? That's their only comment on the entire page.


You're being overdramatic. You don't need to be a language lawyer to use C++ correctly and effectively.


Which language do you use?


> No, that's wrong. Incrementing a volatile isn't undefined. The problem is that some people are using volatile when they actually want atomic variables, so the behavior of the program becomes undefined when you have two threads incrementing the same volatile variable at the same time. The compiler might or might not compile volatile_variable++ into an atomic operation, but some people wrote code under the misunderstanding of the language that it always would.

What you're describing is a different long standing problem. The deprecation of compound volatile operations is because as volatile operations they were nonsense. A volatile operation is a single non-tearing fetch or store, but compound operations by their nature are both a fetch and a store.

By obliging users of volatiles to be explicit with the separate fetch and store, the intent was to highlight that this is not a single operation.

Abuse of volatile to mean "atomic" is so widespread it's how MSVC actually works by default on x86. Access to volatiles with MSVC /volatile:ms - which is default for x86 targets - means Aquire-Release atomic semantics.

Deprecating the compound ops doesn't change that in either direction, it's a bad idea, people, especially Windows developers, will expect it to work, Microsoft will enable it on x86 (but by default not on ARM).


1. The problem you've highlighted (pre/post increment of volatiles) is present in C, and hasn't been addressed there. This is a problem in c2x on compiler explorer for example. So i'd probably say laying this at the feet of the overly large C++ language spec isn't fair. I'm going to probably say it's been there since K&R C days (I think volatile was supported even that far back, but my memory is a bit hazy about such things).

2. This is a good point. I'd counter it by asking does anyone use all of the standard library in a project? If I were to include what i'd ever used, it's probably a subset of the standard.

I hadn't thought about std::move, and actually had to look up which header it's pulled in from, since it tends to get pulled in by other stuff i'm using!


Both the qualifiers const and volatile were new in ANSI C (which became ISO C89). In fact I believe the idea of type qualifiers in C is imported from (pre-standard) C++, thus it's Bjarne's fault.

The general idea (if we force the optimiser to emit the memory access then we can abuse that to do MMIO) is older than ANSI C and as I understand it begins when peephole optimisers begin to first make the "obvious" trick not work in C compilers, but I don't know when it became the volatile qualifier.

As in C++ the correct fix is to use dedicated intrinsics. JF Bastien wrote up C++ intrinsics for this as a template, obviously the C intrinsics would not be a template, but the general idea applies. In reality your hardware does not implement crazy nonsense like a 196-bit unaligned non-tearing memory fetch, so you don't need customisable intrinsics.

e.g. maybe C gets __volatile_load_64(ptr) and that's 64-bit aligned fetch from the address in ptr, there would be a handful you actually need for 8-bit, 16-bit, 32-bit, 64-bit, maybe 128-bit, loads plus stores and perhaps implementations are asked to offer any special cases for their platform, I can imagine unaligned 32-bit is plausible on x86 for example, maybe some DSP has 24-bit, that sort of thing.

The idea is the intrinsic emits the same CPU instructions which are what happens for volatile access today, but as intrinsics they don't give the false impression you can do other stuff, this isn't really memory even though the CPU instructions are memory access instructions.


> 1. There's still a lot of complexity and ambiguity you can fit in 460 pages.

That's a meaningless assertion and reads like a non-sequitur.

Your thesis is that modern C++ somehow became complex. Yet, the truth of the matter is that modern C++ is mostly comprised of formerly third-party libraries, initially released as part of the likes of Boost, that were since then added to the standard. The core language did received much needed improvements throughout the years, such as aggregate initialization with designated initializers, but the total sum of these changes barely grew the standard in around 5% of it's initial size.

Unless you come up with clear examples that you feel support your thesis, you'll have to scratch out your complains as irrational dislikes.


Couldn't you still use move semantics without std::move? You'd just have to write your own cast to an rvalue-reference.


> Couldn't you still use move semantics without std::move?

That's correct, std::move is just syntactic sugar to cast objects to revalue references, i.e., the sort of object that is expected to be moved around.


The standard includes the standard library, a tiny set of pages when compared against Python, Java, F#, C# language reference, VM reference + standard library printout.


This is from Chromium's perspective, yes, but Chromium complies with the Google style guide. I disagree with your characterization that "a very restrictive subset of features" are allowed, though. What's banned is relatively limited, IMO: http://google.github.io/styleguide/cppguide.html


My introduction to c++ was a variant of this book https://www.amazon.co.uk/Sams-Teach-Yourself-21-Days/dp/0672... when I was about 12. However one thing that really stuck with me was a professor at university saying "if you think you know c++ that just means you don't know it well enough to know you don't know it"


Your comment reminded me of a beautiful post by Peter Norvig [0].

He covers learning programming and books like the one you mentioned.

[0] https://norvig.com/21-days.html


The original quote is from Feynman about quantum mechanics.



Very useful presentation. Full of details, but easy to consume.

On voices against stripped down C++ (via code style): I find it working great in practice. Makes the codebase manageable, and keeps people away from using unnecessary complex language features (imagine Java code heavy with streams or reflection, or Python code that resolves most of dependencies at runtime, javascript full of eval(), etc.). Switching to a new language is half-baked plan.


Ad-hoc black or white bans are very retrograde and costly for tje most part and usually stem from purity thinking. Case in point reflection in Java is a godsend. I use it very rarely because it'd uses only comes for very specific needs but when I use it, the alternative either doed not exist or usually would be much more uglier. As for streams well it's just regular functors (map, filter) they are used in every language and are very useful. Now I agree about two things: 1) the stream api is a bit (not that much though) verbose, which significantly contrast with Kotlin. Although e.g .toList() helps 2) yes develpers especially junior ones are eager to abuse functors in an unreadable mess. When there is complexity using loops is usually more readable. Streams however are very fit for regular ETL that represents ~70% of code for most simple apps. The pinnacle of complexity would be e.g. Reactive streams such as rxjava.

I agree python dependencies are a worldwide shame and eval() is very niche (but again should not be universality banned assuming good developers, maybe though one could conditionally ban it aka it would trigger a lint during code review that would need explicit validation.

As for the topic at hands, google style bans are insane e.g. No Exceptions lol


I agree with this comment, and cannot understand why is downvoted. I would like the one that downvoted comments on why. My thinking, more or less in line with the comment is: instead of investing energy, time and resources in writing laws of what is allowed and what no (often without rationale). Use that time, effort and energy in educating developers, so that, if those prohibitions are really sensible, they will anyway refrain from doing that. You get the benefit of not having to change the bans. By doing regular code reviews, you can detect ill formed code, and discuss with the developers. Maybe some developer has something to teach to the "big experts" who write those documents?


What's easier: writing set of rules to drop language features or educating 10k engineers? It's difficult alone to have those engineers follow the style guide (even with help from linters etc).

Average engineer, in any company, doesn't care about the language they use. Just wants to get stuff done. And that's how it should be.


My experience says, if you want to do one project. By all means, it's easier to have a standard where you drop features. -- But beware: you need either good engineers or you have to educate them, anyway.

If you have constant new projects, each one with very different requirements and needs, it will not be so easy to make a standard "one size fits all".

Also, again, you will not be doing "just one" standard, I would not subestimate the effort of such rulesets. You will have to the modify and modify it constantly (I know it from the company I'm, there are whole teams working on that, doing meetings constantly with stakeholders, and replying to exception requirements). In the long run, I would prefer to have good engineers, that do not need to be lead in each little step. --and having to check if they adhere to the rules!

I like the phrase: "If you think education is expensive, try with ignorance" Or: - I'm afraid to waste money educating our employees, and then they leave us - What if we do not educate them, and they stay?!


"no exceptions" is one of the best parts of Google style guide, IMO. Note that, banning of exceptions introduced returning status (error codes done right). It makes it easier to follow the code and makes the code more readable (but, you need a few macros, unfortunately).


In a language like C++ returning status codes means that callers can and will ignore it, even when they shouldn't.


Since C++17, using [[nodiscard]] can help with that.


Indeed, Google's implementation of Status/StatusOr requires explicit handling of those objects, they cannot be discarded automatically.


OK if you like 10-15% performance loss off the top.

And incompatibility with the rest of the world.


> imagine Java code heavy with streams or reflection

I can see Reflection as problematic, but what's wrong with streams?


> imagine Java code heavy with streams or reflection, or Python code that resolves most of dependencies at runtime, javascript full of eval(), etc.

all these things are sometimes the best solution to a given problem


Wow, I didn’t know Google banned exceptions in their C++ code, but the style guide that is linked indicates that they are indeed banned:

https://google.github.io/styleguide/cppguide.html#Exceptions


Yes, they have been banned forever, and not just for size reasons.

In the end, it makes code very difficult to reason about.

It was a deliberate design goal of C++ exceptions that intermediate libraries/callers/etc do not have to be aware of, or handle, exceptions. There is no way to verify or check that all exceptions are caught by someone, etc. This is, as i said, deliberate.

This is quite nice in smaller systems, where you pretty much know every dependency and what calls will do.

But in larger, complex systems, where it is very hard to know or control every level of dependency, it is quite painful and fragile, because something 37 levels deep might suddenly throw new exceptions (or exceptions at all), violate 0 of the API guarantees that are being made[1], and start crashing your program in edge cases.

Among other things

[1] It's very easy for one library to say "we throw exceptions, something must catch them", and the dependent to say "we propagate all exceptions, we don't catch them". Even if something promises to catch them in the middle, it will very quickly get lost as it gets further away from the library actually throwing the exceptions.

The fact that you may be able to successfully assign blame or root cause to the problem doesn't help - being able to say something like "this library 36 levels deep in my dependency tree is not following best practices" is not a particularly helpful thing for development.


This sounds nice but is not true. The bigger the system is, the more value you get from exceptions.

The cost is that cleanup operations have to be in destructors. Do that, and exceptions demand almost no attention.

Problems show up only when some prima donna declares throwing exceptions from what they call isn't allowed.


(Opinions are my own)

Google uses absl::Status / absl::StatusOr as a replacement for what people would usually use exceptions for.

The benefits:

1. As an end user of a large library I can tell you exactly which calls are "guaranteed" to succeed without learning anything else about the code.

2. It is impossible to ignore a Status/StatusOr which makes it impossible to ignore a failure. You have to explicitly ignore the return value which is easy to spot in code review.

3. You can return your exception across RPC an boundary with added detail so if a caller of your service.

4. You can (very easily) enrich the Status with metadata at every single point. So much so that it is very common.

5. You can enrich the way "exceptions" works in a way that makes sense for your company by changing the structure of Status.

Cons:

1. It is more unnatural than dedicated syntax like try/catch.

2. You can't bubble exceptions up the stack automatically so there are macros that do this for you (https://cs.opensource.google/search?q=ASSIGN_OR_RETURN&sq=)


1. you usually capture the most general exception in a lib and avoid all refactorings derived of can/cannot fail deep in a stack of calls that needs to be propagated all the way up. With status it happens the same as with stackless coro. Once something is like that, you need to propagate it all the way up.

2. an exception cannot be ignored either.

3. you can transform an exception into a value or even code them as values.

4. See nested_exception.


You literally provide no evidence of your view, just assertions. Not even reasoned argument or examples. Just assertions. Assertions that lots of people who are serious experts in c++, library design, etc, are prima donnas.

You see how that isn't particularly helpful or constructive to discussion, right?

Maybe you'd like to at least point out the very large scale systems that are using and getting benefits from exceptions, as you claim exists? I can't personally name a single one.


I hope we can all agree that we want the overwhelming majority of the lines of code we write, and of the cycles our programs execute, to be about solving an actual problem representing the purpose the program was written. Lines and cycles fooling with internal junk are overhead, waste.

And, we want our functions to be small, do one thing, and be easily composed.

The problem with Result and their ilk is that they pile on overhead at function-call boundaries, exactly the place where you need design to be fluid.

The great insight behind C++ was that cleanup, packaged and applied automatically, eliminates opportunities for mistakes, freeing up our attention. Exceptions apply that to error handling by running the same, well tested code when errors happen, and gathering error events to a place where we are equipped to do something useful about them.

Now that we often don't even need to code destructors anymore -- the compiler does it better -- our attention is freed for real-world problems. And, not needing to worry about propagating errors up the stack, we can focus on arguments and control moving down and results up.

Attention is easiest to manage in a small program. There just isn't as much to think about. In a big program, with many people involved, it costs a lot more to deal with extra junk, choices about how to bubble errors out. Structuring error handling into destructors and exceptions provides a common framework that burns no discussion time. Everybody gets to concentrate on solving the actual problem, without distraction by incidentals.

We would not be having this discussion if Google had not imposed its rule banning exceptions, which we know was imposed just to accommodate legacy code that did not have destructors. Now they have a thousand times as much code, still without good destructors. Wouldn't it have been less work to fix that code, then, than to impose its costs on everyone today?


Crashing the program on unhandled exception is only default behavior, the program can override it.


Why does this sound familiar?

https://go.dev/doc/faq#exceptions


Go = GOAT


I mentioned to a former team mate who has been doing c++ for 30 years or so that he might take a look at go or rust for a lot of the things they're doing.

His response was, "I already have a new language to learn: c++".


Not mentioned here, but some stuff we bumped into:

* std::filesystem::path broke some APIs with the introduction of u8string.

* some deprecated interfaces of std::allocator got removed.

It was easy enough to resolve and we are in the process of switching to C++20.


Way too many of those points are "this sane looking code no longer works, fix by rewriting it to be significantly longer"


I kinda feel bad for taking this jab at C++, since it feels a bit below the belt... But that's kinda C++'s thing, isn't it?

You can either do the correct thing, or the succinct thing. There's hardly ever a satisfying compromise between the two either. Obviously, we want to do the correct thing most of the time, so that's why C++ ends up being full of ceremonious implementations in practice.

And alas, they're usually equally ceremonious to use, because abstractions in C++ are so incredibly leaky because of its poor type system.

I'm not sure why that is, but my gut tells me it's all this backwards [pseudo]compatibility.


That seems a fair observation to me, with the tweak that "correct" changes over time. C++ code can be written perfectly and still rot as the ecosystem changes, without changing the source.

We are really keen on preserving backwards compatibility, but break existing code anyway. We will not define a stable ABI, but also won't fix stdlib if it breaks ABI.

Also all code definitely has UB in it waiting for a compiler change to expose it as wrong and deserving of no longer working.

Ceremonious captures the state of the art accurately.


>C++ code can be written perfectly and still rot as the ecosystem changes, without changing the source.

But that's true of any system that depends on other systems with lax respect for contracts, or with no contract. You can depend on a third-party function get_time() that returns the seconds since the program started, and later on if its maintainers decide to change it to return a UNIX timestamp because they realized the wording was vague enough to allow it, any code that makes the wrong assumption will break.

C++ is, I would say, quite good as far as backwards compatibility goes. The problem IMO is that it's rather complex and some of its features have been misunderstood over time, such as the meaning of volatile or inline, and thus people have been writing subtly broken code that just happened to work when they originally wrote it.


Eh? There's the NVI comparison operator that fits that bill and maybe the std::forward_iterator one but the rest? Most of the other fixes was less code that's also simpler. Or are you getting thrown off by things like the enum slides where it's 3 different solutions all presented on a single slide?


Expected the worst but pretty happy with Kasting's breakdown here. Admittedly most of these issues are self-inflicted but at least it's better than the usual "C++ bad" meme


I had a great time implementing a C compiler and embedding it in the D compiler so it can import C code directly. I keep toying with the idea of doing that for C++, but since C++98 the language has just gotten too complicated to reliably map onto D semantics.


I have been thinking of this D non-feature when reading up on the Carbon approach. Do you have any takes on how Carbon could be successful on mapping to C++ when D considers it too much of a reach?


I don't know anything about Carbon. But a reasonable approach would be to use an existing C++ compiler and intercept its ASTs.


with import C what is the benefit of creating bindings to C libraries if you can just work with the C code directly?


import C is not perfect. There are pieces of C that don't map directly to D. Such as #define constants and macros.

That being said, it will make writing bindings a LOT easier, even though D already has direct binding to C functions.


ImportC can handle the .h files and/or the .c files, as the user prefers.


As expected modules are relegated to some later time ("will be its own experiment")


Thanks clang lagging behind ISO C++, you can use them today on Visual Studio 2022.


Can't blame them. Modules are Microsoft sabotaging the standard so they can be the only conforming implementation, just like with the Office .doc format.

Modules are unimplementable shite. They would be great if this was the first iteration of the language, but they're a nightmare to fit into the existing ecosystem. If they get support in open source compilers, we can look forward to at least one or two decades of a mixed modules/headers mess in open source projects.


Yet, somehow the GCC folks manage to keep improving their modules support, slow and steady.

If they were unimplementable, there wouldn't exist already two major C++ compilers supporting them to some degree.

It is only clang with their module maps pseudo concept that keeps lagging, that and plenty of other C++20 features.


Implementable by compilers, sure. Implementable by the ecosystem at large, yes, over the course of several decades.

Edit: Also, module support better be 100% binary compatible between GCC and Clang, or it's worse than complete garbage.


As if you could expect any kind of ABI compatibility between GCC and clang binary libraries today, and apparently it doesn't make them a complete garbage, go figure.


umm, you can

edit: to elaborate both g++ and clang++ implement the Itanium C++ ABI[1]. You might get binary incompatibility by mixing standard libraries, so just don't do that.

[1] https://itanium-cxx-abi.github.io/cxx-abi/abi.html


Which is exactly what happens when using shared objects.

Additionally the C++ ABI doesn't tell anything about how each binary library was compiled regarding compiler and linker switches that affect runtime behaviour.


> Which is exactly what happens when using shared objects.

Right. This has nothing to do to gcc, clang and even C++ though. I would be surprised if you could freely link C shared objects linked to different libc implementations.

On most Linux distros both gcc and clang link to libstdc++, so everything works out of the box. I imagine this is not the case for MacOS xcode and gcc from homebrew.


Standard libraries is what makes most of C++.

(Like, literally, going by the size of the language spec.)


And the same applies to C and libc.


What are some ways in which they are bad/broken?


modules were my #1 wanted feature in C++, but they are very disappointing to me:

- they allow `.` in their names to signify hierarchy, but have no built-in notion of hierarchy or submodule. This means possible error messages will have to be suboptimal

- they are orthogonal to namespaces, meaning if you have a module `foo.bar` any identifier it will export will be in the global namespace unless you put it in the `foo::bar` namespace in the `foo.bar` module file. As a user this means that I can import a `foo.bar` module and expect symbols to be imported in any namespace.

- btw, what happens if different modules import the same name? I'll let you guess:-D. Modules were the chance to eliminate this kind of issues by eliminating the notion of shared namespaces, but oh well...

- modules have interface units, implementation units, partitions, interface partition and implementation partition. There must be exactly one non partition interface unit, called the primary module interface. Instead of, you know, just having modules and submodules files like all modern module systems in existence.

- Modules add new and fun ways to get ill-formed, no diagnostic required programs. Such as not re-exporting all interface partitions.

I really hoped that modules were the chance of introducing a simpler compilation model for C++. I'm not pleased with the result.

Some references:

[0]: https://vector-of-bool.github.io/2019/01/27/modules-doa.html

[1]: https://vector-of-bool.github.io/2019/03/10/modules-1.html


That's just usual legacy driven design, like timespec. It's actually a very good move that module separator is '.' instead of something ugly like '::' or even worse ':::'. Not even new languages can easily let go of angle braces and double colons.


Yikes, I was so excited for modules back when I was still writing C++17 and a bit of early 20. Sad they have so many problems with them and/or are just plain not implemented still. Honestly I'm happy I'ved moved on from the language


Popular languages that went for strict 1:1 mapping between modules and filesystem, like Java and Python, often end up moving away from it, or at least adding various hacks (e.g. the way you can split submodules across packages in Python).


I don't know about Java, but in Python, 99% of the modules I create respect the 1:1 mapping between modules and filesystem.

Same in Rust, the overwhelming majority of modules I create is in the standard filesystem <-> modules mapping. For generated code, I use the special syntax that allows not respecting this mapping, but that's once in a blue moon.

IMO, C++ should have taken the same steps: providing sane, correct and easy defaults, while allowing the flexibility to override them when necessary (with special annotations).

I'm disappointed that a modern C++ feature was designed in the long tradition of having bad defaults instead.


Clang had modules ages ago. Still does I think, but not the same thing as was standardised.


What it has are pseudo modules implementated via configuration files called module maps, completely unrelated to how C++20 modules work.


Is one release really causing so many problems? I thought C++ treated backwards compatibility as a holy thing.


From what I can gather, a majority of the problems reported there are new compiler warnings being triggered. Especially if you enable all compiler warnings to catch potential defects, any upgrade to a new compiler version will typically introduce a ton of new warnings that you have to work around.

Edit: There are also quite a few deprecation warnings, which are also not errors per se, but nevertheless you'd want to avoid using deprecated stuff of course.


When you have large codebases, everything causes problems. When I worked with Unreal Engine, minor MSVC releases (same C++ standard, same everything, just going from lets say 17.0 to 17.1) would sometimes make the code not compile anymore. Either because of new MSVC bugs, or because of incorrect code that was previously allowed.


All languages break backwards compatibility, just not to the extent of Python 3.

Heck even Go folks are discovering their stable guarantee isn't as stable as they thought.


Re Go, sure, but most (all?) of those have to do with security updates that may impact features. The discussion is how to make them less painful or opt-in/out in the standard library. None of this has to do with the language itself.


A break is a break, regardless of the cause.


No.

There is a practical difference between a language break and a package break (even in the standard library).

There is a practical different difference between a break due to an explicit security issue and a break due to a design change.


It generally does although they have made technically breaking changes in the past (e.g. `auto`). This is the first release I've used where they broke something in actual code I use because of the comparison operator changes.

However it was easy to fix and actually it was because whoever wrote it did it wrong. Google also found a breakage that was hiding a bug.

So maybe their new stance is "only break incorrect code"? I dunno. I feel like a small amount of breakage is reasonable anyway. Breaking change absolutionists are generally just being dogmatic, and haven't really thought through what zero breaking changes really implies.

E.g. in languages with introspection it technically means you can't change anything.


They also broke the code of the few users that made use of exception specifications.


It doesn't cause problems as in old things don't work as in it takes time to get advantage of newly introduced ones and replace anything considered deprecated (edit: and rename variables clashing with new keywords).


When a new C++ feature is proposed does the proposer have to provide a reference implementation? How can features be missing from the major compilers?


Yes to some extent, depends on how one wants to defend their paper at ISO, during the several voting sessions.

Even when they do, it is in prototype done by the paper's author, not necessarily something that you can merge right away into upstream.

Visual C++ is already there in C++20 and increasingly improving C++23 support as well.


I wonder if a feature has been accepted that turned out to not be tractably implementable!

I work on Ruby compilers and people often suggest features that they don’t realise would be catastrophic for performance if implemented, or are sometimes literally impossible to implement.


Except Visual C++ already has those ideas implemented, so....

Even exported templates, as hard as they were, the EDG folks actually implemented them, only others decided not to follow upon.

The current state with clang is a mix of MIT like license, and those that profit from it not caring about upstream, even GCC is doing better.


I'm pretty sure this rethoric is fallacious, most VMs/languages are not GPL and have MIT-like licenses and yet do not have the issue. It's just that clang lack human resources. Compagnies are not really secretly maintaining their own fork of clang with full support for modern c++. It's not in their economic interest to have to fo all tjis engineering. Instead of malice it'd just plain mediocrity. Yes there are trillion dollars companies that would benefit from better c++ but either they use GCC, either they fail to understand that clang needs funding by pure and quite universal mediocrity. Also the thing is, most languages do not afford to have multiple serious implementations because it is economically absurd, it divide progress by 2 and duplicate the bug surface by 2. GCC at least for the foreseeable future is the de facto C++ implementation.


The facto only for FOSS systems I guess.


Template export was implemented by a single compiler vendor, and that served as a cautionary tale for all other vendors against trying the same.


EDG, right after having implemented export, successfully proposed to remove it from the standard as it was hard to implement and mostly useless!


Does memory_order_consume count?

> people often suggest features that they don’t realise would be catastrophic for performance if implemented, or are sometimes literally impossible to implement.

Oh, that's every language.


Same reason caniuse.com exists for browsers. This just comes from the territory of a language/runtime/platform that doesn't have a monopoly implementation.


No. Ideally there would be an implementation before it ships in a standard but that's not considered necessary.


I stopped using C++ after lambdas were introduced (10 years ago or so?)

Is there anything I can read to get up to date quickly?


I would suggest to go through https://github.com/AnthonyCalandra/modern-cpp-features ; it's quite clean.


Broken link




Bjarne's "Tour of C++", 3rd edition.


@dang: Would it be better to have the first part of the title be "Google Chrome" or maybe "Chromium"? The content doesn't deal with Google's internal codebase.


@dang doesn't work, you're best off emailing hn@ycombinator.com


Writing Chromium grade C++ seems like a hard job with all these extrinsic rules and regulations to make it work


You'll see these kind of rules at every place that cares about their C++ codebase. It's just not a language like Java, C# or JS where you can throw stuff at the wall and it'll probably work out.


> Java, C#

Maybe.

> JS

Hell no. "Frameworks" like React or Vue exist solely for the purpose of retaining developer sanity in the face of unregulated JS code.


Even for Java, C# and JS we do enforce such kind of rules, e.g. https://sonarqube.org


Anyone serious with TypeScript/JavaScript has many Eslint rules to keep the codebase sain.


Is there a case for or against incrementally adopting Rust ?


Which of these things is Rust immune to? How stable is it over a 10, 15, 20 year life? How old can the code be that Rust compiler still compiles and links successfully and bug-free?


Rust does well on this front. There's a new release every 6 weeks, and majority of users jump on it straight away (to complete shock of everyone not used to it).

Rust has editions which keep old code working without any changes, even if you mix it with new code, even if you do it with macros. It has rustfix that automatically migrates majority of the old code.

Rust has a standard project layout, standard test runner, and a central code repository, which enables testing language releases against nearly all publicly available code (see "crater run").

The language itself is stricter, with the safe subset being free of UB, so there's less stuff to break to begin with. You can't suddenly use a moved-from object.

There's even "cap lints" feature that disables `-Werror` equivalent for dependencies, so that new lints don't break your builds.


This how i got traumatized by rust: i had a simple task, 1 day long. Write a routine, use a standard output format, theb parse the results. It was to be presented in front of 20 peers the next day. Decided to try the last part in rust...

Did a few tutorials, not all of them "worked," but i scratched my head and moved on. Started writing the parser. No examples worked. Couldn't put together any reference code. Even copying straight from web pages!

Switched to python. Done in 45 minutes. Told the whole story to the group. Had a laugh. Manager quipped, "that was when you went from leading edge... to bleeding edge." :)

Later i learned rust was still making breaking changes, leaving a wake of dysfunctional tutorials. Canttrustthatlanguage.jpg. Thought i'd go back someday when they get it straightened out.


You were set up for failure: "do something for tomorrow in a new complex technology, and present in front of multiple peers".

Your solution - to use a different technology designed with different requirements, that you were already familiar with, and that you knew could do the job, was the right solution in your situation.

Your conclusion could be revised and improved.


> You were set up for failure: "do something for tomorrow in a new complex technology, and present in front of multiple peers".

Perhaps you missed this part

> > Decided to try the last part in rust...


I don't know when you tried it, but I had a similar need (build a query parser for a tiny SQL subset in a day) a few weeks back, and it took me may be a couple of hours. Actually, picking the library was the more painful part, and this is a problem with Rust IMO, because for most non-trivial new things you (at least I) typically need to dig through crates.io, research, experiment, etc. Anyway, FYI I ended up using PEST (pest.rs) if you still have a need.


Embarrasing that you are so unaware that you present this story as anything but a failure on your own part. Yeah, so you chose a language that you didn’t know for a task that you needed done in 24 hours? Whew, that’s an unforced-error story for the campfire.


I dont feel embarrassed. I'm glad I took the chance. I take chances like that regularly. I don't consider it a failure at all. My team and manager considered me a badass and leader for taking the leap, and we all had a good laugh, too.

"If you want to succeed, you must double your number of failures."

I have had so many suprises and successes by taking chances like that. And even when I don't come out ahead in the short term, I at least get exposure to and practice on new things.


Oh, a badass too? Well carry on then, coding stud.


Editions only work for easy stuff, superficial grammar changes, and in a context where all dependencies are compiled from scratch with exactly the same compiler, aware of all editions.

How many editions are rustc, rust-gcc, cranelift and whatever might come, being still kept up to date in 40 years (C++ age)?

What about all the language changes that actually require semantic changes, how are epochs supposed to deal with inter-editions calls where the epochs have incompatible ABI expectations between caller and callee, regarded expected compiler behavior?


Rust is so trivially updated that claiming using the latest compiler is problematic almost sounds like a bad faith argument. Everyone in the Rust ecosystem updates withing a few days, because of how trivial it is.

Also if you go to GitHub and see a package that's not been updated in 5years, do you think enthusiastically "oh yeah, I'm gonna use this"? Because IMO, if it's not been updated in years, it's probably abandonware.

And another point is that I'm happy for them to deprecate/remove 40-year old (or less even) design decisions that have become outdated. Thinking all design decisions are immune to time decay like is foolish.

The ABI discussion is a bit worn out by now. Everyone will just tell you to use use C ABI if you need compatibility (until something better comes along?), And there's is a plethora of methods for all sorts of languages to help bridge language gaps.


Plenty of enterprise software has not been updated in 5 years, and keeps delivering business value.

Suggesting to stick with the OS C ABI (there isn't such thing as C ABI), assuming it was written in V, for compatibility between Rust libraries is kind of ironic.

It is a matter to which kind of industry domains Rust folks want to cater to.


> Everyone in the Rust ecosystem updates withing a few days, because of how trivial it is.

I love Rust, but this attitude is terrible. Luckily it's also not accurate – it's perfectly fine to stay on your distro's compiler.


Not true. I picked up a git repo 18 months old. I tried to set up my rust dev stack to match the version in cargo. Then hit a lot of issues with dependencies due to abandoned repos used for the crates. I gave up and moved on.


A random person on github abandoning their pet project is not the kind of language stability we're talking about here. It's not the kind of complaint you'd write to the ISO committee.

You may have run into a project using nightly Rust, which is an explicitly unstable version for testing of experimental not-yet-finished features. Using it requires users to intentionally opt out of having language stability. C++ also has GNU and Clang extensions and experimental implementations of not-yet-standardized features.

However, the normal workflow is using a stable version of the compiler. It is backwards compatible with the 1.0 release from 2015, except handful of edge cases that you won't run into an average project.

Users are encouraged to use crates.io and keep Cargo.lock which guarantees they get the same dependencies that worked last time.


> Rust does well on this front.

Rust needs to exist for at least 20 years before you can say it "does well" on 20yo projects.

> majority of users jump on it straight away

This is a sign of a language with a tiny mostly-hobbyist user-base.


> This is a sign of a language with a tiny mostly-hobbyist user-base.

Or that the tooling is so trivially easy to update that people don't need to think about it. Sometimes if I want to upgrade GCC I need to upgrade the whole os, or have multiple versions installed and be careful where they're installed lest I incur Ubuntu's wrath


> Rust has a standard project layout, standard test runner, and a central code repository, which enables testing language releases against nearly all publicly available code (see "crater run").

I'm not sure why this is treated as a selling point. It helps projects up to a point, but the minute you want to do something that doesn't fit the mold, it makes things a lot harder. All of these products are orthogonal to a language, and yet they are very tightly bundled with the idea of using the Rust language. It certainly makes "business logic" systems and web backends easier to implement, but I'm not sure many people were building those in C++ to begin with (except in legacy systems, where you are stuck with what you have).

Also, I'm not sure how a language that is less than 10 years old (in its released, 1.x versions) has any claim to having no problems with evolution over the next 10 years.


> the minute you want to do something that doesn't fit the mold, it makes things a lot harder

This is not my experience at all; it's been extremely helpful for doing things outside the mold:

In a work codebase that is millions of lines of first-party Rust code with thousands of third-party Rust dependencies from crates.io, we're building with Buck not Cargo because that is what the rest of the monorepo uses for C++ and other languages. It's fantastic for this that all the third-party projects describe their dependency graph in the same standard Cargo manifest format and follow a standard project layout, even though our builds do not use Cargo, because we can programmatically translate them to Buck targets.


How on earth can you predict that rust will be stable in 20 years?


Ok here come the rust apologists.

Well when rust makes changes every 6 weeks we all hold hands and update all of our code.


Don't worry your little brain about these silly things. Programming is hard, let's go (language) shopping instead!



Is there any common module that can be shared amongst browser engines? I can feel though it sounds hard to extract these common stuff into .. "browser engine common core"? (e.g. https://github.com/SerenityOS/serenity/tree/master/Userland/...). That would be nice for next gen browser invention.


You can find some module candidates here https://github.com/servo/servo/issues/24026#issue-483508434 The one that make the most economic sense would be for mozilla to drop spidermonkey and make v8 faster instead


Betting on carbon would allow transparent FFI like Kotlin, typescript or graalvm achieve, which is economically disruptive. Rust seems more like a plan B


Rust is too hard to learn.

I like it, but it only aims at replacing ada, not c++.

I asked around and there is currently no good way to make modern native ui apps with rust.

It would be okay if the syntax of rust looked a bit more like C, but it doesn't, it can be a bit difficult to read.


Rust's learning story has some interesting dichotomies in it. For sure, no one disputes that learning Rust is harder than say Python or Java. But how does it compare to C or C++? My take (which I have no proof for, to be clear) is that learning Rust is much easier than either of those if you're truly starting from zero. Some scattered thoughts about this:

- Experienced C and C++ devs have to unlearn certain patterns when they start Rust. C++ experience can be a huge help, but if you insist on using the patterns you're used to in Rust, you often have a terrible time and feel like the compiler can't handle useful programs.

- Particularly with C, what we usually mean when we say "learn" has gotten kind of out of date. If someone with a few years of programming experience says they "know" Python, we might assume they can make an HTTP request and parse some JSON with a couple minutes of googling the relevant API docs. But of course in C, those tasks are much more challenging, and we often allow that someone has "learned" C even if they can't do those things without great difficulty. Part of C's reputation for being (comparatively) easy to learn is that we don't expect programmers to be able to do the same variety of tasks with it.

- A lot depends on what standard of correctness and security we want to apply. For example, writing multithreaded code in Rust has an extra steep learning curve, but the resulting code is data-race-free once it compiles. Writing big multithreaded programs without data races takes many years of experience in C and C++, and it might be genuinely impossible for sufficiently large projects. So depending on what we understand "learning to write multithreaded programs" to mean, we could say that Rust is much harder but also that Rust is much easier.


Slide 37: can't even use ++ anymore. Ouch. (at least on volatile, but still)

Best line in the deck:

"Only write complicated code when you truly need performance. Comment if you do...."


> Problem: Early test showed release binary size increased by 0.9% (Win)

I guess once again you do actually end up paying for what you don't use.

> Solution: Don’t care?

:(

Would have been nice if they at least analyzed where the increase was coming from. Maybe operator<=>?


It is so ironic, that now that Apple and Google decided to focus on their own language stacks, the C and C++ compiler vendors that profit from clang's license aren't that keen in making the upstream work for catching up with ISO C++.

Thus making the once famous clang having an honorable third place in ISO C++ compliancy.

Seeing this from a Google team makes it even more ironic.


I think the main differentiators for clang were more readable error messages than the mess GCC had (and still has), and faster compilation (here it lost a bit of its advantage over time). I haven't heard of anyone praising it over GCC based on standard compliance.

That said, when I explore weird edge cases in how differently Clang and GCC parse source code, and how differently they optimise it, in my experience Clang is still the more logical one, whenever there is a disparity, whereas the way GCC parses code and what code it produces is sometimes completely baffling (and that's not just optimisation stuff - without any optimisations enabled I encountered miscompilations a lot more with GCC than Clang).

I also consider C++ a language for mostly old projects. I start new ones in other languages and don't consider repeated rewrites every two standards, because the "modern c++" crowd found a new way to initialise variables, to be a good investment of my time.


> I haven't heard of anyone praising it over GCC based on standard compliance.

Back in the day (C++0x era), it was near bleeding edge. Clang had all the proposed 0x features ready to go before C++11 was released.

It took a while for GCC to get all the C++11 features after the release.


> Clang had all the proposed 0x features ready to go before C++11 was released.

Except garbage collection, of course.


> I start new ones in other languages and don't consider repeated rewrites every two standards, because the "modern c++" crowd found a new way to initialise variables, to be a good investment of my time.

Same here, but I update my new code as I go if there is a better/safer way to do it that does not impact in any bad way my codebase.


You don’t have to rewrite anything. C++ keeps backward compatibility. You don’t have to make it sound like it’s the case, and you don’t have to shame the "modern c++ crowd" over that false claim. That’s just uncalled for, unconstructive and partisan discourse. I expect better from HN.


Did you see the original post? Lots of code changes were required for Chrome. That’s not backwards compatible.


It’s 99.9% backward compatible. The comment makes it sound like it’s a huge pain every 2 years, as if it was the "python 2 to 3" level of pain.

I went through a lot of these new versions, starting with c++11, and can’t remember one time I had to change anything to my code based, appart from silencing deprecation warnings for unicode stuff.

Now I get that chromium-like code bases are huge and that the 0.1% of backward incompatibility is still a pain. Now, with that level of hugeness of code bases, maintaining it when the language evolves is going to be a pain. C++ has been very careful with backward compat, breaking very few things, compared to other languages. So these criticisms really are uncalled for to me. Sounds like good ol c++ bashing trying to sound wise.

I felt the comment wasn’t fair and wasn’t an honest criticism. Sorry for my angry comment. We live in rough times.


I've done several Python 2->3 transitions, and the amount of effort looks to be in the same order of magnitude described in the post.


This has no relation to Clang's permissive license. The problem here is generally that nobody has implemented the necessary support, not that they have implemented support in their private forks but did not bother to upstream it. A GPL-style license doesn't magically force contributors to start working on a particular feature.

Google at least is one of the largest contributors to the LLVM project -- it's just that their contributions don't tend to focus on Clang frontend work.


Anymore. Google used to contribute quite a lot to libc++ especially, but have found that the language is going in a direction that doesn’t meet its needs.

Not sure why Google would continue to contribute under those circumstances.


The thing vendors care about more than anything is making upstream work supported.

Google isn't even complaining about things, this is a very dry technical document about their experience, for an amount of work they certainly expected and were possibly even pleasantly surprised about.

These sorts of things are routine in massive code bases


Why is it ironic?


gcc is better than clang on every metric. Sorry, it's the facts. ¯\_(ツ)_/¯


Whoa, really? Since when??

After a lifetime of using GCC, I (like many others) moved to clang a few years ago, out of frustration with the slow development of GCC, a desire to use new C++ features, and stayed because of the superior error messages and, in my use cases anyway, superior code generation.

In addition, I gather it's a much cleaner and easier to maintain code base. As a result, we get to have cool things like emscripten, llvmpipe, all sorts of static analysis tools that would be more challenging to build in the GCC universe, and much more.

Honestly, I thought GCC was slowing down development wise.

Is GCC worth trying again? Can you name a few "cool new things" I can do with GCC that I can't with clang? There's plenty of the opposite...


LLVM always delivers the cooler stuff I'll admit but GCC is ahead as an optimizing compiler, general lang support and generating cryptic messages.


Interesting, I've generally found that clang is better at optimising, especially when it comes to auto-vectorisation.


Agreed, autovec is in my experience where clang really shines.


Gcc pretty consistently delivers slightly faster code.


Not in my experience! Though it was a few years ago, I'll have to re-evaluate. In the past, when doing numeric/scientific programming -- lots of dataflow, it was much easier to "coax" clang to generate the right (performant) assembly than GCC. I guess my use is less typical -- the internet agrees with you. :)

Hurrah for competition!

(of course, neither GCC or clang could _ever_ beat ICC at some of my tests.. grumble)


"(of course, neither GCC or clang could _ever_ beat ICC at some of my tests.. grumble)"

They never tried, honestly - ICC had plenty of defaults that were targeted at performance above correctness.

That's fine - but it wasn't what either clang or gcc was going to go for. Even when trying to compare apples to apples, folks often compared them by trying to get GCC/Clang to emulate the correctness level of ICC (IE give GCC/Clang more freedom), rather than the other way around :)

Which, again, similarly understandable, but also a thing that you'd have to spend a while on in GCC/Clang to get to a reasonable place)

Small-number-of-target compilers like ICC are also fundamentally easier. Lots of techniques (IE optimal register allocation[1], etc) that add up to performance gains are a lot more tractable to really good applied engineering when they don't have to be so general.

Similarly small-target-market compilers are also easier. Over time, high performance was literally ICC's only remaining market. So they can spend their days working on that. GCC/Clang had to care about a lot more.

[1] This happens to now be a bad example because advances finally made this particular thing tractable. But it took years more, and feel free to replace it with something like "optimal integrated register allocation and scheduling" or whatever is still intractable to generalize.


You sound like you know what you're talking about.

> They never tried, honestly - ICC had plenty of defaults that were targeted at performance above correctness.

Is it possible to get ICC level performance out of open source tools? Much of my CPU bound work relates to array signal processing, which, if you can code it right :) lends itself heavily to SIMD branchless pipelines. Plus some scatter gathers on a group of other cores to calculate a sparse crosscorrelation tensor.

I would love to be able to get same or better performing code out of clang or gcc, even it it takes 2-4x more work than with ICC...


This is often true for SPEC, but not true for real world code.

Or at least, we extensively tested this at Google, before, during, and after the move to LLVM.

On many thousands of libraries, binaries, etc, made up of hundreds of millions of lines of C++.

While there were wins and losses, on average, LLVM was a consistent net positive.

That was true despite having spent 5+ years of having a large team dedicated to doing nothing but finding places to improve performance of GCC compiled code (and contributing back patches), and doing so very successfully.

That said, as time approaches infinity, the compilers are going to generate the best code for the things that someone took the time to analyze and make the compiler better at.

There is, in the end, no magic that makes GCC better than LLVM or vice versa. The vast majority of it is tuning and improving things little by little for whatever targets someone is trying to improve.


I can only go by the results I measure. There certainly are cases where Clang does better than Gcc, but the difference is anyway rarely more than a couple of percent. Occasionally one finds a factor of two one way or the other, e.g. where one compiler has noticed it can use CMOV without penalty.


proof?


Keyword there is "slightly".


C++20 support comes to mind, the plethora of supported hardware as well.


> C++20 support?

Not that I think it matters but this is, of everything, the strongest argument to me. For shame, clang, for shame! I switched because it had better modern c++ support. Guess the winds are changing.


GCC is quite up to date. Also, the code generation used to be better. I do not know right now. But I think it still might have a slight advantage.


I've always preferred clang for its better lints, and friendlier error messages (sans some decrepit parts around templates that are equally horrendous everywhere)...

And theoretically clang has better ASM output in some cases I say theoretically, because it's been shown that GCC's "worse" ASM performs better; I'm not really an architecture aficionad, so I can't comment as to why that is.

Also, it's been a few years now since I did C/C++. So, maybe these are no longer the case.

Anyway, I've kinda pointed out what I like about clang over gcc, but I'd be curious what you prefer in gcc.


I wonder how up to date is the old belief of better error messages. The was some great redhat blogs about structured error messages in newer GCC.


GCC has gotten better indeed, but it's still in a different league than Clang. I still get into situations where I can't make heads-or-tails of what GCC is saying to me, which can usually be easily solved by switching to Clang.


One problem here is that GCC emits certain warnings as part of the optimizer, which results in many false positives that are essentially impossible for the lay programmer to understand. For example, jump threading might duplicate some code path and propagate constants in it, and then warn about an uninitialized variable / out of range access / etc in that code-path, even though it does not exist in that form in the original program.


Funny enough, this is exactly the kind of situation where other people cry "why did the compiler not warn me about basing optimizations on UB".


I don't do C++ anymore but I will forever remember the Vtable hell "messages" when doing OOP and doing a slight unintuitive mistake about destructor or constructor. Is this still a thing in clang?


Ok, that's kinda true I suppose; I remember that GCC's error messages have indeed gotten better after a major version release.

Also, when you say structured, do you mean errors over LSP, or do you mean more structure in the formatting when reporting in CLI?


GCC returns pretty nice, colored and structured error messages in CLI for quite some time.


I meant in cli a bit like rust arrows logs


clang doesn't output asm


I'm not sure what you mean? You can use `-S`


What is the status of ubsan, msan, tsan and others support for GCC though? Last time I checked they were a bit behind. I agree GCC make clang obscolete regarding C++ support and even performance. I don't know a comprehensive alternative to clang linter but I'm sure there are a few.

Given that llvm receive more human resources than gcc by far, I once expected it to outperform gcc generally (e.g support for polyhedral optimizations, BOLT, etc) Unfortunately weirdly it seems llvm performance is mostly stagnant. I personally suspect we are reaching increasingly diminishing returns with AOT and that the performance graal would be a hybrid that also does JIT at runtime (beyond PGO therefore) and more interpretable than BOLT


At work GCC is a lot slower than clang, the project uses a bit much template magic but still, clang is faster.

Where does this GCC is faster thing come from? I personally havent experienced it


I mean faster runtime performance, I have no clue about compilation time. Well I'm basing this on the countless benchmarks I've seen, e.g. on phoronix over the decade. Also you have to understand that Clang -O2 is (was) "unfair" as GCC did not enable autovectorization until -O3. This has (is being?) changed.


Old hype thing created by gcc fanboys :)

They always claims so.


C++20 is hard, but Peter Kasting's presentation is really easy to understand. I learned a lot.

Also, I don't understand why there are so many negative views on C++. C++ is a beast, but somehow you can control it, and C++ is too popular and still in use to be replaced by anything else in the near future.


Say I have a new project to start today. I need pick a language to use:

  1. a well-tested language
  2. can not use garbage collector due to *performance* requirement
  3. easy to hire if project expands.
  4. ready to use tooling support
  5. widely available tutorials and info on the web.
  6. language is itself alive and updated
  7. project can be scaled over time.
what options do I have? I have to pick up c++ in this case. it falls to the saying "a language is either blamed, or nobody uses it".

Javascript for the web, Python for machine learning, C for low level and system coding, Go for some native cloud and DevOps, Java for enterprise or Android, C# if you're a windows developer, Swift if you are doing Apple, we actually don't have a lot of choices when you need deliver things faster.

I played with nim, ziglang and Rust, but it's hard to use them for real product developments at this point for me.


I think depending on the type of project, Rust might already be there. A couple of bullets raise questions:

> ready to use tooling support

This really depends on the use case. If the tooling you're looking for is "Unreal Engine", then Rust is definitely not there yet. But if you want to write network services, databases, CLI tools, etc., I think Rust has been fully viable with good tooling and library support for years now.

> easy to hire if project expands

No doubt hiring experienced Rust developers is hard, because there aren't that many yet. But anecdotally, training Rust developers is much easier than training C++ developers. Rust makes it easier for one or a few experienced folks to have confidence that a larger number of less experienced contributors aren't introducing stability or security issues.


And also you have to check the quality, not only quantity... yes, you get many people if you search "c++ developers" but how good are they? I think people who are into Rust are very motivated, curious people, with some experience. I would really like a study of that, btw :)


As somebody who writes C++ for my job, I don't think the tooling is particularly good so it's funny to me when people talk about that. Languages like Java or Python have good tooling, but C++ is a dumpster fire in terms of tooling.


>Java for enterprise or Android >C# if you are windows developer

Picking Java over kotlin for android, or lumping C# into "just for windows" bucket is exactly the ignorance that leads you to "we don't have choices" conclusion. If you always default to literally top1 language for given domain, then how can you expect to have multiple choices?

I'm not talking about using some brand new gimmick that came out yesterday and will die tomorrow, but if you aren't able to leave your comfort zone even just-so slightly to update your stereotypes about given given domain and its trends, then by definition you won't see the alternatives.


TBH I was shocked by "Python for machine learning". I do not know where that comes from. Maybe because some important toolkits are available for Neural Networks? But machine learning is not neural networks... you can do it with NNs, but there are so many other algorithms... I'm really surprise of this kind of posts in hacker news. I thought the audience here has other experiences.


Python for machine learning is a coincidence of history.

Python is the traditional scripting language for high-performance computing (HPC) going back a very long time, as a wrapper for highly optimized and scalable C libraries. Python on supercomputers wasn't originally used for machine learning but it naturally included support for excellent linear algebra libraries, etc for various types of large-scale modeling. When machine learning first became trendy, data scientists that wanted access to mature library implementations of critical algorithms that worked very well at large scales were mostly limited to C/C++ or Python wrappers of that C/C++. Naturally, they chose Python for the same reason Python was already being used in HPC -- ease of use.

By virtue of its use in supercomputing, Python had uniquely strong support for many types of machine learning before there was a demand for machine learning. If HPC had used some other popular scripting language instead of Python, we'd probably be using that for machine learning right now.


That's literally the only one that didn't shock me. What universe do you live in where machine learning algorithms aren't tied together with Python? And what industry where machine learning is not mainly neural networks?


In the company I work most of it is done in matlab for "putting together" then goes to C++. Again not neural networks. I'm saying all the other machine learning. Maybe rare case.


What kind of machine learning does your company do? It's probably something simple, because for anything complicated, like computer vision, speech recognition, any non-trivial NLP, and large scale recommender systems you do need neural networks.


Autonomous driving, including, for example, computer vision. No NLP or speech. About the "need" or neural networks, there is a lot of discussion going on. As you may know, the "black box" nature of NNs make them a little more difficult than other means, when you have to validate them for safety critical systems.


If you use CV for autonomous driving you use object detectors, and any modern object detector uses a neural network as its backbone.


Used yes. But not the biggest part. Maybe 10% at most.

We are doing L4, so if something goes wrong, the manufacturer is responsible. Once we were in a test car. It was behaving great. At one point, the car "makes the decision" to accelerate and pass. The manager to which we were showing asks "we did the car not waited a little bit? can we change that behavior?". A very long discussion ensued, about data sets, labeling, training... long story short: we have to be able to change behavior (even for perception) in a deterministic way. So the amount of NNs went down dramatically after that.


This is a good point, and I expect in the near future we will be able to simply ask a neural network to explain and change its behavior without retraining. I already see signs of that in prompt engineering used to interact with GPT3 or Dalle2.


It's just going to be as good as asking a human to explain its behaviour. It might be a semi-accurate interpretation of its actions based on its internal knowledge, but it's never going to be the actual thing. The actual decision making inside a neural network is fundamentally not something you could simplify into language exactly.


Yeah, it's not clear if we will be satisfied with those explanations: "I decided to slow down because I see these (seemingly random and irrelevant) objects around me", because during training on trillions of video frames the model has learned it should slow down when similar object configurations are present to reduce the chance of an accident by 0.0002%.

Even if the given reason is simple, like "I decided to slow down because the car in front of me is red", explicit override of the learned rule ("don't slow down when you see red cars") might potentially increase the chance of the accident by a lot more than 0.0002% because we are messing with the model decision making process.


Python is the main language for PyTorch and TensorFlow, there are few ML frameworks more complete than Scikit-learn, I'm not aware of an equivalent to NumPy in other languages, and this list is probably missing other things I don't use.

What's the alternative for quick ML prototyping?


R, Matlab, Julia are some common alternatives.

Java/Scala I've also seen, specifically things around Spark ML, and Spark compute.

That said, Python is definitely the most common nowadays I'd say.


same can be said to typescript, did not mention them to make the point.


> I played with nim, ziglang and Rust, but it's hard to use them for real product developments at this point for me

What made it hard? For example, Rust seem to fit your bullet list.


> what options do I have? I have to pick up c++ in this case. it falls to the saying "a language is either blamed, or nobody uses it".

You could just use C.


or use a subset of c++ that does everything c does, plus OOP, RAII and smart pointers for free, and it compiles and links with c code smoothly as well. the only price to pay is that libstdc++ runtime must be present, which is just a few MBs that can even fit for small embedded boards.


I've written firmware, systems software, drivers and plenty else this way. Just because the language gives you rope doesn't mean you _have_ to use all of it. I enjoy how terse and straightforward even good ol' structured non-object oriented code can be.


"Object-oriented" is a niche technique.

Any big-enough system will naturally have some OO-ish parts, just because it has some of almost everything. Most small programs will have none.


Exactly. I couldn't have put it better myself, thanks.

They're techniques after all, models, tools.


same here, but I'm embracing modern c++ these days, not for all low level coding yet though.


Yep, big thing for me is smart pointers and RAII. Everything else is secondary.

I have worked on template-heavy codebases that would really benefit from concepts, though.


Smart pointers and RAII do _so much_! I really like zero-cost abstractions, like a nested scope with a RAII object (which will get optimized out) to implement a BEFORE{} and AFTER{}.

That plus structured bindings return types. :)


This is what I do. Write C in C++. You don't have to use all the fancy new shit. I'll never understand why people get so upset about it.


Then you are writing bad C++ code.

The new features are added specifically because they enable writing better programs. Avoiding them means you are choosing not to write better programs. It is allowed, but not a thing to brag about.

That does not mean every program has to use every feature. But when there is a choice between the new way and the old way to do something, the new way is very probably better.

Passing a reference, where you can, is better than passing a pointer. Passing a lambda is better than passing a function pointer. New for-loops are better than old for-loops. A unique_ptr is a better member than a naked pointer. A compiler-generated destructor is better than a hand-coded one.

And so on.


Absolutely, but none of those are really that new. I guess I'm talking more about boost libraries and that stuff. I just can't be bothered and it doesn't matter for anything I need to write.


They are not especially new, but they are not in C.

C is the language that invites in mistakes. The less C we code, the fewer mistakes we ship.


That C in C++ id the part where most of the serious bugs in C++ come from


Honest question: is it easy to find good C++ programmers these days?


I think it is significantly more difficult to find programmers with domain expertise in the kinds of applications C++ is often used for than the language itself. There are quite a large number of good C++ programmers who have never worked anywhere close to the metal, OS, high-performance, etc, and those skills are often more difficult to learn and come by than the language.

A large amount of C++ development is still focused on enterprise and UI apps, even though one could argue that it is not the best language for that purpose.


that's a good question, I have yet to find a good beginner friendly modern c++ education material, be it a book or some course, that can make c++ learning simpler, to the point and enough to start to work with the language before diverting into those deep concepts.

Someone needs to write a c++ 101 book, followed by 201 and 301 for beginner, intermediate, and advanced users.


“C++ Crash Course” by Starch seems to be such a beginner’s book, can’t recommend it enough. Although, I was learning C++ mostly for hobby projects.


a good book indeed, hope it will have an updated version for c++20/23, which arguably is the most important release since c++11, new stuff such as ranges, std::span even concepts could be added now. I think this book is a bit tough for beginners though.


In short, no.

But then, it's hard to find good programmers for any language, so C++ isn't really any worse off here, and better off than some in that at least there's a bunch of candidates to pick from.


Seems like there's plenty, but a lot of em' are already gainfully employed by Google, MSFT, FB and co.


The good ones mostly leave those shops after a few years. Beware of anybody who looks back on them with fondness.


The way I look at it is we're in a transitional period. A language like Go or Rust can replace some of the C++ lift, but we're not sure because they're not a large body of experience with those languages. I suspect, but can't say with any certainty, that we'll wind up in a world where the use case for C++ shrinks significantly. Rust and Go will eat into the share of new Greenfield systems that would have normally gone to C++ or even C.

I like to live in the embedded world and there I think Rust will shortly start to push out C and C++ at the wide-spread, commercial level. This is where my head is at, maybe it helps you formulate your opinion:

First, embedded C++ is not the same a full C++ (with features or library parts missing to accommodate the constrained environment). So I might never see some of the C++2x or even C++1x features. I really don't care about the C++2x standards since I might not see them in the next 10 years.

Second, memory and concurrency issues are real, even when you don't allow dynamic memory allocation after the system is initialized. We test, then test, and then run more tests, and turn on a lot of linters and analyzers but at the end of the day, they're bandaids. You can write terrible code in Rust, but you have to work harder to do it.

Third, if you really need an environment where memory is as fungible as play dough but want something portable (like inside a kernel), there will always be C. Setting up a C callable environment from assembly is well understood. In theory, calling into Rust shouldn't be any harder. But reading disassembled object files from C is generally reasonable assembler for the C I wrote. I haven't really tried doing that with Rust.

Fourth, it will take 5 years or so, but eventually we'll see commercially supported RTOS implementations for Rust that help companies to shield themselves from liability. For example, something like Threadx (AzureRTOS now). This will help push money in a virtuous cycle of better tooling -> more projects -> more money for tool venders -> better tooling.

Fifth, there will be Rust bindings to cover just about everything you do with C++ now (like game development) with high quality, idiot-proof, and portable semantics. You will find that junior developers or fresh-outs will have their skin crawl when asked to write "unsafe" code.

Sixth, there's counter evidence to support a lack of adoption of new languages. Ada provides many of the same concurrency and memory safety guarantees as Rust, and has been available since 1983, but it is not widely adopted. It is harder to use and requires significant developer training. If Rust is significantly more challenging for developers, it may go the way of Ada.

But that's in the future. If I had to start a new project today (for a braking system on a train, for example), I would start with C, a validated RTOS, and commercial toolchain. If I had to light up my kid's halloween costume? Embedded Rust as a learning exercise. The rendering engine for a VR headset (with limited batter and compute), probably C++.


Go isn't a competitor to C or C++. I don't know where that idea even comes from. Go is competing with Java and web languages.

Rust is the only real competitor, but given the sheer amount of existing C and C++ code, the idea that they will become legacy languages any time soon is pretty out to lunch.


From Rob Pike - One of the creators of Go. see https://commandcenter.blogspot.com/2012/06/less-is-exponenti...

A lot of command line tooling is being written in Go along with systems software (like basically all of container land). Maybe some of these would have otherwise been written in C or C++. For example, if you use the Azure storage GUI to manage storage the underlying "thing" that copies data from your filesystem to azure storage is written in Go and that's why it works on Windows, Linux, MacOS. C++ is somewhat more portable than C so maybe something like that would have been written in C++? Who knows? But Go is definitely showing up in all sorts of native, command-line stuff. And Rob Pike - one of the people who developed Go - intended it to be a new home for C++ developers as well.


> Sixth, there's counter evidence to support a lack of adoption of new languages. Ada provides many of the same concurrency and memory safety guarantees as Rust, and has been available since 1983, but it is not widely adopted. It is harder to use and requires significant developer training. If Rust is significantly more challenging for developers, it may go the way of Ada.

Rust is definitely difficult to learn, but I'll anecdotally say that Rust helped get me into the embedded world as someone with very little C or C++ experience. Unlike Ada, Rust has a modern toolchain and type system that feels very comfortable to developers coming from higher level languages, even if many of the concepts related to manual memory management remain quite difficult.


> Unlike Ada, Rust has a modern toolchain

We have a modern toolchain too :)

    # install compiler and build tools
    alr toolchain --select

    # make, build and run a project
    alr init --bin my_project
    alr build
    alr run


In the aeronautics and space industry Ada was and is popular. Ok, not the biggest industry, but still...


It's a very good language. I learned it graduate school in the mid 1990s. I personally think the learning curve is not particularly steep. But it seems like most developers and the industry would rather enforce coding standards to nerf the foot-guns in C/C++ (https://news.ycombinator.com/item?id=7628746). With Ada there would still be coding standards but I would wager that it wouldn't be as much about inherent problems in the language.

They might both have a rule like: AV Rule 1 Any one function (or method) will contain no more than 200 logical source lines of code (L- SLOCs).

But Ada would probably not have something like: AV Rule 59 (MISRA Rule 59, Revised) The statements forming the body of an if, else if, else, while, do...while or for statement shall always be enclosed in braces, even if the braces form an empty block.

Or: AV Rule 193 (MISRA Rule 61) Every non-empty case clause in a switch statement shall be terminated with a break statement.

Those two arise from deficiencies in the basic C/C++ syntax. They are probably not fixable in a future revision of the spec because they would break too much code. So we create a coding standard, configure a linter to check for it, wire up our source repository to scan for it on checkin, and make all the leads code review for it.

Ada might have different language specific standards. For example, around not allowing breaks out of infinite loops. Requiring, instead, a loop construct with a testable condition. But even then, you can pragma things out of the compiler to help you enforce behavior. That's "built in."

My comment with Rust is not that Ada is bad or too hard, but rather even a small amount of additional work might be enough for developers and organizations to avoid Rust. There's a lot of Rust advocacy, which is a good thing. However, there was also a lot of activity on comp.lang.ada.advocacy (if I remember the newsgroup correctly). One good thing is that Ada felt "imposed" by a number of people and that automatically stimulates a kind of rejection response. Rust's introduction is definitely more "bottom up."


Rust can't easily bind to many modern, high performance C++ libraries because it lacks a fair amount of the semantics of C++.


I think that may be a red herring. We normally don't link to name mangled C++ libraries, anyway. Most libraries export their useful symbols in de-mangled format for linking to other languages. That way you can basically dlopen and look for the symbol. There are some libraries that are really only intended to be called from C++ and I don't expect anything other than C++ to use those libraries. I'm not sure if Ruby or Python (for example) can deal with mangled names. I think there are some tools that will generate wrappers to make them callable from things that don't understand mangled names, but if you want to make your stuff interoperable - make it C callable.

It isn't that hard to define something like MyType_t which is a pointer. Then some C code to allocate to wrap the MyType so the type is usable from any language.


I don't think this is a material limitation. Anecdotally, we have few issues with sensibly blending C++20 and Rust. Any exported APIs will need to be circumscribed anyway for integrating with other languages.


I wonder how much of (2) is speculative and how much of it is a real need in actual projects.


The negative performance impact of GC in performance-engineered code is neither small nor controversial, it is mechanical consequence of the architecture choices available. Explicit locality and schedule control makes a big difference on modern silicon. Especially for software that is expressly engineered for maximum performance, the GC equivalent won't be particularly close to a non-GC implementation. Some important optimization techniques in GC languages are about effectively disabling the GC.

While some applications written in C++ are not performance sensitive, performance tends to be a major objective when choosing C++ for many applications.


When people complain about "negative performance impact of GC", often they're actually bothered by badly designed languages like Java that force heap-allocation of almost everything.

I think this might have been fixed in latest versions of Java, though, not sure if value types are already in the language or just coming soon.

Aside from that, it's my understanding that GC can be both a blessing and a curse for performance (throughput), that is, an advanced-enough GC implementation should (theoretically?) be faster than manual memory management.


In theory, a GC should never be faster than manual memory management. Anything a GC can do can be done manually, but manual management has much more context about appropriate timing, locality, and resource utilization that a GC can never have. A large aspect of performance in modern systems is how effectively you can pipeline and schedule events through the CPU cache hierarchy.

There are a few different ways a GC impacts code performance. First, even low-latency GCs have a latency similar to a blocking disk op or worse on modern hardware. In high-performance systems we avoid blocking disk ops entirely specifically because it causes a significant loss in throughput, instead using io_submit/io_uring. Worse, we have limited control over when a GC occurs; at least with blocking disk ops we can often defer them until a convenient time. To fit within these processing models, worst case GC latency would need to be much closer to microseconds.

Second, a GC operation tends to thrash the CPU cache, the contents of which were carefully orchestrated by the process to maximize throughput before being interrupted. This is part of the reason high-performance software avoids context-switching at all costs (see also: thread-per-core software architecture). It is also an important and under-appreciated aspect of disk cache replacement algorithms, for example; an algorithm that avoids thrashing the CPU cache can have a higher overall performance than an algorithm that has a higher cache hit rate.

Lastly, when there is a large stall (e.g. a millisecond) in the processing pipeline outside the control of the process, the effects of that propagate through the rest of the system. It become very difficult to guarantee robust behaviors, safety, or resource bounds when code can stop running at arbitrary points in time. While the GC is happening, finite queues are filling up. Protecting against this requires conservative architectures that leave a lot of performance on the table. If all non-deterministic behavior is asynchronous, we can optimize away many things that can never happen.

A lot of modern performance comes down to exquisite orchestration, scheduling, and timing in complex processes. A GC is like a giant, slow chaos monkey that randomly destroys the choreography that was so carefully created to produce that high-performance.


> performance tends to be a major objective

My comment is about the thinking behind making this decision, C++ or not. It wasn't "is it speculative that GC will add a cost?" or something like that.

I wonder how much of the thinking that leads one to conclude "I need so much performance here that I can't afford a managed language", for example, is real carefully thought vs. speculative.


In my experience 99% speculative and WRONG. Who said: "early optimization is the root of all evil"? :) Today more and more is possible to have a GC without terrible performance issues. Some weeks ago I read an article here in HN, about LISP used for safety critical systems. That bad fame of GC comes from the early versions of Java... but I've been using GC languages a LOT, and never had those "stop the world" moments.


The expression is "premature optimization". And, Donald Knuth.

GC overhead is always hard to measure except end-to-end, because it is distributed over everything else that happens. Cache misses, TLB shoot-downs. Mitigations are very difficult to place.

Practically, you usually just have to settle for lower performance, and most people do. Double or triple your core count and memory allocation, and bull ahead.


Not my bailiwick but I feel like early Java's problem was a combination of everything not a simple primitive is an object that goes on the heap plus a GC optimized for batch throughput vs latency. Bonus it's Java all the way down to the bitter end.

I'm with you. One should look at latency requirements and the ratio of profit vs server costs when making a decision. AKA when your product generates $250k/month, you're paying three programmers $40k/month, and your AWS bill is $500/month isn't the time to try and shave pennies.


Go's garbage collector is faster than you imagine. Have you used it?

Go is a good replacement for C and C++ for almost all purposes.

Most purposes where Go is inapplicable should be using explicit SIMD (GCC intrinsics) or CUDA C anyway.

The others purposes where Go is inapplicable are low-level real-time stuff that should be written in C for a specialized software stack (e.g., software in a car).


Even with version 1.18 the median is 3x slower than c++ in the benchmark game. And most of those programs don't even exercise the GC (other than binary trees benchmark). If you are targeting a quadcore arm embedded type thing with no gpu, go is going to take something that saturated one core, and make it saturate 3, if you are lucky enough it is easily parallelized. Even when a gpu is available, it often isn't a good fit to the problem.

I like go, I use it on occasion. If I stick to the simple stuff it is really easy to think about and if it is just a back end on a big server, why not. But that is a niche case for me. I usually prototype in matlab, then implement in c++, and yes, often cuda too, but I think saying that go is almost always a drop in for c++ is missing the vast majority of what c++ is used for. Go is not a systems language, it benchmarks slower than many vm lamguages like java and c#. It's gimmick is that it compiles really fast and is easy to reason about, so it is good for velocity. But it sacrifices a lot for that. FFI compatibility, and runtime speed foremost. Sometimes those things don't matter. But for systems programming, dsp, embedded, or AAA games they are deal breakers.


I don't know if you've read the benchmark code from The Benchmark Game, but anyone who has looked at the code takes those results with a grain of salt, or discards them entirely.

For example, the C/C++ implementations use arena allocators, and they could do the same for the Go implementations, but they don't.

The Benchmark Game is a joke. Here it is, for anyone who wants a good laugh: https://benchmarksgame-team.pages.debian.net/benchmarksgame/...


I disagree, but they do take some effort to use effectively. Isaac (or someone) has spent a lot of time segregating out idiomatic simple, and overoptimized solutions. I have looked at the programs, and within the idiomatic programs similar to the types of stuff I do (not a lot of allocation) go varies between 30% and 300% more CPU time. And I have used it as I said. Because go is compiled to native people keep suggesting it is a systems language. Fortran is compiled to native too, but I don't want to write a kernel driver in it. Go has a huge niche, back-end. I think it has plenty of competition there with java and c#, but I reach for go because it makes things quick and easy. I might learn c# if back-end was my day to day, but it isn't.

Using c++17 (don't know 20 yet) for very small embedded targets works fine, easy FFI into the BSP, small to no runtime depending on usage.

Trying to target that with go would add a lot of extra challenge.

I think something like rust or preferably a subset of it for c++ ise cases makes sense. My next project without a GPU I have penciled in rust on the plan. The other developers are excited. Still too hard to use gpu with it. Or rather too immature.

The other developers like go too, but throughput will be critical without the gpu, and every little bit will count. And for numeric code it wouldn't just be a little bit of a hit. I wouldn't even suggest julia, and that is as close to a do-it-all language as I have seen.


C++ allows you to control your memory allocations.

You can use mark and sweep, you can us reference counting, you can use arenas, you can use local stack, you can use shared memory, you can have custom allocators for any object, you can avoid memory allocations altogether, and any combination of these.

C is the same. Java can do some of this, if you twist it hard enough. Go cannot. That's ok -- often, even most of the time, you don't care, and then Go is a fine language.

But C++ is useful in a much wider set of domains than many people think, because it offers a level of control that many other languages don't.

My favourite analogy? Go (etc) is like an Apple product: smooth, and shiny, and tries to be idiot-proof, but you can only do it the Go way. C++ is more Linux: a mess of incompatible, incomplete, infuriating things that let you do absolutely anything you want, if you invest the effort.


As a Go expert, you should consider fixing it, and more people will be willing to use Go for performance-oriented projects. Like it or not, a lot of people go to the benchmarks game website to think about programming language performance.



Thanks for the work separating out the solutions. It really helps compare the sort of code I'd actually write in those languages. Also, what is the reasoning behind no numpy? I'd never do something like nbody or Mandelbrot without numpy or torch in my actual job, so it isn't idiomatic in a way. Not a complaint, I could always fork it if I cared enough, just curious about the reasoning. I know pypy didn't really want to be involved. Thought it might be like that for numpy?


pidigits and regex-redux explicitly allow use of C libraries.


I looked again at the idiomatic solutions and of the GC languages, on the benchmarks that are relevant to me, go leads the pack (except unchecked swift which doesn't really count, and julia which I think may use actual magic). I feel somewhat vindicated in not bothering to learn c# yet. I am trying to become more fluent at julia though. It already has a very extensive set of libraries from it's community. I may eventually use julia for the things I reach for go for now, and probably some of the things I use matlab, c++ and cuda for.


Go will give me VSS of hundred of Gigabits on a MIPS board that has only 64MB memory, it's a known 'feature' by design. Its binary size is at least 10x larger than C/C++.

Go also has the stop-the-world GC problem, GC is great but it does have a price tag.

I like Go a _lot_ and use it in some projects, but I certainly will not claim it can replace C++ 'generally', not at all.


Sure, you can always find extremely constrained, embedded, or real-time safety-critical applications where only a carefully chosen subset of C is applicable. You shouldn't be using the sprawling C++20 there, either.

But for pretty much everything else (see the caveats in my comment above) you are better off, a lot better off, using Go.


C++17/20 is superior to Go, and widely used, for anything that looks like high-performance data infrastructure, which is a broad class of software. There are classes of common architectural optimizations that aren't feasible with a garbage collector because of how it interacts with scheduling and CPU caches. Even 1 millisecond for a GC pause -- often considered "low latency" -- is much too slow when standard operation rates are tens of millions per second.

Go is very good for many things but it does not offer competitive performance for these types of applications because design elements that have a large impact on throughput are poorly supported.


go has other problems that preclude it from being a good option including binary size, memory usage, and portability. There are certainly a lot of places it is a good choice for, but I can't really imagine using it anywhere I use c++ today.


Portability?

Are you saying Go has portability problems?

Can you elaborate on that very surprising claim?


when you talk about macos/windows/linux without CGO yes Go is very portable and easy to use, when you need CGO, or you need work on other architecture or OSes, Go is pretty much a no-go. and c/c++ are still the only ones close to the claim "runs on everything".


What are people using C and C++ for that you can comfortably use Go instead?


For example, I work at a company (a company you hear about daily here on Hacker News) that has a compiler generating linear algebra kernels.

The compiler is a huge assemblage of C++ templates, stitched together with a little Python.

The generated code is in an assembly language for a highly parallel machine, and needs to be heavily optimized down to the cycle.

But the compiler itself does NOT need to be so minutely tuned. It needs to be maintainable and it needs to allow easy development of complex code generators.

Go would be a great choice for the compiler.

Instead, we struggle with C++ templates, CMake'ing our code slowly and with absurd complexity, modifying the compiler slowly and with great difficulty...


Honestly, it sounds like C++ is just a poor choice for this project, since you are not so interested in performance and you are a lot more interested in developer speed. It is not an example as to why Go is a good replacement for C++ in almost every project. This is a case where you could replace C++ with any business-logic-oriented programming language and you would be happier. It just so happens that your language of choice is Go (and presumably the tech lead's language of choice is C++ for some unknowable reason).

Also, you should not discount compiler speed completely. You, yourself, were upset at the speed of a compile process. All of your users are going to be waiting to get things done while the compiler is running, so you might want to do some math as to how much a 10% slowdown would cost in developer salaries. The makers of gcc, LLVM, and clang spend a lot of effort on improving the speed of their compiler to make sure that you have minimal time waste.


Honestly Go would be a bad choice as well, wouldn't it? It's not like it has much in the way of abstractions.


Sounds like you have a code-quality problem more than a language problem. In that situation, switching languages probably would not help you much.

Get a handle on your code quality, and put the language features to work to solve problems for you. Then Go will increasingly look pitifully inadequate.


I've read all of your comments and I commend you trying to illuminate these people. Some of them seem to be living in the 1990s, spewing the usual hocuspocus about programming languages, the kind of thing you hear once and it becomes a myth. It's like people don't want to reevaluate their choices, and it's insane we keep using this god-forsaken language when we have much better tooling, as you said.


Sounds like Fortran to me.


Fortran is easy to hire for?


For numerical and scientific (high performance) jobs, possibly not that much harder than C++. For other jobs, I doubt it and would stick to C++.

The issue is that, for example, you might be getting people with poor software engineering training per se. You risk hiring some very smart phd that writes code that works and runs really fast, but isn't that readable, extendable, maintainable, testable, etc.


Your second paragraph describes what I see with Python ML guys.


It's generally true for ML and data science, regardless of the language.


I see it in a lot of matlab code as well.


This might be a stupid question. But is there performance degradation between versions of c++?

I saw a couple of stack overflow questions last week of people complaining about c++ 17 being slower 14, and 14 being slower than 11.

But I find it hard to believe. I just started learning c++ recently and can’t find a conclusive answer.


There is at least one language-enforced performance improvement: C++17 enforces some amount of RVO. Thus some stuff that would have been copied in previous standard is now not anymore. More standard types supporting move semantics & such also mean that generic code which correctly calls std::move / std::forward may do fewer copies the more recent standard you use.


Those are just versions of the standard. What people would be comparing is the performance of particular implementations. It's entirely possible that a less mature implementation would have problems.


Yeah that makes sense, thanks.


The "idiomatic" version is slowly getting slower because the idioms are getting higher-level and more expressive. If you don't use the idioms, you can have the same speed. Smart pointers (including unique_ptr<Foo>) have non-zero overhead compared to Foo*. The object oriented parts can introduce significant slowdowns. Template code can have huge code footprints if you are not careful, which slows things down.

If you are looking for cycle-level performance, you likely need to use a more C-like subset of the language, but you can still use many conveniences like RAII and the smart pointers with 0 overhead (when construct/destruct are not in the critical path).


The idiomatic version has gotten faster since C++03 overall thanks to move semantics.

std::unique_ptr<Foo> doesn't have any overhead over Foo* in code where semantics is the same (i.e. you want to delete when going out of scope) on any sensible ABI - it has the same storage size, and all operations are trivially inlineable to the same exact thing you'd do with a raw pointer. About the only time I can think of where it can be slower is when the ABI insists that a struct cannot be passed or returned in a register.


> std::unique_ptr<Foo> doesn't have any overhead over Foo* in code where semantics is the same

Not true. And you just refuted yourself by being up the ABI question. You should watch Titus Winters' presentation on this topic where he compiles code using unique_ptr and raw pointers, compares the assembler produced, and explains why unique_ptr has non-zero overhead. The overhead is indeed coming from the ABI. Google has been wanting the next C++ to break ABI but they didn't get enough votes in the standard committee.

https://youtu.be/rHIkrotSwcc


Not breaking binary compatibility is a pure good.

The standard could pull unique_ptr in as a built-in feature with a different name, and retain backward compatibility at cost of an unfortunate redundancy. Code using unique_ptr would gradually transition to the new thing.

Of course each would have a move constructor from the other, to ease the transition.


The above is commonly encountered, but not good advice.

Most important, defend your hot path against incursions. E.g., don't pass smart pointer arguments around on hot paths. That is what matters.

New language features are not slower than old features. But there are slow constructs to use carefully. Which they are will always surprise you, usually pleasantly. For example, almost everything around lambdas is fast, fast, fast, except =capture.

Measure often. Look for surprises.


> Don't pass smart pointer arguments around on hot paths.

oh this so much.. I remember optimizing a particle system which would copy a shared_ptr for each particle on every update operation... IIRC just switching to references ended up being 10+ times faster


If you can design your system so you rarely if ever use pointers, that would be the single best thing you can do for your C++ codebase

I like to enlighten my interns every summer about the reality of pointers, and how much of a noob trap they are, and that yes, their professor lied to them in some ways


sadly this was not "my system" but was something for a 2-3 day project on an unknown codebase so a deeper refactoring was out-of-the-question


I literally said not to construct or destruct smart pointers on the hot path.

Also, my experience with lambdas has been otherwise. There are a lot of cases where lambdas need a heap allocation when you otherwise wouldn't. However, being able to use FP ideas can make up for that.


My objection is to the expression "C-like". That does not correctly distinguish what is fast from what isn't, and instead promotes a harmful myth.

A lambda needs a heap allocation only if any of its captures do.


If you're writing the same code, almost certainly not.

The only way there would be would be if the compiler either can't optimize something as well because the lang changes requirements (this seems unlikely), or if they have bugs or inefficiencies because they haven't spent much time on the new standard.

If you're using new stuff from the new standard, they could be slower or faster or undecidable, really depends.


I started reading the link "bans everything to start with anyway" in the slides:

https://chromium.googlesource.com/chromium/src/+/HEAD/styleg...

And I noticed the ban of shared_ptr and <chrono>. I know the use of shard_ptr should be done cautiously, but just banning it from use? How do the Chromium devs solve a problem that requires shared ownership? I guess you can come a long way with singletons and consumer/producer queues. But are raw-pointers just used instead of shared_ptr when shared ownership is needed?

Also banning use of <chrono> ?

Can someone try to explain the possible reasons?


As for <chrono>, the link you provided also has a (short) justification behind the decision: https://chromium.googlesource.com/chromium/src/+/HEAD/styleg...

Regarding smart pointers, not sure if you also found this: https://chromium.googlesource.com/chromium/src/+/HEAD/base/m...


Chromium has an existing ecosystem with a variety of smart pointers already.


yeah I found this when stumping upon https://source.chromium.org/chromium I guess that makes sense as well.


Did you see that <chrono>? C++ committee smoked something unspecified when they ratified <chrono>, but banning it is perfectly reasonable.


The Boeing 377 Stratocruiser was the epitome of piston technology: 28-cylinder radial engine that needed more maintenance than flying time. It vibrated so hard that it separated from the wing.

https://en.wikipedia.org/wiki/Boeing_377_Stratocruiser


Maybe you're being hyperbolic, but the catastrophic engine separations in the Stratocruiser were due to propeller failures.

http://enginehistory.org/Propellers/B377Props.pdf


Leads to https://en.wikipedia.org/wiki/Aero_Spacelines_Pregnant_Guppy

As some comedian used to quip, Pregnant Guppy would be a good band name.


I cannot reply to your comment from "proof?" for gcc is better than clang at generating binaries.

But here I found some benchmarks (did not inspect though): https://www.phoronix.com/review/11900k-gcc11-clang12/2


Honest question, do not want to insult anybody: I've heard lots of times, that the reason why C++ sometimes is weird and complex, is because utter care is taking in maintaining backward compatibility. Sorry if I'm wrong, but I remember seeing a video about variable initialization, which showed many ways of initializing variables, and at the end, the excuse was "all because we have to maintain compatibility to C". No my question, again, please do not think I'm insulting anybody: This presentation seems to show the compatibility between most recent releases is not very good. What am I missing here?


> This presentation seems to show the compatibility between most recent releases is not very good.

Half of the "features" shown in the presentation are exceedingly niche. It's just that a 20 million LOC (or however many Chrome has right now) code base is almost guaranteed to exercise every niche feature somewhere.

Having helped with C++ standard transitions of a code base half an order of magnitude smaller, the amount of issues requiring code changes was minuscule so far. You tend to have much more boring problems, like:

- "We can't use newer C++ because C++/CLI doesn't support it"

- "We can't use newer C++ in these headers because they end up included in CUDA code, and that compiler only supports C++14"

- "We have to wait for a new version of <tool> before we can lint/sanitize/profile/format our code"

- "This third-party code has the compiler up in arms now because it does questionable things"


Yep, C++/CLI is stuck on C++17, and apparently will stay like that.


The presentation mostly lists warnings and notices about deprecation. If you turn them all off, most likely the compilation would pass.

So, backwards compatibility is there (if we ignore newly introduced keywords).


I’m usually somewhat critical of Rust… but not when I encounter C++. What an over-engineered shitshow. Rust looks like a simple set of useful tools compared to C++’s convoluted warehouse of horrors.


What you don't use, you don't understand.


Is there a link to a recording anywhere? I find that slides usually miss a lot of the interesting information. Talks are more than their slides.


Not publicly, unfortunately. This talk was given internally a couple of times.


In a few months, I might be switching to a team with a C++ project. A lot of the team is new to C++, and there's nothing about the project that needs C++, Java would have been fine.

This doesn't look very fun.


Compared to Java, modern C++ isn't so bad. Dependency management and build systems are the main problems. Both of those should be solved in an existing project.


C++ has a lot of sharp edges. Skimming through the slides it seems as though Chromium hit upon a lot of the big problems because:

1. Google always seem to enjoy bucking the trend in terms of recommended ways to do things and end up using niche features that are likely to be deprecated 2. Chromium is a stupidly massive codebase so they have more things to fix just purely through scale

If you stick to the sane subset of the language that any competent modern C++ development shop would then you wouldn't encounter any of these problems except small/easy stuff like the new reserved keywords.

That being said, if you don't have a team that's already experienced in C++ then picking C++ is a bizarre choice.


C++ was the right choice for broader organizational reasons, but the wrong choice for what the project needs.


If there's ever a big production outage, you can make the case for Java.


Because Java is known to magically never cause production outages?


Because the OP described inheriting a C++ app his team doesn't know how to manage.

It's a language where segfaults, memory leaks, and other problematic issues are easy to manifest.

I assume they have deep Java knowledge, since they suggested it themselves.


This would likely result in worse problems. Things You Should Never Do, Part I

https://www.joelonsoftware.com/2000/04/06/things-you-should-...


This has never been my experience, and Joel isn't an all-seeing oracle.

In just one notable example, a company I was at had a team develop an important platform in Node.js when the rest of the company was hired for and familiar with Java/Ruby. This app ran our 3rd party API gateway and was a central part of how our company was attempting to grow.

The Node.js team left wholesale to go found CockroachDB, which left nobody at the company who had the expertise to take over. You'd think that someone at a fairly large company would have Node.js experience, but it wasn't the case that we could staff the team back up easily.

There were several major production outages and the app lagged behind in development for the entirety of its life. We also had to port our protoc changes and traffic stack just to serve this one app. This despite being a central part of our upmarket strategy.

Ultimately it was completely rewritten. And nobody regretted that decision. We carefully weighed the pros and cons.

There's something to be said for a company that standardizes on one or two languages. Letting engineers have free reign leaves you with Haskell and Erlang littered in important places, with a very tiny bus factor.


No one said to let everyone have a free for all with a million different languages. Rewriting things creates its own set of bugs. Every time.


Of course it does.

But leaving things untouched is sometimes impossible or an even bigger drain.


I wonder how long it would take an experienced developer to rewrite all of Chrome in Rust. It would probably take a special person though who is an expert in C++, Rust, and the codebase.


There are no "special" persons or superheroes. It is all about time + money.

If a tech giant wants this to happen - e.g. because memory un-safety is mostly the root of all evil - they can use people from the current team and throw a few millions on hiring new talent.


Well, Chrome has many, many hundreds of developers who have worked for many years on the codebase. If we froze the world and turned the whole team over to writing Rust, and we assumed people were perfectly productive in Rust from day one, we could maybe do it in five years.


30 years?


Why do you think that? How complicated could it possibly be? Also as a person becomes more of an expert in a specific thing, they make progress much faster (if they remain motivated).

Also it’s just rewriting the logic. One could theoretically write unit tests for every function to make sure the inputs/outputs match.


Why only an unhip, old geezer would write outdated drivel like:

  const int MAX_SIZE = 1024;
  const int HEADER_SIZE = 128;
  set_content_size(HEADER_SIZE - MAX_SIZE);


Probably less geezery to write

  set_content_size(MAX_SIZE-HEADER_SIZE);
Sayin'.


Never mind the operand order; what do we gain by using constexpr instead of const?

In C++, const int x = 3 is already a constant. As of C++98 already, you can use x as a case label in a switch statement.

It cannot be any more constant.

Not cool any more?


You just want to ensure they don't take up space in your struct body.

Otherwise, const is fine.


The word for this is "future shock".


[flagged]


Sometimes you need to avoid GC or have direct control of your threading model and you need a language that is more expressive than C. Your only two options here with broad adoption are C++ and Rust, both of which are very complicated languages.

Other languages like Zig, Ocaml, and Erlang have legitimate claims to similar performance to C++/Rust with expressiveness, but they do not have the same adoption.


See my comment elsewhere in this thread, where I argue that Go is a good replacement for C/C++ for almost all purposes.


I have spent several years writing C++ in circumstances where Go is a terrible C++ replacement. In these cases, you either need manual control of memory or you have extremely tight requirements on either speed or memory usage. In both cases, Go falls short, and C++ actually works relatively well (so does Rust - everything I am saying about C++ here applies to Rust as well). Despite C++ having a large spec and being complicated, it pushes all of that complexity to compile time. At runtime, you pay nothing for the complexity of the language.

Please elaborate to me as to why Go is an appropriate C++ replacement for:

* Trading systems that use direct NIC access and need sub-microsecond execution times.

* Performance-oriented databases (like ScyllaDB or Aerospike). Like trading systems, these are characterized by having custom shared-nothing (often stackless) asynchronous runtimes that need direct control of syscalls, and optimized IPC and synchronization primitives.

* Libraries like memcpy, math libraries, compression libraries, and encryption libraries, which need both SIMD intrinsics and minimal overhead compared to assembly implementations (Go fails on the second part of this criterion - even the Go calling convention has significant overhead). And no, you do not need to write these in assembly: C and C++ versions are far more readable and equally fast.

* Memory allocators, which cannot circularly depend on another memory allocator.

* Embedded systems with constrained memory footprints.

* Hardware drivers.

* Code for non-CPU machines, like GPUs or DSPs.

Almost all the real use cases for Rust, C, or C++ are not suitable for Go. Go is only a suitable replacement for systems programming languages in places where Java is also a suitable replacement: when you have a powerful computer and fairly loose constraints, and you are primarily doing business logic.


Go was specifically designed as a language for un-demanding applications written by coders with strictly limited skills.

That is most applications, in practice. Unpleasantly often, though, applications move from un-demanding to demanding, and then having been coded in Go is unfortunate. A nimble organization will code a C++ program for that use case. Lesser shops will lumber along patching the Go code.


These slides are profoundly uninteresting.


It's easier to say goodbye C++!


C is 10 years younger than COBOL, C++ is 20 years younger than COBOL, COBOL is still around.


A full rewrite seems extremely expensive and long. I’m afraid C++ isn’t going anywhere in our lifetime.


C++ can stay, but people can make a decision not to use it for anything and not to take any jobs involving C++ (or other systems/bare-metal-ish languages).


I've written a fair amount of C++ in my day, and this seems totally unacceptable to me.

Also my opinion seems unpopular on this topic, so I suspect some group of people that really want to get behind C++ don't like that kind of talk.

Whatever though. Rust and other newer languages are kind of giving the spaces C++ works well in a run for its money and in other ways just completely blowing it out of the water in my opinion.

It might be time to look at D again too.


It's more easy to just don't adopt the new standard.


So, it seems most problems are C++ making things extra bureaucratic and annoying. Cool

The "pre/post increment of volatiles is deprecated" sounds like a huge pain. I can't imagine a worse waste of developer time than fixing such a minor thing (and to be fair C allowing both a++ / ++a should never have existed)


That's been undeprecated in C++23. It's mildly embarrassing, but it shows the process works.


Are all of C++20’s volatile deprecations un-deprecated in C++23? This doc from 2020 lists all:

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p21...

But this proposal from 2021 suggests only undeprecating bitwise compound operations:

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p23...


Sorry, you're right, my bad. People are actually writing compound arithmetic assignment ops on volatiles, huh.


You got my hopes up about un-deprecation :) but I was glad to at least learn about the proposals


don't you mean ++C23


well to begin with if you're using volatile, you're probbaly doing something wrong already.

The valid use cases for it are ridiculously few.


But if you're using volatile correctly, then there's nothing wrong with ++*v.

I was wondering about that slide that says the meaning is unclear, I don't see what's unclear about it. What particular assembly instructions it translates into is irrelevant. It's not like ++ is guaranteed atomic or anything.


it's not guaranteed atomic, and if you ARE using volatiles, "maybe" doesn't really cut it, so now you need to do `++*v` the proper way (ex. `lock cmpxchg` / `__atomic_compare_exchange`), or explicitly write it out the long way (`*v = *v + 1`) and risk the small chance of the value changing under you between read/write


using volatile correctly usually means you're manipulating write-combining memory.

Write-combining memory has weak memory ordering where all writes are delayed so anything doing both reads and writes is most likely going to bite you in the neck.


If you're accessing any kind of I/O register or special memory, obviously you need to know the rules of engagement for the particular kind. If you don't, then you're just going to be making the same mistake in a less obvious guise. Like writing *v=*v+1 instead of *v++.


Damn. May as well have used a language that isn’t released yet like Zig. That’s a ton of problems.


Um no. When I upgraded it was a few hours of work at most, for Google since its code base is extremely large maybe a few days to weeks.


Probably no big issue for a small to medium codebase. But I doubt the "just a few days" estimate for something as big as a web-browser. If I read correctly they even found use-after-move bugs during the process.


The use after move existed prior, just wasn't moving even though they had written move.

It is also written in a way that a linter should be able to detect if you disallow use after move.

https://source.chromium.org/chromium/chromium/src/+/main:com...


It's surprisingly hard to detect these sorts of cases reliably; we have a request out to the clang-tidy folks to add some improved detection of use-after-move there.


Took a couple of months actually, but it's mostly complete now and we're starting to throw the switches.


Is this a joke?




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: