I have read the following post titled "I ❤️ Qt containers! :)" on the Qt Development mailing list and found it extremely interesting. It is written by Volker Hilsheimer, the current chief maintainer of the Qt project, and elaborates on some design principles Qt libraries are using. The post was was written as a response to more general question of why isn't Qt switching to standard C++ library containers.
I hope nobody gets offended that I repost it here - I do it because not everyone can read the archives. I'd also like to be able to find it for myself in case I'd like to ponder on Qt containers and their design.
So happy reading (emphasizings and formattings are all mine):
" .... Historically, STL implementations were unusable and unreliable for cross
platform development (we supported HP-UX, AIX, SGI, Sun back in those days),
and generally incomplete (only a few associative containers pre-C++11). So, 30
or 20 years ago, or perhaps up to C++11 and until we could drop commercial Unix
systems as irrelevant (esp for Nokia’s plans; although no idea about the
quality of the STL for Symbian C++), the STL wasn’t really much of an option.
However, this is a more fundamental question about what we try to achieve with
Qt. Qt has never tried to be a C++ library that follows the design principals
of the std library. In many cases, we don’t even care that much about the
single responsibility principle (hello, QWidget). Qt container classes have
always been more than a dumb storage of data on top of which algorithms
operate. QString is a very rich class with tons of functionality, specific to
text handling. std::string is a sequence of characters. Working with QString vs
std::string to deal with user-provided text input requires rather completely
different mindsets.
Our core competence of designing intuitive APIs does not exclude container
classes. That’s why with Qt containers, we can write
1 2 3 | if (list.contains(value)) {
}
|
rather than
1 2 3 | if (std::find(list.begin(), list.end(), value) != list.end()) {
}
|
Perhaps it makes me an inferior C++ developer, but I rather prefer the former.
Well, std::map got a contains(), and std::string a starts_with() in C++ 20, only
25 years late(r).
Indeed, sometimes that convenience means that our users, and we ourselves, can
do something silly like:
1 2 3 4 | if (map.contains(key)) {
value = map.value(key);
}
|
Convenience is no excuse for us as developers of Qt to be sloppy. It is also no
excuse for ignoring the new features we get into our toolbox when we move to
C++ 20 or 23. But that C++ 20 finally introduces std::map::contains (but not
std::vector::contains…), or adds std::span, is also no excuse for us to toss
our own designs out of the window, and to demand that all Qt users must embrace
the STL instead.
One of Qt’s goals has always been to make C++ accessible for people that come
from other languages, with a programming model that is not rooted in how the
C++ standard library does things. That programming language used to be Java -
hence our Java-style iterators in Qt containers. Today, people perhaps rather
learn programming with Python in school. There will always be more people that
have learned programming with other languages, than those that have carefully
studied the C++ standard and the impact of various constructs in Compiler
Explorer. We must make it easy for the former, while also enabling the latter.
And there are the practical reasons why I don’t want to replace QList with
std::vector and QHash with std::unordered_map: we store our data structures in
the d-pointer, and we want to stay flexbile wrt the actual stored type. So
copy-elision and return-value-optimization don't buy us much: we need to return
copies of containers from our property getters. Not const references to
containers, and not temporary lists that can be moved out. So we do need
reference counting.
For the here and now, and the last 25 years of Qt and C++, it’s not helpful to
argue that we will soon be able to return a type-erased span and get rid of
“horrible and inefficient” APIs returning owning containers. std::span is a new
tool, opening up new opportunities; the expressiveness of e.g. C++ ranges might
even make it much easier for someone coming from e.g. Python to use Qt, while
allowing us to write much more efficient code. So we do need to consider how we
name and design our APIs today so that we can add new APIs to unlock that power
in the future. And we need to keep looking for ways to improve what we have -
with extra awareness of what potential changes mean for our users and
co-contributors.
Those improvements cannot require that we force everyone to change significant
amounts of existing code, all the time; or that they have to regularly unlearn
established Qt patterns; or that they have to live without the convenience.
Yes, I’m biased, but I honestly don’t see any universe where a Qt without our
implicitly shared, owning, old-fashioned containers, and instead with only STL
containers and programming paradigms, would have been as easy, or as much fun,
to use.
Volker"
Summing up
As it was already said elsewhere, "Qt is known for making C++ easy and accessible"! You might say it's C++ for the people. Maybe it's for that reason that C++ didn't go the way of Haskell despite Commitee's best efforts? 😏
 |
A screenshot (I forgot from which presentation...) |
PS: I didn't remove anything from the original post for the sake of completeness
No comments:
Post a Comment