[Development] How qAsConst and qExchange lead to qNN
Marc Mutz
marc.mutz at qt.io
Wed Nov 9 11:52:15 CET 2022
Hi Ulf,
On 09.11.22 08:18, Ulf Hermann via Development wrote:
>> I don't want to take the Qt containers away from Qt users. I want to get
>> rid of their use in our APIs, so that both the Qt implementation as well
>> as our users are free to choose the best container for their needs
>> instead of having to pick from one of the public Qt containers.
>
> I would like to know how that is supposed to work in practice. We have a
> lot of public API dealing with Qt containers all over. What are you
> going to do to, for example, to
>
> void addActions(const QList<QAction*> &actions);
>
> in qwidget.h? What should it look like when we're done and our users are
> free to choose the best container for their needs?
In this particular case, the API would be
void addActions(QSpan<QAction*> actions);
This is the trivial case: setters for contiguous data. It's
backwards-compatible, because QList implicitly converts to QSpan (and
std::span), but now, as a user, I can also pass a C array or std::vector
or QVLA of QAction*:
QAction *actions[] = {
new FooAction(this),
new BarAction(this),
};
addActions(actions);
This is the same effect we have with Q*StringView, incl. avoidance of
caller-side construction and destruction of owning containers.
This is NOIv1: https://www.youtube.com/watch?v=JUUID0_NvQI
We can also return spans:
QSpan<QAction*> actions() const;
but this only works, though, if we're returning a) stored and b)
contiguous data. If we return computed or non-contiguous data, instead,
we can, however, use coroutines:
std::g/QGenerator<QModelIndex> selectedIndexes() const;
This is NOIv2: https://www.youtube.com/watch?v=tvdwYwTyrig
The benefit here isn't only that the implementation data structure is
encapsulated, but also that, for computed values, the computation stops
when the consumer stops consuming.
The drawback is that the memory allocation of the coroutines' stack
frame is not elidable (crosses ABI boundaries), but when compared to
returning computed owning containers, you don't have more, and typically
have much less, memory allocations.
> Mind that I'm specially interested in this because I'm currently facing
> a similar problem, making different container types available in QML.
> QML has its own poor man's "range" type in the form of QQmlListProperty
> and it's terrible.
I don't know the QML landscape nearly as well as I should, but I would
invite you to consider the role coroutines can play in implementing
reactive programming environments such as QML and QProperty, even if we
cannot rely on them atm.
>> >> Q_FOREACH
>> > [I can make 100% correct predictions about changes I intent to push,
>> > too. What's the point?]
>>
>> I have no desire to touch the implementation of Q_FOREACH, ever. I did,
>> unwillingly, when its users suffered unnecessary pessimisations in the
>> past, but the port to C++20 ranged-for-with-init is not of that kind.
>
> Waiting for someone to push a patch with code you've already outlined
> and then approving it is pretty much the same as changing it yourself.
> You just don't need any approval for that ...
I can honestly say that I was surprised about the effect of the C++20
feature on the Q_FOREACH implementation. I was just predicting by
extrapolating from the last change to Q_FOREACH (using C++17
if-with-init), and wasn't aware of the consequences until after writing
down the implementation :)
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
More information about the Development
mailing list