Showing posts with label qt. Show all posts
Showing posts with label qt. Show all posts

Dec 7, 2017

[CPPCON2017] [Qt] Effective Qt (2017)

Effective Qt

CppCon 2017: Giuseppe D'Angelo “Effective Qt (2017 edition)”

  • Understand the Qt containers
    • Don't use the Qt containers (unless you have to)
    • Qt containers are not actively being developed
    • Datatypes held in Qt containers must be default constructible and copiable
    • No exception safety guarantees
    • Most C++11 APIs still missing
    • All post-C++11 APIs missing
    • No flexibility w.r.t. allocation, comparison, hashing, etc.
    • Use Qt containers if there isn't a STL / Boost equivalent (unlikely)
    • Use Qt containers when interfacing with Qt and Qt-based libraries
    • using the Qt containers, rather than converting back/forth
  • Every time you define a type that you may end up using in a Qt container, remember to declare its typeinfo Q_DECLARE_TYPEINFO.
    • Adding a trait “after the fact” is possible, but it's a potential ABI break
    • Type traits for Qt containers
      • Qt uses type traits to optimize handling of data types in its own containers
      • The most important optimization is:
        • when growing an array of objects, is it OK to use realloc?
          • Safe to do iff the type is relocatable
          • Huge optimization gain over allocating a new buffer; moving elements; deallocating the old buffer
          • Many types are relocatable and could benefit from this optimization
            • E.g. most Qt value classes, thanks to pimpl
    • Relocatability:
      • The compiler cannot tell whether a type is relocatable or not
      • Type authors must annotate relocatable types by using type traits
      • Some libraries let authors add these traits:
        • Qt → Q_DECLARE_TYPEINFO
          • Q_PRIMITIVE_TYPE
          • Q_MOVABLE_TYPE
          • Q_COMPLEX_TYPE
        • EASTL → EASTL_DECLARE_TRIVIAL_RELOCATE
        • STL → *crickets*
      • if a type has pimpl enabled, but, the pimpl has a pointer point back
        to the type instance itself, it's _not_ movable.
        Reason? Since the instance has been moved, the pointer pointing back
        is garbage.
      • A type has pointer pointing to ifself(this type) is not relocatable.
      • Beware, any pointer inside the type that is consider to be relocated should
        consider will it point to a valid address after type instance being relocated.
  • Understand implicit sharing, and be careful about hidden detaches
    • Implicit sharing: a double-edged sword
    • STL is NOT using this, actually, forbidding it.
    • the Qt way of ref counting.
    • COW
    • A Qt value class implementation is typically just a pointer to a pimpl, which contains the reference counter and the actual payload
    • Reference counter is manipulated during an object's lifetime
      • On object creation: refcount is 1
      • Copying an object: refcount is incremented by 1
      • Destroying an object: refcount is decremented by 1; if it reaches zero, deallocate the pimpl
      • Calling a const member function: (nothing)
      • Calling a non-const member function: if the refcount is greater than 1, then detach (= deep copy the the payload)
    • where's the catch?
      • Handing out references to data inside a container does not make the container unshareable
      • It's easy to accidentally detach a container
      • Accidental detaching can hide bugs
      • Code polluted by (out-of-line) detach/destructor calls
      • 小心不要將container內的data記憶體位置傳出。
        直接Update 其 data 不會trigger COW!!! 會造成其他
        Reference到此container instance產生錯誤,以為該位置的data
        仍是舊的data.
        Update data? Through container member function!
    • Accidental detaches - 1
      • “Innocent” code may hide unwanted detaches:
        • QVector<int> calculateSomething();
          const int firstResult = calculateSomething().first();
        • Calls: T& QVector<T>::first();
          • Non-const, may detach and deep copy!
        • Solution is easy: call constFirst()
    • Accidental detaches - 2
      • QMap<int, int> map;
        // ...
        if (map.find(key) == map.cend()) {
        std::cout << "not found" << std::endl;
        } else {
        std::cout << "found" << std::endl;
        }
      • find(key) might detach after the call to cend(), returning an iterator pointing to a “different” end
      • “found” is printed, even if the key isn't in the container
      • Solution: use constFind(key), don't mix iterators and const_iterators
  • Never use Qt's foreach / Q_FOREACH;
    use C++11's range-based for. (Be careful with Qt containers.)
    • this actually applies to boost foreach as well...
    • Disable its usage in your code base by defining QT_NO_FOREACH
    • It will extremely likely be removed in Qt 6
    • If you are not mutating the container, make the container const – 
  • Run clazy on your code base, and fix its warnings
  • Understand Qt string classes. Embrace QStringView.
    • There hasn't been much development around QString / QByteArray in the last few years
    • The only important change that happened is that since Qt 5.9 QStringLiteral / QByteArrayLiteral never allocate memory
    • Ways to create string in Qt:
      • “string”
      • QByteArray(“string”)
        • A sequence of bytes
        • No encoding specified – Akin to std::string
        • Implictly shared
        • Its constructors allocate memory – QByteArray::fromRawData() to avoid (some) allocation
        • QByteArrayLiteral(“string”) never allocates – Since Qt 5.9, this is true on all supported platforms
        • Use it to store byte arrays (i.e. data)
      • QByteArrayLiteral(“string”)
      • QString(“string”)
        • A UTF-16 encoded Unicode string – Support for Unicode-aware manipulations, unlike std::u16string 
        • Implictly shared
        • Its constructors allocate memory – Including QString::fromUtf8(), QString::fromLatin1()
        • Clutch: QString::fromRawData() as non-allocating constructor – Prefer QStringView
        • QStringLiteral(“string”) never allocates – Since Qt 5.9, this is true on all supported platforms – Data is stored UTF-16 encoded in the readonly data segment
        • Use it to store Unicode strings
      • QLatin1String(“string”)
        • A literal type that wraps a const char * and a size – It doesn't manage anything
        • Mostly used in overloads when there's a fast-path implementation possible for Latin-1 strings, and they come from string literals:
        • E.g. substring search:
          int QString::startsWith(const QString &substring);
          int QString::startsWith(QLatin1String substring);
          QString str = "...";
          if (str.startsWith(QString("foo"))) // allocates a temp. QString
             doSomething();
          if (str.startsWith(QLatin1String("foo"))) // does not allocate + uses
          doSomething();  // optimized implementation
      • QStringLiteral(“string”)
      • QString::fromLatin1(“string”)
      • QString::fromUtf8(“string”) 
      • tr(“string”)
      • QStringView(u”string”)
        • New in Qt 5.10
        • as an interface type
        • The primary use case for QStringView is for functions parameters
          • If a function needs a Unicode string, and it doesn't store it, use QStringView
        • Unicode safe
        • Never allocates
        • Can be built from a wide variety of sources
        • as an alloc-free tokenizer
          • To extract substrings, without allocating memory
          • QString str = "...";
            QRegularExpression re("reg(.*)ex");
            QRegularExpressionMatch match = re.match(str);
            if (match.hasMatch()) {
              QStringView cap = match.capturedView(1); // no allocations
            // ...
            }
        • A non-owning view over a UTF-16 encoded string:
          • QString
          • QStringView
          • std::u16string
          • Array and std::basic_string of QChar, ushort, char16_t, wchar_t (on Windows)
        • Literal type; akin to C++17's std::u16string_view
        • Offers the majority of the const QString APIs, without the need of constructing a QString first
          • More APIs, QStringBuilder support etc. expected in 5.11
  • Prefer the Standard Library ones.
  • Qt containers was there for reasons:
    • Qt needed to work on platforms without a STL
    • Qt didn't want to expose Standard Library symbols from its ABI
    • Qt containers used in Qt APIs, and available for applications
  • Qt containers use camelCase
    • STL use snake_case

