[Development] Views (was: Re: QList for Qt 6)

Mutz, Marc marc at kdab.com
Thu May 16 17:26:08 CEST 2019


On 2019-05-16 16:55, Thiago Macieira wrote:
> On Thursday, 16 May 2019 06:05:37 PDT Mutz, Marc via Development wrote:
>> Going forward, we should be looking into removing more and more owning
>> containers from the interface and replace them with views. The 
>> standard
>> is working on a solution for the stale reference problem, and by the
>> time Qt 7 comes around, it will be hopefully widely available.
> 
> That goes against the Qt API design. Do not return views, since it 
> implies
> that internally there is an object that produces that view and that it 
> does
> not go out of scope. The reason we return full, owning containers is so 
> we
> have the freedom inside to change how we store the data without that 
> affecting
> our API.

I believe the opposite to be true: I believe owning container use in the 
API to break a class' encapsulation:

If you return QVector<>, which choices do you have for the internal data 
structure? A QVector.
If you return a view, you can use a std::vector, a QVector, a C array 
internally, even switch at runtime (like QRegion does for the case of a 
single rect). So views allow the implementation to vary its internal 
data storage strategy while returning owning containers does not.

And no, you _can_ write views for node-based containers, too. Cf. how 
QVariant is (indirectly) iterable these days.

Sure, you'd not return a view to data that the class in question does 
not already own, though with coroutines you could return a generator 
instead. This is about the data that is already stored in the object. 
This is about QRegion::rects() and QGradient::stops(). This is also 
about QAIM::roleNames(). All these APIs lock down the implementation to 
a particular data store, thus _reducing_ the implementation's freedom to 
adjust it's internal workings. And by Hyrum's Law, they also make users 
depend on idiosyncrasies of the implementation, as witnessed by the 
pre-QRegion::begin/end() anti-pattern of abusing boundingRect() when 
rectCount() == 1 in order to avoid the building of a QVector just to 
store one rectangle, a special-casing I could remove in 100% of cases 
once QRegion became iterable (= a view on it's rects property).

All this shows, in my interpretation, quite clearly that owning 
container use in APIs limits both the freedom of users of the API as 
well as its implementation.

That said, there _is_, of course, a case to be made for setting or 
returning an owning container, but only by way of move. This, however, 
ties the implementation to that particular container, and should thus be 
done, like all encapsulation-breaking techniques, only to get that last 
bit of performance out.

Thanks,
Marc


More information about the Development mailing list