[Development] Views

Marco Bubke Marco.Bubke at qt.io
Fri May 17 18:38:05 CEST 2019


Thiago, you partially implying that BC is still needed but with technologies like flatpak or snappy this will maybe not common use case anymore. They provide even behaviour compatibility if you stay with the same runtime. Something which is not provided by binary compability. I personally think if the behaviour is changed it should not even compile anymore so you can fix it easily.

On May 17, 2019 17:08:28 Thiago Macieira <thiago.macieira at intel.com> wrote:

> On Thursday, 16 May 2019 11:18:08 PDT Mutz, Marc via Development wrote:
>> > When you first design the class, sure. But 5 years later, you may have
>> > the
>> > data internally kept in a QMap or QHash, mapped to some other
>> > information. So
>> > your function that used to "return d->member;" now does
>> > "return d->member.keys();"
>>
>> Can you point out a Qt class where this was the case in the past?
>
> I'll have to look it up, but it has happened in the past and if not in Qt,
> then in one of the KDE libraries. The Library API Design Policy includes this
> particular point (don't return references) for a reason.
>
>> > Another case would be where you're keeping extra data in the internal
>> > structure and you need to filter that out before returning. Or the
>> > dual:
>> > augment with some implied data. The latter could be quite common if the
>> > class
>> > is not storing anything in the regular case, but synthesising it on
>> > demand for
>> > the benefit of the old API.
>>
>> This one is simple: Array of struct -> struct of arrays. Well-known
>> optimisation in the games industry.
>
> Sure, but if you returned a view of structs, how would you later make that
> view of structs work with your internally-stored struct of arrays?
>
>> > But only so long as each of those containers store lay the data out the
>> > same
>> > way in memory, which is not the case for my QMap example.
>>
>> You basically have a dichotomy: Either you have contiguous storage, in
>> which case you return pointers, or you have non-contiguous storage, in
>> which case you need some form of runtime-dispatch Iterator Pattern.
>
> Or you have a well-known storage pattern, in which case you don't return an
> array of pointers (which would need to be allocated anyway), but you return a
> means of finding those elements. That's what the containers do for you.
>
> So I don't have a problem returning a type that is cheap when you're designing
> the API, so long as you don't lock yourself into it if you later want to
> change. I don't see anything but actual containers here.
>
>> I don't think you'd ever come into a situation where you'd need to
>> switch from one to the other. If you think so, please provide an example
>> where this was necessary in the past.
>
> I'll look it up. Note that in hindsight all of those will look like bad
> design, but the point is that we didn't know any better when the design was
> first done.
>
>> > Two of your examples [QRegion, QGradient] basically return an internal
>> > structure, so I'm not seeing
>> > how they are relevant.
>>
>> How are they not relevant? Because the class basically _is_ a container?
>> Well, then see QAIM::roleNames(). Apart from the questionable design to
>> return a node-based associative container for the O(10) elements in
>> there, instead of a (view to a) sorted array of structs[1], it is a
>> prime example of how the API requires a particular implementation. I
>> don't remember whether it returns a QMap or a QHash, but either way, one
>> might want to return the other, down the road - or even depending on the
>> number of elements in the container, like QRegion does.
>
> I meant they are not good examples because they always return something that
> currently is stored in the d pointer. So there's no problem in changing them
> to return a view to that storage, since that storage does not go out of scope
> at function exit.
>
>> For an example of where roleNames() goes horribly wrong, see
>> QQmlListModel. Is has the data stored elsewhere and re-constructs a
>> QHash each time it's called. With a runtime iterator, it could probably
>> produce the values on the fly, and with a sorted array of {role, name}
>> it would allocate one block of memory per call, not O(n).
>
> I don't understand what you meant here. Are you advocating that the return
> type should have been different? What type would have helped?
>
> Or are you saying that if we had different tools internally, the
> implementation of QQmlListModel::roleNames could have been better?
>
> Either way, this is a very good example for why the return type mustn't be a
> reference or a non-owning container: the two sources for the function are not
> compatible with the return type.
>
>> > How would you implement this one with a view-based return?
>>
>> Glad you asked:
>>
>>      static constexpr QGradientStop defaultStops[] = {{0,
>> QColorLiterals::black}, {1, QColorLiterals::white}};
>>      return SomeView{std::begin(defaultStops), std::end(defaultStops)};
>>
>> Instead of the simple solution shown here, what we'll more likely see is
>> QVector::fromRawData(). Which is trying to retrofit a view onto an
>> owning container. Yuck.
>
> It's a price we pay for flexibility. No doubt that a view is much cheaper to
> constructor, pass and destruct. But it's also more limited.
>
> --
> Thiago Macieira - thiago.macieira (AT) intel.com
>  Software Architect - Intel System Software Products
>
>
>
> _______________________________________________
> Development mailing list
> Development at qt-project.org
> https://lists.qt-project.org/listinfo/development




More information about the Development mailing list