
Ranges, Code Quality, and the future of C++ - pplonski86
https://medium.com/@jasonmeisel/ranges-code-quality-and-the-future-of-c-99adc6199608
======
Asooka
As a full-time C++ developer, my only gripe with features like these is that
they require nontrivial compiler optimizations in order to produce the
equivalent "old-style" high-performance code. That leads to two issues for me:

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.

~~~
jlebar
As a C++ compiler developer I don't think these features are what's causing
long compilation times. Most of them don't even require any changes to llvm.
AFAIK ranges doesn't, and the changes necessary for coroutines are simpler
than you may think.

Next time you have a program that takes a while to compile, try profiling
clang with perf! It's super interesting.

------
CorvusCrypto
In both the original example and this updated version, the nested loops (or
foreach calls) create generators on each iteration. However, the first example
is creating the generator chain more implicitly than the newer example. Since
the original example also uses a coalescing expression (i.e. I mean that he is
returning a dynamically-created generator chain as a variable) it means the
use of "return" keywords at every subcall to foreach which can confuse
programmers that aren't as seasoned with generators or functional generator
patterns. It's actually, I would argue, not "bad code" but rather a different
approach that procedural programmers probably aren't used to. The benefit of
this style of doing things is that it comes with the benefits that usually
come with creating lambda expressions. There are better examples of creating
on-the-fly generators, and surely thats what the original author wanted to
showcase, but to programmers not well-accustomed to common range-based
generator patterns, the Pythagorean triple example for this looks and feels a
mess when it's clear you don't need to generate the generator dynamically.
This updated example I think is more generally useful for showcasing the range
iterators to programmers.

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.

~~~
Koshkin
Agree. The "bad code" example looks beautiful to me, albeit unusual for
someone who is accustomed mostly to the imperative style; I have no doubt that
it would only take a small amount of (self-)training to absorb such style to
the extent that it becomes easy to read.

------
longcommonname
As someone who is on a personal journey to transition from c with classes
thinking to modern c++ I can see why there's so much hate from non-cpp
developers about how these features look ugly.

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.

~~~
sifoobar
I went the other way, started writing C++ in 95 and digging into C properly
around 2005.

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.

~~~
pjmlp
The solution is easy, managed languages for 90% of the code and C++ for the
remaining no-excuses full throtle execution speed, while offering reasonable
safety for arrays, strings, out parameters, enumerations, lifetimes.

Not everyone is running Solaris on an ADI enabled SPARC.

~~~
Koshkin
I have a somewhat different perspective to offer: C or C++ for components
(effectively 90% of all code), a managed or scripted code (10%) for the glue.

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.

~~~
pjmlp
Taming is necessary, because regardless of what you and me think about modern
C++, there is a large crowd that still writes C with C++ compiler with total
disregard for any kind of security validation tools.

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.

~~~
saagarjha
Apple supports Objective-C, which is arguably just as bad as C++.

~~~
pjmlp
It does, due to its NeXTSTEP legacy, but the roadmap is quite clear.

"Swift is intended as a replacement for C-based languages (C, C++, and
Objective-C)."

