Ranges, Code Quality, and the future of C++
medium.com
medium.com
1. Longer compilation times
2. Your program may be too slow in debug mode for you to meaningfully debug it
We're kind of at a crossing point where I would love it if I could have a compiler that "de-sugars" these expressions into optimized C code, i.e. removes the lambdas, adds local variables, etc. and then compiles with no or minimal optimizations for debug mode. I really love gcc's -Og mode, which should do something very close to that, but I still have to read the assembly code to get a full picture of what's happening. I feel like we could do a little bit better.
Edit: And if you're wondering why I care that much about performance, it's because there is really no reason to use C++ today other than to write high-performance software. There are other languages that can get bare-metal performance and I don't want to insult anyone using Fortran, Ada, etc. plus I'm keeping a close eye on Rust, but the combined tooling that exists for C++ still makes it a better, more productive alternative as far as I can judge. The other reason to use C++ would be to interface with a C++ system, in which case it might be easier to just write everything in C++ and skip the translation layer pain, but you might not care that much about performance in that case.
Next time you have a program that takes a while to compile, try profiling clang with perf! It's super interesting.
Also this is just my opinion. Of course it could be that the original example is hated by all and a total POS, but to me it seemed quite clear in a functional context what it was getting at and what it meant for use of dynamically-created generators in higher order functions.
But, as a developer, who has dealt with an absurd amount of bugs that can be prevented from these modern c++ features I really like this.
Just make sure that the cure isn't worse than the disease. It's all to easy to slip into identifying with C++ because of the substantial effort it takes to master.
Should you, like me and many others; eventually find yourself spending more time and effort on taming C++, don't hesitate to let go. It's a tool.
Not everyone is running Solaris on an ADI enabled SPARC.
The reason is that the modern C++ is perfectly suitable for high-level programming -- on par with Java, C# or Visual Basic. No "taming" is needed.
So the only way to prevent such security exploits is by preventing copy-paste compatibility with C, which C++ sadly cannot do without breaking backwards compatibility.
You can see this happening across all desktop and mobile oriented OSes, where both C and C++ have been reduced to high performace low level OS layers, with userspace migrating to something else.
Microsoft is the only vendor left on OSes for the consumer space that still cares enough to support C++ on their UI toolkits.
"Swift is intended as a replacement for C-based languages (C, C++, and Objective-C)."
Taken from https://swift.org/about/
"Swift is a successor to both the C and Objective-C languages."
Taken from https://developer.apple.com/swift/
Launchd, dock were announced as rewritten at WWDC 2017, the new XCode build system and instrumentation at WWDC 2018, and it will continue little by little.
Naturally it won't happen overnight, but Apple isn't known to endure Python 2/3 scenarios for that long.
At WWDC 2017 they mentioned that launchd was fully ported. The video is available.
This is completely untrue.
Selective optimisation of hot code will always lead to execution time being spread evenly through your code.
Most of the time, the C++ help isn't even necessary anymore, unless the systems were doing some GPGPU stuff, real time audio or high performance graphics.
Using them in scenarios where performance matters is the typical everything looks like a nail.
Feel free to disagree, my experience during the early 2000 with Tcl has taught me to never use languages without native support for JIT/AOT toolchains ever again when performance matters.
Anything related to distributed computing, performance does matter.
These new right left-values, special rightvalues etc make my head hurt. Template programming makes my head hurt. I can't look into fancy cpp code and understand it anymore.
I'm wondering have new programmers are supposed to catch up too all the things in cpp even now and the mountain is getting ever bigger.
The trick was to treat it as a brand new language that happened to have some resemblances to other languages some of us already knew (i.e. C, C++, etc). C++17 is actually a pretty expressive language and pretty fun to program in.
I recognize most people don't have this luxury. Interfacing to the small number of external packages we used involved making modern "cutout" connections to the legacy packages, which actually was worthwhile.
K. was just asking how your new project has gone.
Reading the post, I thought, "hey, that sounds like Gumby", and then looked at the byline.
I am running C++ on an Arduino now, and prepping to teach my son to run it on a Blyst Nano. He wants to make his own smart watch. He will end up doing a lot of very educational yak-shaving on the way.
Hope to see you again sometime soon.
N
This, a thousand times. It's all very well for gumby to say that C++ is a big toolbox, but a good toolbox doesn't have all the mismatched tools from ten previous toolboxes all thrown together. Longcommonname can say that using modern C++ features help prevent errors, but that statement becomes a bit problematic when the most modern C++ style becomes deprecated C++ every few years. As the OP aptly illustrates, there are just too many ways to do even fairly simple things. All were idiomatic "best practices" for their time. It's not a problem if there are many ways to do things if they're all comprehensible to someone who knows the universal core of the language (as is often the case e.g. in C or Python). It's a problem when understanding each of them requires a whole separate excursion through some separate specialized and time-bound part of the standard, yet they all exist together in any large codebase that has had to build on those shifting sands. That's not good for maintainability. Future programmers will misunderstand the intent of code using each transient style, and either break it when they change it in place or miss some important detail when they try to replace it.
Different versions of C++ should remain separate to a much larger degree than is currently the case. Trying to combine several distinct language-as-written into a single super language-as-compiled (a mistake also made by Scala) never seems to work out very well.
Yes, you can have major breaking changes every few years. But do you really think every larger C++ code base being stuck with the C++ version it was founded on is the way to go? Should they all be rewritten for each version?`
Don't get me wrong, I'd love if we could deprecate more old stuff. The problem is that it doesn't just disappear from existing code (of which there is a _lot_) by marking it as such. If there are straightforward replacements (e.g. auto_ptr -> unique_ptr) that's one thing, but unless you want each new C++ standard to be unusable with existing code, you can't just do that.
Yes. It's the codebase for one of the largest data storage systems in the world. If it stopped working, literally billions of people could be affected. Is that good enough to get past the ad hominem?
> past the idealism.
Is the idealist the one who questions the wisdom of changing a codebase that already works, or the one who demands it? It's all too easy to accuse others of being too idealistic, or to make up strawmen (see next point), but I don't think those are very constructive ways to approach a discussion.
> do you really think every larger C++ code base being stuck with the C++ version it was founded on is the way to go?
Of course not. But the transition from old idioms to new ones can be managed. The first thing that has to happen is that the people adding new features to each standard must be explicit about which old features are being deprecated when. Sure, you can specify --std=c++25 to get the new hotness, but then that might preclude use of old feature xyz from C++11 in the same compilation unit. You have to choose, and you should choose, according to pragmatic needs instead of mere neophilia.
> unless you want each new C++ standard to be unusable with existing code
Controlled deprecation doesn't necessarily mean 100% incompatibility between versions. N-1 is a very common model, which can be extended across any K prior releases. There are tons of examples, not only from languages but from APIs, file formats, operating systems, and even other kinds of engineering besides computers. People should absolutely be given time to adjust, but the time should be finite. "Simultaneously support everything that every existed" is the one model that's least likely to work in the long term.
Regrettably they typically do, as well as three kinds of the same kind of screwdriver, with slightly different lengths (c++'s equivalent would be 'for', 'while' and 'do..while'.). One of the appeals of a new language (e.g. rust) is that you don't start with the legacy boat anchors...yet (look at Python or perl).
I don't think there's a very good solution.
Go tried to go the other way by explicitly not having lots of affordances. Personally I'm not a fan but I can see the logic.
Yeah, true. Maybe the better analogy is not the toolbox but the screws, bolts, etc. that the tools operate on. I have several sizes of Torx and other exotic screwdrivers which I have never needed ... but I might some day, if some idiot who knows where or when decided that they just had to use that particular screw for something. In large long-lived C++ codebases there always seems to be some asshole who used the equivalent of a MorTorq or Tri-Wing just because it looked cool, so that feature has to be supported forever after. Programmers years later then see this far from self-explanatory code, because these additions always seem to involve using familiar symbols in arbitrary new ways, and have to pore through old versions of the standard to figure out WTF it does. I do think it would be far better if the old screws and the drivers for them were deprecated from time to time.
My initial hypothesis was that the language was evolving to simplify how a someone can stitch together multiple libraries to create an application. If the complexity can be reduced (by say, adding garbage collection), then the quality of programmers a software company would need to hire can drop from the Donald Knuths of the world to someone who is more likely to submit their resume at an average software firm.
However, at this point, it is safe to claim that the complexity of keeping up with C++ far exceeds the complexity of the original problems it was trying to solve.
Does anyone have a good argument for making C++ so convoluted?
Plus even other languages that appear simple, are only so for those that don't really know the complete language standard.
Python is my favourite example, as it is demeed as simple language for begginers, yet the language + library reference PDFs are also a couple of thousand pages long, and there are so many PEPs that hardly anyone can pinpoint which is the minimum Python version to execute a random piece of sample code.
There are other approaches: e.g. js + npm, which can get you super rapidly to something functioning. It has different risk points than C++, and different benefits. If it works for your use case you'd likely be a fool to use C++, especially since its "standard" library is so much larger. But likewise there are complicated problems for which you'd be wrong to use node.
Transportation runs from bicycles to mack trucks to tanks to spacecraft, each with different complexity and performance issues.
What it means is that you can quickly write a powerful program or a team can build a large, maintainable system. Some people on that team might use some of the more abstruse tools to make stuff (specific macros (aka templates), other complex objects) that create common resources used across the team.
You can choose a simpler language but then for some cases every user needs to repeat certain boilerplate and/or risks forgetting some important corner cases.
You can let everyone free to use any arbitrary tool in the toolbox but then you equally end up with an unmaintainable mess.
This really isn't any different from building, say, a power plant. Some people have gone to a lot of work to develop the steam schedules (regulatory code, such as schedule 60 piping) and you can buy them and have people who know what they are doing weld them up as you need them, but you don't have to design the safety code, simply be sure you're within its constraints.
It remains the funniest thing I have ever heard said about C++. It completely derailed what had been becoming a tedious conversation. I don't recall what Language X was.
As an example, the < and > characters may indicate comparison (< and > in boolean expressions), a template declaration (< and > with a comma separated list following a function/class name declaration), a template specialization, an operator overload, an object access via pointer (->), a bit shift, (<< or >>), and possibly writing to a stream (<<).
A less egregious example, [] might be used to indicate indexing into an array, indexing into an object via overloaded [], declaring an array type, or declaring the captures in a lambda. While you can figure it out contextually, it's often irritating to have to figure out context first and then interpret the code.
I do agree that the stream operators << and >> was a bad experiment in retrospect.
Look at rust, or python, those are great languages, but obviously you need a new compiler and you need to write new code.
With C++, you can just add new code to an existing code base, and share that code across projects.
What is nice is that the language is evolving, but previous code still works. So the problem with C++, is adding new cool features, on an existing language, while not breaking existing code bases.
That's how you improve a language, by keeping its users while adding new better ways of doing things.
I honestly don't think rust or go will really survive over the decades. C++ already has momentum, and honestly, its close-to-C syntax is still a good thing because it's intuitive. How down-to-earth simple do you think go or rust are for students? How do you wrap your head around having to explain students the purpose of a garbage collector, not to mention how it will behave and how predictable it will be?
Adding a garbage collector would make code more complex, not simpler, as it would break RAII. I spend a lot more time dealing with memory and resource allocation issues in GC languages than I do in C++.
In languages like Python we have to use `with`, and this is harder to use in the client code, and the class is much harder to write, than anything in C++ where we just add a destructor to clean up.
std::vector<char> data(10 << 20);
std::ifstream("/dev/urandom", std::ios::binary).read(data.data(), data.size());
The above code can never leak (either memory or file handles) under any circumstances and the implementations of the types involved push zero overhead/boilerplate on me as a user of them.I think if you want to reduce complexity of C the place that everybody else is looking is at how to allow opting in and out of legacy support.
The imperative-style examples don't compose in any meaningful sense. The only thing you can really do with the imperative style programs is to sequence them, i.e. run one after the other.
With ranges you can apply various types of transformations, combine multiple ranges, it lets the consumer(!) decide how much work should be done, etc. etc.
The gives incredible leverage for code reuse.
What's wrong with that? Isn't that the most readily accessible (and therefore maintainable) kind of composition? I realize that some people might prefer other forms of transformation or combination, but can you give an example of where it provides a tangible (not just aesthetic) benefit?
(Alright, this is C++ so there's a little bit of cost due to a tiny amount of extra 'syntax' where C++ imperative syntax is "free", but that's trivial for any but the most trivial of examples.)
In these terms it looks like a tautology. But the essense is still there - if for some reason you'd like to have a freedom of order of some transformations, you need composability, as an opposite to an approach which prescribes the order.
Either you grok yield or you don't.
I do like new advanced features but we must not let them make us lose sight of the metal.
c++11 remains to be the most popular language in algorithm competitions(e.g. usaco), 51% usage there, along with 33% java and 12% python, which surprised me
However it is better than keeping with plain old C.
I don't know much about Ada except what experience I've had with PL/SQL, which is supposedly related to it.
I found myself counting the number of characters I'd have to change to get that first C++ snippet to evaluate as ES6.