[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