
Breaking all the Eggs in C++ (2015) - signa11
http://scottmeyers.blogspot.com/2015/11/breaking-all-eggs-in-c.html
======
olooney
For the transition from Python 2 to 3 the Python team released just such a
conversion tool, aptly named "2to3." [1] This was better than handy but the
transition from 2 to 3 was still lengthy and painful. In practice many of the
suggested changes were unnecessary or at least needed human review (e.g.
suggesting changing "for n in range(1,11)" to "for n in list(range(1,11))".
Thanks, 2to3, but that code is still correct and actually faster with the
iterator version of range().) Another problem was that, just like it says on
the tin, 2to3 produces a version of code that _only_ runs under Python 3,
whereas most library authors during the transition time wanted a version that
would run under _both_ 2 and 3 a la the six[2] library.

So while a one-way automated refactoring script sounds like a good idea, and
is strictly better than just breaking backwards compatibility and expecting
everyone to deal, it wasn't an unqualified success for Python.

[1]:
[https://docs.python.org/3/library/2to3.html](https://docs.python.org/3/library/2to3.html)
[2]: [https://pythonhosted.org/six/](https://pythonhosted.org/six/)

~~~
faho
Python has the additional problem of being highly dynamic, though!

In C++'s case, the transformation tool has much more information, so more
things are possible to do.

E.g. let's take the ".erase" case from the article. In python, a method

    
    
        def foo(bar):
            bar.remove(baz)
    

can't simply change "remove" to "erase", because it has no idea what type
"bar" has.

A C++ tool sees

    
    
        void foo(std::list<sometype>& baz)
    

and can then safely change "remove" to "erase".

~~~
wbkang
I think that breaks down as soon as templates are involved, which is very
pervasive in C++ codebase.

Given

    
    
        template<typename T, typename sometype>
        void foo(T<sometype>& baz)
    

It's not so straightforward anymore.

~~~
jgtrosh
It can still apply the operation anytime a specialized object is defined.
(This is how templates are compiled, right?)

------
pjc50
You can't remove features from a language, that creates a new language
instead. The transition then takes _years_. Haven't people realised this since
the Perl 5/6 and Python 2/3 transitions?

~~~
leetcrew
I'm a little bit surprised to see this coming from scott meyers, who I
generally assume to have much better judgment than I do when it comes to c++.

I would think (or at least hope) that there are not many new projects being
started in c++ these days. with this assumption, it seems like the main focus
for c++ today should be on adding features that enable developers to
incrementally rewrite old code with a view towards increased safety. range-
based for is a great example of this. a large portion of unchecked vector
accesses within loops can be trivially rewritten with range-based for to be
much safer, and a small team can simply fix them one by one as they quash
bugs.

on the other hand, removing NULL all at once in a legacy codebase could be
almost impossible for a small team. I've seen many unfortunate instances where
the code actually relies on NULL being the integer '0'. fixing this kind of
thing across thousands of files would be an absolute nightmare.

I am certainly sympathetic to the idea that the code that will be broken by
these types of changes is sloppy and should be rewritten anyway, but this is
unlikely to actually happen. most resource-constrained teams are just going to
freeze their projects at c++yy and never benefit from any new features.

edit: I partially retract this post. the gradual 10-year deprecation process
he specifies seems reasonable enough. I still question whether making c++ less
confusing to newcomers is a good use of resources. there's really not a lot of
situations where it makes sense to start a new project in c++.

~~~
twic
> I would think (or at least hope) that there are not many new projects being
> started in c++ these days.

I would imagine there are fewer new projects in C++ than, say, JavaScript, and
fewer new projects in C++ than old projects in C++.

But C++ is absolutely still a live language! For the simple reason that there
is no alternative which can work in all its significant niches (systems,
realtime, embedded, HPC) and which is mature. Rust is on its way. D could be
on its way. Ada never caught on widely, for some reason.

~~~
bdavis__
Ada was also too slow to compile with the computer systems of the day. I
remember a HW company coming on site with a demo (contained on 9 track tape,
of course). Compilation of a couple thousand lines of ada took all day (5+
hours). In that era, couple of thousand lines of fortran was 10 minutes. Big
difference. Ada was ahead of it's time. Still the language of choice when you
care more about non-functional requirements.

~~~
zozbot123
> Compilation of a couple thousand lines of ada took all day (5+ hours). In
> that era, couple of thousand lines of fortran was 10 minutes.

And a couple thousand lines of Turbo Pascal was 10 seconds! XD

------
AlexanderDhoore
If everyone just runs a static analyzer after compiling you could have best of
both worlds. For example the tool would complain if you don't use 'override'.
Problem is that not everyone runs the same tool. So at work I would like to
compile with (for example) -Wconversion (warn about implicit conversions that
lose precision). But this is impossible because Qt headers raise so many
warnings on this. And because C++ has no module system I can either 1) fix all
of Qt or 2) don't add the warning and keep writing old C++. I think you can
guess what I will do.

~~~
jcelerier
you can just include Qt with -isystem instead of -I, it will disable warnings
for those headers

~~~
hermitdev
Definitely recommend using -isystem for third party libs such as Boost, ACE,
QT, Protobuf, etc. I strive for warning free code in my own, but dont have
much control over the libs I use. Warnings from Protobuf are especially
annoying due to Googles ill-thought-out disregard for signed/unsigned
comparison (and their bullshit rationale in their style guideline) and
continued use of NULL/0 as a nullptr constant (they only support C++11 and
greater, no reason to not use nullptr now). I strive for clean compilation
with "-Wall -Wextra -Wzero-as-null-pointer-constant -Werror", but alas, hard
to implement after the fact in legacy code.

