[Development] Question about QCoreApplicationData::*_libpaths

Kevin Kofler kevin.kofler at chello.at
Sat Jan 16 00:44:22 CET 2016


Marc Mutz wrote:

> On Friday 15 January 2016 03:58:12 Kevin Kofler wrote:
>> So why not just add a QOptional that works the same as std::optional?
> 
> a) because we can't (we don't yet require the necessary language features)

A QOptional<T> that works with Qt's implicitly-shared data objects (such as 
QStringList, which is what it is wanted for here) only really needs this:
T data;
bool present;
(I guess that order will give the better memory layout than the opposite 
order.) If present is false, you just put a default-constructed T into the 
(ignored) data field, which will simply have a null d-pointer and thus cost 
you almost no time to construct. I don't see what further language features 
are needed for us.

> b) because once introduced, people will add all kinds of Qt-specific stuff
>   to it, making it impossible to replace it with std::optional down the
>   road. And since it will be good enough for the low demands of Qt
>   development, it will no longer see development and fall behind the state
>   of the art.

So what? Qt users are not forced to use it, and I'm sure several will, maybe 
BECAUSE of the "all kinds of Qt-specific stuff" that people actually find 
convenient.

> <rant>
> Consider QVector: it has been Qt-ifed by both adding (technically) useless
> duplicate Qt-ish API, CoW, and a Qt-specific type classification scheme
> plus corresponding optimisations that make it very hard to argue for
> std::vector use instead. The Qt community had two decades where they could
> have influenced the direction std::vector would take so it could allow the
> same optimisations that QVector has, but the time and energy was instead
> put into re-writing the containers for every major release (yes, for Qt 5,
> too, and Thiago's Qt 6 QVector again looks much different from the Qt 5
> one).

But the Qt-ish API and the CoW are exactly what makes the Qt containers NICE 
to use, unlike the STL. I have found myself more than once using QtCore in a 
project solely and explicitly for the container classes! They are what makes 
C++ a nice-to-use language.

> The CoW makes QVector slow and increase code size, leads to hidden
> detaches that not even Qt experts regularly avoid in their daily work, and

Well, I am well aware of the issue, and know to use e.g.:
static_cast<const QVector &>(vec).first()
when needed. (I guess this is actually more efficient than the popular 
.at(0) workaround. It is definitely not less efficient.) But normally I will 
just always operate on const data structures (usually const references) when 
I'm not writing to them, so I don't have to cast anything. (And of course, 
when I'm writing to them, chances are the detach is exactly what I want or 
need.)

> doesn't even work properly because you can take iterators into a QVector
> and, after copying the vector, change both copies through the iterator
> into the first. That is a conscious trade-off because making the container
> unsharable, as it must become once it hands out iterators, would make CoW
> fail to take effect _all the time_. But it leads to careless copying of
> QVectors that also doesn't really help with porting to std::vector.

So don't use iterators into QVector, use indexes, it's a random-access 
container. The non-const operator[] that you use then DOES detach when 
needed. (And of course, for read-only iteration, foreach works great.)

The only container type where I found iterators to be truly useful is maps 
(QMap, QHash, etc.), where the iterator can give me both key and value at 
once and saves me the key lookup.

> All the while - and I don't believe I'm saying this - std::vector *blazes
> the trail* with move semantics, emplace_back and stateful allocators
> (making QVarLengthArray all but useless). Does QVector use move semantics?
> No.

Move semantics are mainly an ugly way to avoid copies if you don't have CoW. 
With CoW data structures, all you save through move semantics is the 
reference counting. And move semantics make it easy to shoot yourself in the 
foot. (Either you leave behind an invitation for a use-after-free bug, or 
you end up swapping instead of assigning, which is also a pessimization.)

> Does QVector have emplace_back? No.

Just like the move semantics, this is also an ugly and complicated way to 
avoid a copy if you don't have CoW or merely save the reference counting if 
you do.

> Does QVector have an allocator, even one that, citing Alexandrescu,
> actually deals with allocation? No.

For the average programmer, an allocator is just an obscure thing that shows 
up as a template parameter in all the error messages making them ugly and 
unreadable.

The fact that Qt containers only have the template arguments that are 
intuitively templated on (e.g., QVector has only the contained type as a 
template parameter) is really a feature, not a bug.

> Does QVector, even with Q_PRIMITIVE_TYPE payloads, expand to less code as
> std::vector? No: https://codereview.qt-project.org/145587 (comment from
> 01-14 11:21).

So there is room for improvement there. But in the end, this is not going to 
be the deciding factor for which implementation to pick for most 
programmers. And in the end, you need to compare QVector with class 
libraries in programming languages that offer comparable convenience, not 
with the C++ STL.

If you try to force programmers to use the STL, chances are they will rather 
just switch to some other programming language that offers the semantics 
they expect.

> Does QVector have a debug mode comparable to that of std::vector
> implementations? Nope.

I never had any need for that.

> Or: The only reason I ever used std::list was to use its splice() feature.
> Does QLinkedList have splice()? No, of course not. Because it _cannot_
> (it's CoWed: how do you take ownership of nodes if the they are shared? By
> copying all other nodes in a detach()?).

Then use std::list if it suits your use case better. Just don't force 
everyone else to use it.

Personally, I also don't normally have a use for QLinkedList, QList suits my 
needs just fine. :-p (In fact, QList was changed from the linked list it was 
in Qt 3 to an array of pointers in Qt 4 exactly BECAUSE that's the more 
efficient data structure in most practical applications.)

And I simply find the Qt containers to be much nicer to work with as a 
whole. In fact, I recently had to use std::priority_queue for a (QtCore-
based) project of mine, a container class that sadly is not implemented in 
Qt (*). After attempting to use it directly and cursing about it, I ended up 
implementing a Qt-style wrapper around it:
* My data class simply multiple-inherits from public QSharedData,
  public std::priority_queue<T>.
* My public class contains a QSharedDataPointer of the above, renames the
  methods to names matching QQueue, and in particular adds a dequeue()
  method that does both top() and pop(). Null objects (null d-pointer) are
  also handled gracefully (the const methods fake an empty queue, the
  enqueue (= STL push) method allocates the d-pointer).
So, a few lines of boilerplate, and suddenly the API becomes usable, and CoW 
just works. Incidentally, the CoW was also the only way I found to avoid a 
copy of the whole queue while initializing while keeping C++98 compatibility 
(so no move or swap):
* I need to initialize my priority_queue from a list (actually a QList :-)
  but I would have the same issue with QVector or even std::vector).
* There is no push method that takes a whole list of items.
* So I can only:
  - either push every item one at a time, which sorts them less efficiently
    than a bulk insert,
  - or use the constructor that takes iterators, which then leaves me with a
    whole std::priority_queue to copy. My CoW wrapper avoids that copy.
* And my API wrapper actually accepts the QList (or actually any list type
  with constBegin() and constEnd() methods) directly instead of requiring
  iterators.

(*) I did find one third-party QPriorityQueue class, but it was just a
    wrapper around std::priority_queue that was neither CoW nor had API
    consistency with QQueue, so it was better to implement my own.

> This is why we need to stop duplicating std API. It's not Qt's core
> competency to meddle with things we only have a half-interest in. It's fun
> to write QOptional. Until it isn't anymore. And then the (Qt) world needs
> to live with the poor-man's std::optional for the next few decades.

I think having more Qt containers would actually be a good thing, not 
something to be scared of.

        Kevin Kofler




More information about the Development mailing list