Taken from [https://swift.org/about/](https://swift.org/about/)

"Swift is a successor to both the C and Objective-C languages."

Taken from
[https://developer.apple.com/swift/](https://developer.apple.com/swift/)

~~~
saagarjha
I would be very surprised if Apple’s frameworks ended up becoming Swift-only
any time in the next decade. While Swift is the spiritual sucessor to
Objective-C, there is far too much Objective-C already written that would be
costly to obsolete, even if Swift had the facilities to do everything
Objective-C can (and, as I’m sure you’re aware, it is not at that point yet).

~~~
pjmlp
Sure, but that will eventually happen, even if they do just a couple of
components per OS release.

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.

~~~
saagarjha
Those are not frameworks, those are apps that ship as part of the system.
Apple has not and seemingly will not deprecate Objective-C for third party
developers. They haven’t even rewritten any significant code in Swift, IIRC.
Maybe parts of launchd?

~~~
mnem
Timac analysed several MacOS and iOS versions for swift usage, and its growing
through the system surprisingly rapidly for a language awaiting a stable ABI.
Here’s the most recent analysis, which also links to others:
[https://blog.timac.org/2018/0924-state-of-swift-
ios12/](https://blog.timac.org/2018/0924-state-of-swift-ios12/)

~~~
saagarjha
Yup, I’ve read those and that’s what I based my statement on. Apple doesn’t
seem to have rewritten any code in Swift “just for the sake of it”; they have
slowly introduced it the in places they could to add new features.

------
Entalpi
I feel like a development such as the C++ standard needs to deprecate things a
lot faster than they currently do. Imagine in 15 years time when C++ compilers
are so complicated that they become self-aware and decide to get rid of us
programmers.

~~~
notacoward
> the C++ standard needs to deprecate things a lot faster than the currently
> do.

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.

~~~
MauranKilom
Do you interact with any significant C++ code base? Because I'm having a hard
time seeing what your practical recommendations would be past the idealism.

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.

~~~
notacoward
> Do you interact with any significant C++ code base?

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.

------
dilippkumar
Why is C++ so complicated?

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?

~~~
gumby
C++ is a large toolbox. You don't have to use all of it, and probably
shouldn't use all of it in one program.

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.

~~~
ncmncm
It was Jonathan Shopiro (author or iostreams) who interrupted a comparison
equating Language X with a seaplane, interjecting that by such a standard, C++
would be a boat anchor with feathers.

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.

------
Quekid5
Really weird to read this and not a single mention (in the article or ITT) of
the most critical concept here: Composability. That is what ranges can do for
your code -- make it composable.

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.

~~~
notacoward
> The only thing you can really do with the imperative style programs is to
> sequence them

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?

~~~
Quekid5
I answered that in the following paragraphs: It gives you a huge amount of
potential for reuse -- essentially for free.

(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.)

~~~
notacoward
"Following paragraphs" is an ambitious term for barely one line of text. You
have _claimed_ there is some unspecified promised land of reuse through
unspecified transformations and combinations, but you really haven't shown
anything that's not equally achievable with "mere" sequential application. It
still looks like a matter of aesthetics - that you _prefer_ a more syntax-
laden functional style, but not one that's objectively _better_.

~~~
avmich
Let's define "objectively better" as "allowing to do A, then B as well as to
do B, then A - rather than only allowing to do A, then B". Then composability
- defined as "allowing to do A, then B as well as to do B, then A" \- is
objectively better than imperative style - defined as "only allowing to do A,
then B".

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.

------
nuclx
Looked at the code and my current endeavors to learn Rust are reinforced. I'd
prefer to never touch that abomination of a language that is C++ ever again.

------
cbsmith
Honestly, I don't understand how the coroutine version is easier to read, or
what was confusing about the original example.

Either you grok yield or you don't.

~~~
MauranKilom
I find it's a lot more boilerplate, more visual overhead in finding the
important bits in the sea of various brackets... But maybe I'm just blinded by
imperative upbringings.

------
notatcomputer68
Where are the local variables stored for a coroutine?

I do like new advanced features but we must not let them make us lose sight of
the metal.

------
shaklee3
Great example. The first one was very hard to understand, and this one made it
a lot more clear.

~~~
saagarjha
That’s an interesting perspective; I found both to be similarly complex. Both
boiled down to “lazy generators”, albeit with different syntax.

------
ausjke
my biggest problem with modern c++ is indeed readability.

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

------
muterad_murilax
Is modern C++ the new Ada?

~~~
notacoward
No. Ada might be too complicated, but it's far more coherent. C++ is like a
novel with each sentence written by a different author. Even worse, each
author keeps trying to pull the plot and the characters in a very different
direction. Any C++ codebase that spans more than one version of the C++
standard ends up combining the two into its own special dialect, defeating the
whole purpose of having standards in the first place.

------
dmix
Off topic: the code examples don’t scroll on mobile

