Modern C++ for C Programmers (2018)
berthub.eu
berthub.eu
"Stroustrup campaigned for years and years and years, way beyond any sort of technical contributions he made to the language, to get it adopted and used. And he sort of ran all the standards committees with a whip and a chair. And he said "no" to no one. He put every feature in that language that ever existed. It wasn't cleanly designed—it was just the union of everything that came along. And I think it suffered drastically from that."
-- Ken Thompson, 2009
Source: https://en.wikiquote.org/wiki/Ken_Thompson#%22Coders_At_Work...Moreover, the criticism by Thompson and Torvalds refers to the use of C++ as a systems programming language. The language proved itself very efficient for application programming, including the browsers everybody is using.
He is completely wrong, because his comments reveals a complete lack of understanding how ISO works, or worse, he does understand it and is doing a malicious comment to transmit his biased opinion.
Bjarne has the vote of one person, it doesn't matter how much he yells, screams, slashes the whip down the members back.
It is one single vote, and above all, unless he has some dark magic powers, he cannot multiply himself to attend all voting sessions.
Garbage collection isn't in the language though some people wanted it. So it isn't a union of everything.
However, since no one has made any use of it, and existing C++ GC's follow other schemes (Unreal, C++/ClI,...), there is a ISO C++23 proposal to remove it from the standard.
C++ is a design-by-committee mish-mash language chokeful of everything that was ever thrown at it without any regard to how well it all fits together. In practice it is indeed used in a form of restricted feature subsets, because there's basically no other way to use it. It gets the job done, but it's an ugly bloated monstrosity. Not bad, just ugly.
Unfortunately Mozart/Oz has not taken off in the Industry but C++ has fulfilled the niche though not as cleanly as envisioned in that book. Paradoxically, the breadth of the language features has become its strength. You can do Imperative/Object-Oriented/Generic/Functional/Concurrent programming all within the same language and using the same toolchains. Moreover, all these higher-level concepts do not prevent you from getting close to the metal if needed. AFAIK, no other "industrial" language even comes close. Alexander Stepanov, the inventor of STL is on record stating that he could realize his vision of a comprehensive, generic, flexible, efficient Algorithms library only in C++ (his attempts using Ada and Scheme both failed). That should tell you something about the power of the language.
Has Ken Thompson ever retracted those comments? I can't find anything to suggest he had a different assessment. The phrasing used in that interview / book sounds like this is a subject he has deeply considered and well-reasoned - certainly not an off-the-cuff 'C++ is awful'.
Ken and Bjarne may have some history, as you say - I do not know - but suggesting that implicitly reduces the legitimacy of his assessment may presuppose an inability to maintain impartiality on technical merits. I note that in the comments I quoted, and in other comments he's made about C++, his arguments are foremost about the quality and features of the language, with some hints about why it got to be the way.
Whether it's an appeal to authority, or a deference to someone with unarguable expertise, is perhaps a subtle distinction.
Additionally, anyone who has been programming for more than a decade, is most likely a polyglot developer has is well aware of similar problems in other languages.
Which C#, TypeScript, Java, Haskell, OCaml, F#, PHP, Python,... subset do you happen to program on?
No; People keep repeating this as if it were a truism, but it is not borne out by any empirical evidence. Sure there is some learning ramp to understand a new style of programming but it is far more easier than trying to learn and express oneself fluently in a whole another language. This has been true both in my own experience and most of the people/teams i have worked with.
See also the comment from "FpUser" in this thread echoing the same sentiments.
People are people when it comes to biases, ego and personal relations whatever other characteristics they might be eminent in; so no need to deify anybody. History is full of great people who were mean/petty/egoistic towards perceived "competitors", eg; Newton and Leibniz, Edison and Tesla, James Watson and Racism etc. etc.
>Recent sibling comment has added to the overwhelming tone of HN comments on TFA - that C++ is a poor language
Your original posting of the quote added nothing to the OP article which is a quite nice introduction to Modern C++ features but hijacked the entire thread and set the negative tone for everything else with the result that instead of being a constructive thread on how best to introduce Modern C++ we are engaged in pointless arguments on the features of a language which has already been highly successful in the "Industry".
Programming general use libraries - sure. It is often enough hard to read black magic but I'll let people that are actually doing it worry about the problem. It does not have to propagate to application level programming.
I feel the technical lead should be catching these problems during code reviews, and establishing best practices, etc. Also this problem exists in other languages, perhaps elevated slightly in C++, but until rust came along it was 1 of 2 choices you had for certain problems (C being the other obvs.). Speaking of rust I thing the jury is still out on whether it can catch up with C++ in terms of popularity.
This is why I like the slightly more walled-in gardens of the JVM and CLR. For instance, when someone is talking about JSON serialization in C#, you could bet your life that they are referring to one of ~2 specific libraries and sets of methods.
It is a lot easier to build a community of shared understanding when everyone is playing the same game every day.
I wouldn't bet on it, https://devblogs.microsoft.com/dotnet/try-the-new-system-tex...
Also try to talk about Java 17 to clueless group of devs still delivering stuff into production with Java 8 in 2021.
Id say it’s part cluelessness and it’s part that they just don’t care. They’re comfortable with 8 and don’t want to learn anything new (even if 8 is a slower runtime).
I fear that the community will never move on past 8.
This would be the 2nd of 2 that I was referring to. The first would be Newtonsoft.Json.
You have to modify code using System.Text.Json for the code generator based way, and it doesn't support all the features, enjoy the comments section.
> and it doesn't support all the features
I think there was a misunderstanding.
We do not use System.Text.Json because it doesnt support everything Newtonsoft.Json does. At no point did I intend to make the claim that these were somehow compatible implementations.
Our shop is stuck on Newtonsoft until Microsoft realizes that polymorphic deserialization is actually a really useful thing and not just a pedantic security concern.
C++ doesn't force you to use everything it's got. Can't I just write basic C style procedural code in C++ and use OOP constructs if necessary? It is completely optional I think. Don't like templates? Don't use them. Set a policy in the team/company to avoid using advanced C++ features.
I’m reasonably familiar with C++, but not so much with arcane template meta programs. I never write templates, but it still trips me up from time to time when I have to understand one.
The complexity needs to be somewhere in the end. A charitable view would be that Cpp's approuch is more ... inclusive?
It reminds me, every time I try to jump too implementation of some standard library method/class, it just seems like a completely different language to me. And C++ is the language I've most experience with.
Meanwhile, I jump to some Rust or Golang* standard library implementation, it makes perfect sense very quickly. Even though I just use them for hobby projects.
* Go is probably a bad example because it doesn't have any generics. But still love how much more approachable their stdlib code is.
The global namespace still exists so unqualified names that do not have a reserved id( eg _Foo ) cannot be used and need to be qualified (e.g ::std::(min)( a, b );). All this to protect against us.
Some codebases are easier to read, I would rank them from libc++, MS STL, libstdc++ is readability. libstdc++ also seems to have code all over the place and use far more inheritance for composition of things.
That's not to say it doesn't exist in the implementation of the Standard Library. I just never have any need to see any of it.
Admittedly such a situation is not entirely due to C++, but it didn't help. But again, I do quite like C++ especially nowadays.
May we should create a new subset of C++. It will be called C+.
You can, but the bloat Linus is complaining about comes from the ability to do that. C is a real mess and is a large part of what is holding C++ back from being simpler (for both better and worse). C seems simple, but you lose a lot of abstraction ability when you use it.
C++ seems a lot more complex, but that complexity allows you to write abstractions that do exactly what you need. Modern C++ is the discipline that lets you write good abstractions, with exactly the control needed.
Don't take the above as some sort of statement that C++ would be simple with C - that is completely untrue. Only that C++ is moving to giving people the power to write the simple and powerful abstractions they need - but the nature of those abstractions are very complex and that cannot be avoided if you need to write them.
> use OOP constructs if necessary
THIS IS FALSE TO THE CORE! C++ is NOT about OOP. C++ is about writing the style that makes the most sense. OOP is good for a subset of problems, but a lot of problems can be solved better by not using OOP, and C++ supports that. If you think of C++ as C with classes you are making a large mistake.
While C# is now on version 10, and Java on 17, if we account for the releases in 2021, the large majority of the world is still delivering .NET Framework (C# 6)[0] and Java 8 code into production.
When I use modern C# and Java, always need to give a mini-lecture on current state of the world.
[0] There are ways to use newer C# version in .NET Framework, but with some caveats.
If you write C code in C++, you will only have C code, and nothing better than C code. If you write Java code in C++ (which you can, and some do) you will only have Java code, and nothing better.
Leave the C parts behind. Leave the O-O parts behind. Lean into Modern C++, and reap the benefits of all the work put in to make the language better than C, better than Java (i.e. pre-C++98 plus GC), better than C++98, better than C++11, better than C++14, better than C++17.
Pointer arithmetic and inheritance are still occasionally useful; you can still wrestle in the mud. But if they show up in more than a few places in a program, you are Doing It Wrong.
All I’m saying is if a new feature is added to C++, it doesn’t affect me and I can choose to pick which features are useful. I can still keep it absolutely minimal. C with some useful libs, and occasional OOP.
Why this is the worst possible answer? Obviously it works for me, it makes sense to me and it is perfectly logical in my view.
Others have said the language bloat creates burden when you read other people’s code. That makes sense to me.
Care to elaborate more? If you want to harshly criticize, it’s fine but you need to say a bit more so I can understand where you’re coming from. I just see a bunch of rhetorical statements.
Yes, you can limit yourself to the C and Java subset, just as you can drive everywhere in first gear, can eat all your food raw, can crap in a hole. Just because you can does not make it a good idea. Objectively, the more your code looks like C, the worse it is, because you are inviting into it all of the flaws endemic to C code.
"Bloat" doesn't require that the features are literally useless, merely that their usefulness doesn't justify their added complexity.
And for many people, a huge portion of C++ falls into this category.
Just look at the 18 different methods of initialization for an example of this. Or look at ranges, which seem to simultaneously be far more verbose than the alternatives in every other language, while having measurable negative impacts on compile times (that have spread throughout the stdlib, so even if you don't use them you have to pay for them). Ranges are certainly "useful" but the cost/benefit ratio is questionable.
There are new initialization modes that are simpler. The old ones still have to work. But you don't have to use the old ones anymore.
Serious questions:
What subset of C++ is "Modern C++"?
Are there flags for C++ compilers to only allow modern C++ and generate warnings when non-modern C++ is used? (For example, if an older-style C++ idiom is used, the compiler might say: "use of Old_Idiom is deprecated. Consider using New_Idiom instead".)
I don't use much C++ and I have found the language daunting to learn because it is large, complex, and full of gotchas. A better subset that is enforced would help newcomers not get lost.
But it is for the new code that the most value is available.
Generally, if there are two ways to do something, and one appeared in a recent Standard, using that is usually the better choice. Cppreference.com helpfully tags each bit with when it appeared.
====================================================
1992. Effective C++: 50 Specific Ways to Improve Your Programs and Designs.
1995. More Effective C++: 35 New Ways to Improve Your Programs and Designs.
1998. Effective C++, Second Edition: 50 Specific Ways to Improve Your Programs and Designs.
2001. Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library.
2005. Effective C++, Third Edition: 55 Specific Ways to Improve Your Programs and Designs.
2010. Overview of The New C++ (C++11)
2010. Effective C++ in an Embedded Environment
2014. Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14 ====================================================
and you would not be able to understand modern C++ anymore...
"The Errata Evaluation Problem"
http://scottmeyers.blogspot.com/2018/09/the-errata-evaluatio...
"As you may know, I retired from active involvement in C++ at the end of 2015, and in the ensuing two and a half years, I’ve forgotten enough details of the language that I am no longer able to properly evaluate bug reports regarding the technical aspects of my books. C++ is a large, intricate language with features that interact in complex and subtle ways, and I no longer trust myself to keep all the relevant facts in mind. As a result, all I can do is thank you for your bug report, because I no longer plan to update my books to incorporate technical corrections. Lacking the ability to fairly evaluate whether a bug report is valid, I think this is the only responsible course of action."
It’s a little bit of a peeve of mine that people just decided on new languages instead. But it is what it is, and I am probably not knowledgeable enough to really judge.
The trouble is, if every new version is _technically_ its own new language, and if you have to break compatibility a little, why not just break a lot and fix everything?
It's not like C++ was ever known for ABI stability anyway.
There is a lot of old code that will never be rewritten but is still valuable, and I believe we will waste a lot of time just language-shifting entire ecosystems as new languages come and go.
Look at python 2 to 3. There are still packages out there on python 2 that haven’t been migrated. And thats mostly the same language!
(Note that my view is colored somewhat because I am a scientific programmer)
Yes. Rust is complicated, but the compiler is unusually good at telling you what you did wrong. Before it results in a crash.
The underlying problem with C/C++ is that it still doesn't have a decent array story. " char * " is so 1970s. That can't be fixed without breaking so much. All you can do is try to paper over the problem with templates. But the mold always seeps through the wallpaper. Look at the example in the original posting that reads:
FILE* fp = fopen(fname.c_str(), "r");
There it is, access to a raw pointer. Why is that even allowed any more? You ought to have to wrap "Unsafe" around it, or something.*C++ got off on a weird I/O direction. That
file << item << item;
syntax never really caught on. Someone liked Currying too much.There have been a few different projects aiming to introduce 'proper arrays' into C, but they never get traction, even when carefully designed for good backward compatibility. C is seemingly just too resistant to change. Here's Walter Bright's suggestion. [0]
Aside: why is the word story so popular on Hacker News these days? We can just say C doesn't have decent arrays. We aren't discussing its history, we're discussing a specific technical facet of the language.
[0] https://www.digitalmars.com/articles/C-biggest-mistake.html
I'm not sure this is true. It has been a year since the last really significant change to the language, whereas C++ is adding Ranges, Concepts, Modules, and so on and so on. The pace of change has slowed way down in Rust and sped way up in C++, it almost feels like C++ is moving "faster" now or they are at least close to matching pace.
But a lot of the Rust efforts at the moment are really just closing holes in the language - things that you would expect to be able to do, but haven't been able to do due to compiler limitations. Generic Associated Types and Const Generics both kind of fall into that category. The rate of true "new feature" development is way down.
I never understood people's fascination for Scott Meyers books (and i own a lot of C++ books). I felt that it sort of clued you in to the microdetails/dark corners but failed to teach actual big picture programming using those same features.
Its kind of like, saying Donald Knuth was never a Computer Programmer, but some old faculty professor, with too much time on his hands, who capitalizes on puzzles and the natural complexity of computational theory. :-)
It also does not address the core point of the post.
At the moment for C++ the scene goes like this:
- For every traditionally challenging feature/issue of C++ advocates will say it has already been fixed on the latest standard. So you always feel to be a proper C++ developer you have to be a "Modern C++ Developer"
- You are supposed to know and use the modern features of the language but also use it according to what I call "the C++ on top of C++" called the "C++ Core Guidelines".
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
I printed them last week and its a 498 pages PDF. So of course you will have wait and rely on compilers/linters keeping up with it.
https://docs.microsoft.com/en-us/cpp/code-quality/using-the-...
They are not done yet, and we will soon move to the next phase in complexity in the daily life of a C++ developer. Having to troubleshoot the correct implementation of these automated checks by the different automated tools.
Like many, I have to sometimes use C++ professionally. Every time I do, I feel like I am a candidate to the Darwin Awards.
https://www.artima.com/articles/the-most-important-c-booksem...
I’ll begin with what many of you will find an unredeemably damning confession: I have not written production software in over 20 years, and I have never written production software in C++. Nope, not ever. Furthermore, I’ve never even tried to write production software in C++, so not only am I not a real C++ developer, I’m not even a wannabe. Counterbalancing this slightly is the fact that I did write research software in C++ during my graduate school years (1985-1993), but even that was small (a few thousand lines) single-developer to-be-thrown-away-quickly stuff. And since striking out as a consultant over a dozen years ago, my C++ programming has been limited to toy “let’s see how this works” (or, sometimes, “let’s see how many compilers this breaks”) programs, typically programs that fit in a single file. (make? Who needs stinkin’ make?) My living is based on C++, but it’s not by virtue of the programs I write in it.
It’s not by virtue of any intimate association with the language’s standardization, either, because I’ve never been a member of the C++ standardization committee, I’ve never been on the committee’s mailing lists, and I’ve never attended any standardization meetings. My knowledge of the inner workings of the committee—including the things that have had a significant impact on it—is based on what I’ve read and heard from others. This means that I may be ignorant of important forces that shaped C++ as we know it, because those forces may have been felt only within the committee.
Wow! This answers many questions that i have had over the years.
I’ll begin with what many of you will find an unredeemably damning confession: I have not written production software in over 20 years, and I have never written production software in C++. Nope, not ever. Furthermore, I’ve never even tried to write production software in C++, so not only am I not a real C++ developer, I’m not even a wannabe. Counterbalancing this slightly is the fact that I did write research software in C++ during my graduate school years (1985-1993), but even that was small (a few thousand lines) single-developer to-be-thrown-away-quickly stuff. And since striking out as a consultant over a dozen years ago, my C++ programming has been limited to toy “let’s see how this works” (or, sometimes, “let’s see how many compilers this breaks”) programs, typically programs that fit in a single file. (make? Who needs stinkin’ make?) My living is based on C++, but it’s not by virtue of the programs I write in it.
Donald Knuth wrote TeX and related tools which are huge/complicated software over and above his Algorithms/Computer Science Theory contributions. He stands alone in his greatness.
Scott Meyers is not even in the same league. He is a "consultant" (with all the good and the bad which goes with this profession) and in the same group containing the eXtreme Programming/Agile/Scrum crowd.
Hell I forget what half my codebase does in a matter of weeks and have to go hunting through documentation.
I'd say the only really hard part is understanding move semantics, but even then that's not really necessary and the verbiage on the internet is way too academic in that regard when it's really simpler in practice than it is when explained.
Just like looking at C# 10, Java 17, Python 3.9,... and making sense where the features or standard library calls came from.
It is when you scale the team, or have a codebase that is being kept running since C++ exists, that the problems start.
However, to be fair and something that C++ haters tend to cleverly forget, trying to update a .NET Windows Forms application written in C# 2.0 into C# 10, Java 1.4 into Java 17 and so on, will face similar issues in complexity and language idioms.
The only difference between the two is that the C++ qsort implementation lives as inline code in a header file, while C's qsort lives "out of reach" in the C runtime library. If C's qsort implementation is in the same compilation unit (or LTO is used), a C compiler has the same optimization opportunities as C++.
For example here's a C qsort example where the compiler can see the qsort implementation (using an inline copy of MUSL's qsort). Note in the assembly window how the comparison callbacks have disappeared completely, this is basically C's version of "zero cost abstractions" (which really is just a fancy name for "compiler optimization passes", which isn't unique to C++):
Secondly. std::sort is a template with the type of the comparator being a template parameter. This means that if you use a lambda or functor as the comparator, the template will create a std::sort instance that always calls that comparater and the decision to inline the comparator is relatively simple. In controst, for the qsort example the compiler will need to either inline the whole qsort at the call site (which happens in your example) or create a specialized copy of the qsort function for your comparator. If you switch the compiler to GCC neither happens in this case (it does specialize it for some of the other parameters, but not the comparison function).
Not really. I only use header-only libraries and dependency management is a breeze for me. I check each library out as a git submodule into a third-party subdirectory in my project directory, point the includes path at it and off I go. When I want to upgrade, I pull the git tag/branch/commit I want and recompile. Its pretty simple.
How so?
> Also this works nicely for self contained dependencies, and much less nicely for not self contained dependencies.
Doesn't matter. You said: "You can speak for everyone." which is simply not true, as I have outlined. You didn't say anything about not self contained dependencies or whatnot. The person you replied to said "I spent an unconscionable amount of time managing dependencies and libraries in C++" and I... well, don't. So clearly not everybody does.
Rust catches data races, use of non-thread-safe functions, and use-after-free problems across the entire codebase, including (mis)use of 3rd party libraries, at compile time. I can add `.par_iter()` to a loop, and if it compiles, I know it doesn't mangle any data. If it doesn't compile, the compiler points me to the exact struct that needs a refcount, or a mutex, or atomic. That saves me weeks of chasing heisenbugs.
In debug builds it checks all integer arithmetic for overflow (no need to wrap types in custom class, works for dependencies too). This has saved my ass a few times.
There's a pretty decent standard library, and a good number of quality dependencies that can be installed with a 1-liner. If I need to (de)serialize a struct from JSON, it's a couple of lines. I want to switch that to msgpack instead, 2-line change. I need that compressed? 2 lines again, plugs in easily, and supports serialize-and-compress without an intermediate buffer.
Build system uses convention over configuration, so there's no artisanal "build script" to manage, or even an argument which build system sucks less. Dependency management and workspaces work well enough that it's practical to modularize projects into smaller libraries, and actually reuse them. Unit testing is built-in. Doxygen-like generator is also built-in, no config needed. Of course any language can do the same, but having zero friction removes excuses and really helps making people actually use these things.
Qt and C++ it is then.
Neither C nor C++ could be considered new languages, and this seems to contribute to this estimation of embedded engineering as a "lost art." C and C++ give a "here be dragons feeling," that only the best engineers can hope to avoid pointer pitfalls and memory leaks. I wonder if Rust's advances in programming language theory could alleviate this problem. The new generation of programmers I feel I belong to are comfortable with functional programming, advanced types, and facile tooling like NPM.
I have personally found embedded engineering underrated in American industry, both for fun and profit. If adopting modern practices leads to further embedded engineering adoption, that would be a worthwhile pursuit for individuals and our community. Perhaps this takes the form of Rust.
[a] Top 300 in global ranking, with a standard but not cutting edge computer science program unlike Stanford or CMU. Having gone to only one university I admit this confusion could simply be a failing of my own education.
Embedded programming in C++ is pure fun. The more modern your code is, the less room you leave for bugs: when it compiles, it mostly works. Where it doesn't, it shows up in your logic, not in random memory corruption.
Constexpr methods seem perfect for embedded in a lot of ways too. Yes, I won't be touching real memory or ports, but I can ensure that the method isn't doing silly things either. Having a memory address class that filters on the proper operations and only allows them is awesome. Saw a good video on the topic recently too https://www.youtube.com/watch?v=fsMmh8F8uV0
C++ code bases may have less buffer overflows and off-by-one errors but I don’t trust them unless they come with an extensive safety-net in the form of unit-tests with good coverage.
I think the cultural shift is partly just people voting with their feet and going where the money is.
It refers to an approach that doesn’t worry much about how things were done in C and instead thinks about how to represent things cleanly and efficiently with the aim of faster development and fewer bugs.
I was an embedded developer for years and still write a lot of code in C++ because it results in fast code that is easier to write than if I tried in C.
One of mantras of the c++ committee is that you should pay (have overhead) for the stuff you don’t use. So I don’t try to write code that looks like C.
For some industries where security or correctness guarantees are necessary, you see things like MISRA C used rather than Rust (or cpp).
[1] https://en.wikipedia.org/wiki/Ravenscar_profile
[2] https://en.wikipedia.org/wiki/SPARK_(programming_language)
[&] https://docs.adacore.com/sparkdocs-docs/Examiner_Ravenscar.h...
That's a big set of ifs, though. It's possible that the compiler errors are discouraging enough that C++ might feel like a more comfortable choice (I would encourage you to not let this be your reason unless you're just interested in solving a one-time problem). I keep checking in on Rust to see how small and dependency free I can get the compiled binary, and I usually need a mess of nightly features. The ecosystem is also still a mess. There are a few great libraries, but otherwise it's a minefield of 0.x releases for most dependencies (admittedly, some of the most important ones are autogenerated bindings where the version number is meaningless). Bindings are really easy to write with Rust, however, and it's one of the few languages that really gets that right, but it's nowhere near as convenient as calling/using C or C++ from C++. Most of these issues are consequences of C and C++ having the existing inertia, but it is what it is. Some people want to ship things and not join a community.
If you're trying to get a job or just learn: learn both. C++ isn't going to go away completely any time soon, but, with every month that passes, there are fewer and fewer technical reasons to justify starting a new project in it vs Rust. Choosing an inferior tool because it's the only one you understand might often be the correct choice, but it's not a choice you want to have to make.
It's largely the realm of C, with some industries working in Ada/SPARK. In terms of its profile, Rust fits the bill for being a good system/embedded programming language some day, but I don't know if it's mature and stable enough yet to be what you'd pick for a really serious job.
As for C++, I don't really see the point if you don't have technical debt.
I only recently investigated rust, and was hugely impressed with it. I regret not investigating it earlier. Language support for ownership and lifetime of variables is such a huge step up for writing code. Trying to do this through as hoc conventions in C, or through RAII in C++, is a lot harder.
I haven't yet had a chance to write anything significant in rust beyond a few small test projects, but next time I want to beat Python performance on something I'll be using Rust instead of Cython or C or C++.
But, say, if you don't have a good support for your platform Rust is not useful. Ditto for most existing codebases. And in any case I don't see why your primary concern should be being modern vs getting things to work well
Debugging C++ when your compiler for weird target (SH4, or worse) contains bugs, or when you have some relaxed memory consistency model, is quite a challenge because mapping C++ to assembler is definitely non-trivial. C, even with heavy optimizations, is a breeze comparatively.
I would happily join a team that uses Rust in an embedded setting. It shows that they care deeply about doing better than average, and that probably means they are better than average in everything.
I don’t agree that C++ automatically prevents bugs, even though using e.g. RAII can help a bit and leads to maintainable code.
If you use exceptions (you probably shouldn’t) there is one more code-path out from your code. That together with constructors/destructors being called and using STL instead of more trivial data-structures often leads to a lot bigger assembler output to trawl through if you need to.
By STL I guess you mean std::string and std::vector. That is wholly optional. I would not use those in a small embedded project, except maybe at startup, and then with a custom, non-freeing bump allocator.
- No exceptions. Instead they had util::Status (open sourced as absl::Status [1]) and a union type util::StatusOr<T>. I don't believe there's a C++ equivalent for this yet. There's a draft proposal for std::expected [2] but it didn't make it into C++20. I'm not sure what the future is. After many years I truly believe exceptions are a false economy for error-handling;
2. Teams can't write their own templates. This is for good reason. There's almost certainly no good reason to do this and you'll probably screw it up what with correctly handling rvalue references (eg perfect forwarding [3]) and the like.
3. No mutable reference parameters. These are really an anti-pattern and can give the caller no clue that something is modified. Worse, if you allow them you'll find people forget to const their references so you don't actually know if they're modified or not. Even worse, the compiler doesn't know either so it limits optimization opportunities.
It wasn't perfect. For anyone in the know I'll just say "int/size_t" and leave it at that, as one example.
Here are some practical problems:
1. The language is incredibly complex. This makes tooling more difficult. It also means any large C++ shop will out of necessity limit the features allowed such that every shop using a different C++ dialect;
2. As much as we now (since C++11) have smart pointers, you'll still find yourself using raw pointers. You then get into the issues that arise from this, primarily using pointers after their lifetimes and documenting who owns what the pointer points to;
3. The focus on threading primitives. I am now firmly in the camp that if you ever manually create a thread, you've screwed up. The potential pitfalls and errors from doing so are just massive. Most of the time you don't need it either. You should be using higher concurrency primitives or, in a lot of cases, use a cooperative async-await model as you rarely ever need to write truly multithreaded code;
4. (This applies to C too) The fact that types are platform dependent mean you need to use translation layers. So the standards only say a short isn't longer than an int and a long isn't shorter than an int and that's it.
5. Even initializing a variable is complicated [4];
6. std::string vs char * interfaces. Fun fact: there was a time in Chrome when every keypress on the Omnibar resulted in >25,000 string copies.
7. The ability to use uninitialized memory.
8. Stack vs heap allocation is obviously useful but the complexity cost is just massive. Just look at lambda capture [5].
7. The learning curve for C++ is just huge and that entails a lot of useless knowledge (eg explicit constructors, default constructors, copy constructors, move constructors, copy constructors vs assignment operators).
Almost all the time you don't write code for performance. You write it to work and for the next guy to know what the hell is going on and it's this last one where I think C++ falls so short.
I've known too many self-proclaimed C++ experts who revel in the complexity of C++ and consider it a virtue. It's like it allows them to show everyone how smart they are by using some esoteric feature or relying on some non-obvious default (eg a bit like the Javascript semi-colon ASI egoists).
I just never want to touch it again.
[1]: https://abseil.io/docs/cpp/guides/status
[2]: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p032...
[3]: https://eli.thegreenplace.net/2014/perfect-forwarding-and-un...
[4]: https://en.cppreference.com/w/cpp/language/initialization
If you are drawing conclusions from what you saw at Google, you have opinions about some other language that only just happens to be compatible with a C++ compiler.
In fact, AOSP has plenty of examples on how not to write C++ in this day and age.
Also I found the protocol buffers and RPC C++ bindings to be straightforward. YMMV.
Additionally the tooling reasonably pleasant (at least for "leaf" projects in the build system).
I know if I were starting a C++ project from scratch that after questioning my life choices that led me to this point, I'd just auto-start with Bazel, Abseil, Gflags, GLog and GTest. The whole thing would also have to work with CLion.
For non-negative values less than 2^31, these will happily cast among themselves "correctly", but outside that range you will run into trouble.
As one example of what can go wrong, consider the following code, where we have a 32-bit int (signed) and a 64-bit size_t (unsigned):
size_t s;
int i;
s = 0x00000000f0000000;
i = s;
s = i;
printf("0x%lx\n", s);
We've first assigned to s a valid value for its range (but greater than or equal to 2^31), then assigned s to i, casting it to i's int type (hint: i is negative here because of the int type's sign bit), then assigned i's value back to s, casting it back to a size_t (hint: this performs sign-extension of the most significant bit). The final value of s, as printed by the last line above, you will find is 0xfffffffff0000000, which is not the original value we assigned!
How do they do that? As string lengths can vary. One way might be to store the continuous strings in memory and then maintain an array of pointers to those strings. But then we are back to pointers.
Searched google. Didn't find anything.
But you can be smarter than that. As it happens, I just implemented "vector of strings" where all strings are contiguous in memory.
The code: https://github.com/sumatrapdfreader/sumatrapdf/commit/bc8311...
The basic idea is like you said: you keep strings contiguously in memory and maintain a separate array of pointers to each string. Except it's not a pointer but u32 index into that block of memory.
Even if those were pointers, not the u32 indexes, it would still be cache friendly.
The point is that strings are close together.
If each string was allocated with malloc() then you don't control where they are allocated.
In the worst case each string could end up in a separate cache line, which would be much slower if you were to iterate over all strings.
The data will stay contiguous as long as it's constant/read only.
But as soon as you start to use it and change it for doing some real work, it will get scattered.
Nice property of the libc++ one is that a 64bit integer will fit into it without allocating
auto lim = atoi(argv[1]);
Use of atoi. And what if there is nothing at argv[1]?