------
waynecochran
Anyone who has programmed with Objective-C (or Swift) for years knows Apple's
language deprecation cycle. Write code one year, the next year you are flagged
with a deprecation warning -- Xcode of course will help you upgrade your code.
Your unchanged code will probably work for another year and then suddenly it
is an error. The "magic wand" of Xcode / Clang helps you thru this
transistion. Apple likes to break eggs.

~~~
b3b0p
Has Objective-C been deprecated? I was under the assumption it's still fully
supported. It obviously won't see the same level of updates, but it's also
been around for a longer period of time to mature.

I honestly like Objective-C and prefer it, but I have been using Swift mostly
for the iOS projects I have done in recent year or 2. Mostly out of curiosity
though.

~~~
eridius
Obj-C has not been deprecated. And in fact the only parts of Obj-C itself that
have actually been deprecated were during the migration to "Obj-C 2", where a
slew of runtime functions (that nobody used) were removed, as well as direct
access to the `isa` pointer (I don't actually remember if the `isa` pointer
access was removed when Obj-C 2 was introduced or later when we got tagged
pointers).

What the parent is likely referring to is APIs from the SDK, which do get
deprecated, and then years later (many years later) removed.

~~~
saagarjha
> a slew of runtime functions (that nobody used) were removed

Hey, class_poseAs was useful :(

------
gmueckl
Scott Meyers overlooks one problem with his quest to kill backwards
compatibility: upgrading compilers is a no-go for many safety critical
codebases. The re-certification of the code for released products would have
to be repeated in its entirety - a sneaky bug or behavior change in the new
compiler might introduce any number of issues. That isn't going to fly for
cost reasons. This effectively freezes shared code between products at these
ancient language standard levels.

------
dang
Discussed at the time:
[https://news.ycombinator.com/item?id=10562105](https://news.ycombinator.com/item?id=10562105)

------
Figs
> Just replace the use of ++ with an assignment from true.

You can't just replace it with assignment to true directly as postfix ++
evaluates to the unmodified value; you actually need to replace b++ with
something like (b?true:(b=true,false)) to avoid breaking code that uses b++ in
an expression. That seems significantly uglier than just allowing b++ to
continue to exist though...

------
havermeyer
This sounds like a terrible idea. You can provide a way for people to opt out
of legacy behavior, but don't break them outright unless you want to lose your
most faithful, long-term users.

------
MaulingMonkey
> All we need is a magic wand that works instantly and flawlessly.

> That makes clang-tidy, if nothing else, a proof of concept.

More like a proof of failure. Apply clang-tidy's transformations to source
code and now you've now got one gigantic merge conflict to resolve every time
you merge from upstream. That's partially fixable if you script your
integration, or your integrators have good checklist discipline (they don't)
and apply clang-tidy to new upstream commits before trying to merge them, and
you've properly documented that.

Downstream is more problematic. And in my experience, large merge conflicts
like this are just begging for hard to spot bugs introduced by mismerging
commits - much more common than I've noticed bugs from passing 0 to pointers
or NULL to non-pointers. To add insult to injury, it'll fail to finish the job
- for example, trivial edge cases like 0 being used in a macro which is
interchangeably used to initialize ints and nullptrs depending on context - so
now you need manual intervention to actually fix up the code enough to
actually "break the egg".

Not to mention NULL in C headers, as mentioned in the blog comments, being not
a great thing to get rid of.

> Experience shows that relying on compiler warnings to inform programmers
> about "bad" practices is not reliable.

Get a bigger CI build matrix. This is a criticism of the implementation, and
perhaps those who don't use warnings-as-errors - they have much bigger
problems than a confusing 0/NULL/nullptr trifecta though. Standardize some
warnings? Sure! We've been adding things like [[nodiscard]] that, while still
not requiring warnings, strongly encourage them to the point of being just a
hair's breadth away from standardization.

Several compilers don't even like enabling new warnings by default - hence the
-Wall -Wextra -Weffc++ -pedantic -etc -etc -etc dance, which still doesn't get
everything you might want. If you want to change the culture around that so we
opt out of warnings instead of opting in, sure, I'm on board with that. We
already have the tooling to crank those up for your own code while using more
permissive settings for third party code you'd rather not modify/break.

If you can't convince compiler vendors to warn more by default, you're going
to have even less luck getting them to break code by default (unless you can
trick them into thinking it's an optimization).

TL;DR: We've got more reasonable, less drastic tools. Maybe push those to be
used more efficiently, instead of inviting a new Python 3 fiasco. I'm sure I'm
not the only person who would jump to Rust (or Go, or Nim, or ...) instead of
to C++vIncompatible - because if you're going to make a breaking change, why
not break enough things to make it worthwhile?

~~~
BeetleB
>We've got more reasonable, less drastic tools. Maybe push those to be used
more efficiently, instead of inviting a new Python 3 fiasco.

Most people I know who complain about the Python 3 transition are not heavy
Python programmers.

Migrating to Python 3 was certainly a pain, but not really a major one. The
number of developers who fled Python because of this is miniscule. If the
Python 3 transition is the poster child of "things going wrong" with backward
incompatible changes, I would heartily recommend other languages make those
incompatible changes.

~~~
ci5er
Really?

I believe that moving a 500k loc code base qualified us as semi-heavy. And
relying on packages that didn't move qualified us as stuck.

Now - don't get me wrong: i/we liked python2. There was little wrong with the
language or sticking with it. But please don't think the cost of migration was
a weekend of lost coffees.

