[Development] How qAsConst and qExchange lead to qNN

Marc Mutz marc.mutz at qt.io
Mon Nov 7 21:15:58 CET 2022


Hi all,

SCNR taking the proffered bait:

On 07.11.22 18:51, Edward Welbourne wrote:
> For Qt to remain relevant in the rapidly-evolving C++ landscape, it
> needs to change; but one of its strong selling-points has long been its
> conservatism in API and ABI, that lets working code keep working.

I've not been around for the Qt 1 → Qt 2 port, but I did participate in 
all of the Qt 2 → Qt 3, Qt 3 → Qt 4, Qt 4 → Qt 5 and Qt 5 → Qt 6 
transitions. Speaking with my Qt user hat on, I'm terribly sorry to 
inform y'all that Qt has largely _failed_ to keep working code working. 
Sure, a trivial QWidgets program from the mid-90s may still compile and 
work in Qt 6, but as soon as said program touches Qt container classes, 
it's game over.

Both Boost and C++ itself have a much better track record of keeping 
working code working, let alone any random C library. And this is 
actually a fair comparison, because we're comparing apples (STL 
containers) to apples (Qt containers).

Let's not kid ourselves thinking Qt is any special here. It isn't. We 
mustn't just look at one major release cycle. Qt loses projects at every 
new major release, because changes to non-core-competency parts of the 
API make porting unnecessarily complicated, so that unmaintained or 
understaffed projects[1] cannot muster the strength to be ported.

My goal with qNN is to make porting away _simple_. All that's required 
is to s/qNN::/std::/ and be done. No deprecation, no sanity bot, not 
even the need for local code knowledge; just simple global textual 
replacement. And no regressions, if you first switch to C++NN, and only 
then do the replacement. We can even retain the qNN symbols until Qt 
requires C++(NN+3), because the qNN headers will be nothing but a long 
list of using std::foo; at that time.

It's not really acceptable that such trivial ports should be subjected 
to all the same (or, apparently since it's done in bulk, more 
restrictive) requirements than for deprecation of core-competency APIs. 
The more so as I must have missed the outcry of developers when we inflicted

   // Universe A
   Qt 1      Qt 2     Qt 3                     Qt 4+5             Qt 6
   QGList -> QList -> QPtrList / QValueList -> QList / QVector -> QList
                                               QLinkedList

on an audience that could, instead, have had

   // Universe B
   Qt 1      Qt 2-4         Qt 4-5         Qt 6           Qt 6-7
   QGList -> std::vector -> std::vector -> std::vector -> std::vector
   Cfront    C++98          C++11/14       C++17          C++20/23

To hold users, a project must maintain _long-term_ API stability, not 
rewrite the container classes for every major release.

So, sorry, but as a user of Qt I'd really like to use those stable STL 
classes, if only your volatile APIs let me. I'd rather I had done that 
_one_ port between Qt 1 and 2 than all those ports in Qt 1-6.

Well, as they say, if 20 years ago was the best time to do the switch, 
then the next-best time is today[2].

Anyway; to all those who disagree when I say Qt should concentrate on 
its core competencies and stop meddling with container classes, shared 
pointers, etc, I say this: which of the two universes above would you 
rather have lived in these past 30 years? A or B? Be truthful!

Because, you see, whenever we phase out some Qt NIH API, we beam a small 
part of your (and our!) code from Universe A into the Universe B. I 
_really_ can't understand why anyone would complain. Must be a case of 
recency bias or something like that...

Speaking of the devil, from my pov, qAsConst isn't "small enough" to 
survive. Fighting off patches that try to make the deleted rvalue 
overload "do something" is _also_ maintenance work, even if it doesn't 
result in a code change.

Finally, I'll bet my behind (pardon my French) that at some point in the 
not-too-distant future someone will appear on Gerrit with a patch to 
change Q_FOREACH to use C++20 ranged-for-with-initializer:

   #define Q_FOREACH(decl, expr) \
      for (const auto _q_foreach_c = expr; decl : _q_foreach_c)

And I'd probably approve it, because then that thing can actually just 
be replaced with the expansion everywhere, and then, finally, be deleted.

B Universe, here we come!

Thanks,
Marc

[1] I need qpdfedit once every year to do my taxes, but every year it's
     getting harder to get the old laptop that still has it installed to
     run again (yes, I know of VMs)

[2] Shamelessly stolen from Isodope
     (https://youtu.be/ESAaz9v4mSU?t=514)

-- 
Marc Mutz <marc.mutz at qt.io>
Principal Software Engineer

The Qt Company
Erich-Thilo-Str. 10 12489
Berlin, Germany
www.qt.io

Geschäftsführer: Mika Pälsi, Juha Varelius, Jouni Lintunen
Sitz der Gesellschaft: Berlin,
Registergericht: Amtsgericht Charlottenburg,
HRB 144331 B



More information about the Development mailing list