Linear containers

  • QVector std::vector
  • QList -
    • array-backed list
      • Not a linked list
    • Terribly inefficient if the the object stored are bigger than a pointer
      •  Allocates every individual object on the heap
    • Avoid using it (unless you have to)
    • use QVector instead
    • might simply become a typedef for QVector, and a new type (QArrayList?) introduced in Qt6
  • QLinkedList std::list
  • - std::forward_list
  • QVarLengthArray -
    • preallocates space for a given number of objects
    • avoid hitting the heap
    • a vector with SSO
    • Similar: Boost's small_vector
  • - std::deque
  • - std::array

associative containers

  • QMap std::map
  • QMultiMap std::multimap
  • QHash std::unordered_map
  • QMultiHash std::unordered_multimap
  • - std::set
  • - std::multiset
  • QSet std::unordered_set
  • - std::unordered_multiset

Relocable means:

  • Relocability is independent from being POD
  • Relocatable types may have non-trivial constructors/destructors
    • E.g. Qt pimpl'd value classes
  • A trivial type may not be relocatable
    • E.g. if the address of an object is its identity
    • All C data types are trivial, but non necessarily relocatable

Dont' use deprecated Qt APIs

  • Always define QT_DEPRECATED_WARNINGS
    • Makes the compiler emit warnings if using deprecated APIs
  • Define QT_DISABLE_DEPRECATED_BEFORE to the version of Qt you develop
    against
    • Turns usage of deprecated APIs into hard errors, iff they have been deprecated in that Qt version or in a earlier one
    • No “new” errors if you upgrade Qt
      • E.g. in qmake:
        DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x050900

Never use QList for your own code

May 23, 2015

[QT] QThread

How to use QThread in the right way (Part 1)
How to use QThread in the right way (Part 2)


Even though this seems to work, it’s confusing, and not how QThread was designed to be used.
(all of the functions in QThread were written and intended to be called from the creating thread, not the thread that QThread starts)