[Development] How qAsConst and qExchange lead to qNN

Volker Hilsheimer volker.hilsheimer at qt.io
Wed Nov 9 15:27:28 CET 2022

> On 9 Nov 2022, at 12:49, Marc Mutz via Development <development at qt-project.org> wrote:
> Hi Volker,
> On 09.11.22 10:15, Volker Hilsheimer wrote:
>> But I do believe that we can add APIs that are iterator and ranges friendly to Qt without tossing out the baby with the bathwater and without breaking tons of code and patterns. For APIs where we simply return a stored QList we want to be able to return that QList as a shared copy. If we would only return a std::span as a view on the stored list, then client code will need to construct their copy from that std::span. That is equally silly.
> There are two assumptions you are making here, and it's important to 
> understand them when discussing NOI:
> First, that when we return a copy of a stored QList, most users would 
> keep the data around for longer.
> I don't have the stats to prove it, but I'd say that that's a corner 
> case, not the general case. Even when talking about storing the data 
> temporarily in a function, no owning container is required. And if 
> there's no Qt API that requires you to pass owning containers, passing 
> from and to Qt API also drops out of the use-cases that require 
> returning a shared copy of a QList.

The assumption that I don’t have to make is: there is a tone of working code that calls existing Qt APIs to get or set QList values on objects. That code might or might not assume that this operation is cheap as far as making a shared copy of that list is concerned. The API called might or might not take that list and store it, rather than process it and store it in some other form. I honestly don’t care. I know that the code exists and does what the people who wrote it expect it to do.

We can probably in most cases add overloads to setters that take a std::span/QSpan, and construct whatever the implementation needs to store from that. But we cannot remove existing QList-taking setters, and we cannot change existing getters that return a QList to return a std::span. Even if it’s API/ABI compatible with the help from QT_REMOVED_SINCE.

> Second, that the data is stored in a QList in the first place.

Yes, that’s the case that I explicit mentioning in my email. It would be great to have alternatives that can return a std::span, and that doesn’t need to construct a QList or whatever in the first place.

An alternative, not a replacement. I am making assumptions, you are wagering. We are not going to break

if (widgets->actions().contains(myAction)) ...

based on wagers and assumptions.

We need to come up with a naming convention for getters that returns std::span so that we can add those APIs as alternatives. And perhaps we want symmetry between setters and getters working on spans, rather than making std::span setters overloads.


SBO: short buffer optimization
NOI…? Non-owning … interface?

Your arguments would be easier to follow with fewer acronyms ;)

> Even most Qt types don't have a need for CoW semantics of QList in their 
> implementation. The perceived need is coming solely from having to hand 
> out owning containers in the API, an operation we don't want to 
> deep-copy. So, catch-22.
> But imagine you /don't/ need to hand out owning containers from your 
> API, what container would you choose, then? Well, QRegion chose 
> variant<QRect, QVector<QRect>>, and I've repeatedly shown how that 
> caused users to have to use rectCount() and boundingRect() to avoid the 
> conversion of the first into the latter option.
> QVersionNumber uses SBO to store short version numbers inline, only 
> falling back to QList if the number isn't representable as a qint8[3]. 
> The implementation is truly horribly complex, and the API is more 
> limited (the user can't iterate over the segments) than it would be if 
> it had not used SBO.
> I'd wager that a lot more Qt types would rather benefit from a non-CoW 
> SBO container than benefit from the non-SBO CoW ones we currently have.
> And the use of QList in the  API is holding those types back from using the
> optimal data structure,  because QList is under compatibility constraints, so
> even where we _have_ an SBO container (e.g. std::string as a stand-in for
> QByteArray  or std::u16string for QString), we can't use it, because the API uses 
> owning Qt containers and we can't change them. Or we do, and then we 
> continue the vicious cycle of Qt container churn on each major release.
> NOI can break that cycle, so the Qt containers don't need to change, 
> keeping existing users happy, and both the implementations and users of 
> our API are free to choose whatever data structure best fits their 
> needs, making Qt developers and new users happy.
> Win-win.
> Thanks,
> Marc
> -- 
> 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
> _______________________________________________
> Development mailing list
> Development at qt-project.org
> https://lists.qt-project.org/listinfo/development

More information about the Development mailing list