[Interest] Set manipulation in Qt 6

Giuseppe D'Angelo giuseppe.dangelo at kdab.com
Sat Jun 20 12:34:11 CEST 2020


Hi,

Il 20/06/20 08:44, Vadim Peretokin ha scritto:
> QMap<QString, QList<QPointF>> customLines;
> QMap<QString, QColor> customLinesColor;
> // ...
> 
> #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
>      auto customLineKeys = customLines.keys();
>      QSet<QString> missingKeys{customLineKeys.begin(), 
> customLineKeys.end()};
>      if (!customLinesColor.isEmpty()) {
>          auto customLinesColorKeys = customLinesColor.keys();
>          QSet<QString> 
> customLinesColorKeysSet{customLinesColorKeys.begin(), 
> customLinesColorKeys.end()};
>          missingKeys.subtract(customLinesColorKeysSet);
>      }
> #else
>      QSet<QString> 
> missingKeys{customLines.keys().toSet().subtract(customLinesColor.keys().toSet())};
> #endif


With my hat of the guy going around and deprecating toSet() and friends: 
the rationale for these deprecations is the terrible code that those 
methods encourage, and the


Let's just look at this last line. This is, at a minimum:

> customLines.keys()

- 1 temporary allocation + #LINES temporary copies (builds a QList 
copying all the keys from the map)

> .toSet()

- #LINES+1 allocations + #LINES copies (builds a QHash copying all the 
values from the list)

> customLinesColor.keys().toSet()

- 1+1+#COLOR temporary allocations, 2*#COLOR temporary copies (same, but 
this is completely temporary)


Totals:

- 4 + #LINES + #COLOR allocations
- 2 * #LINES + 2 * #COLOR copies
- 3 + #LINES + 2 * #COLOR destructions at a minimum
- 3 + #COLOR deallocations at a minimum

(Luckily the keys are QStrings, and not something where the copy and 
destruction would be even more expensive, or QList would cause further 
allocations).



There are much better alternatives you can aim for with minimal effort.
Assuming that at the end you do want exactly a QSet, you can port it in 
the slightly more verbose but still straightforward:

> QSet<QString> customLinesKeys(customLines.keyBegin(), customLines.keyEnd());
> {
> QSet<QString> customLinesColorKeys(customLinesColor.keyBegin(), customLinesColor.keyEnd());
> customLinesKeys.subtract(customLinesColorKeys);
> }

Which saves buildings the temporary QLists. Totals now:

- 2 + #LINES + #COLOR allocations
- #LINES + #COLOR copies
- 1 + #COLOR destructions at a minimum
- 1 + #COLOR deallocations at a minimum



I think it's possible to do even better -- a QMap, by definition, keeps 
its keys in order. Which means now you can use e.g. std::set_difference.

Unfortunately we can't use a QSet as an output -- let's sketch with a 
std::unordered_set for the moment:

> std::unordered_set<QString> result;
> std::set_difference(customLines.keyBegin(), customLines.keyEnd(),
>                     customLinesColor.keyBegin(), customLinesColor.keyEnd(),
>                     std::inserter(result, result.end()));

Cost of this one:

- 1 + (#LINES / #COLOR) allocations
- (#LINES / #COLOR) copies

AKA optimal.

With a QSet, the cost and the code would be identical. The problem is 
that we can't write _that_ code as QSet is not usable with std::inserter 
(QSet lacks a suitable insert() method). Then again, do you _need_ a 
QSet or would a QList output suffice?

(Which brings me to my second crusade, try stop encouraging the usage of 
Qt containers, as their API is full of holes and doesn't play nice with 
algorithms or ranges. But it's enough for this mail.)

HTH,
-- 
Giuseppe D'Angelo | giuseppe.dangelo at kdab.com | Senior Software Engineer
KDAB (France) S.A.S., a KDAB Group company
Tel. France +33 (0)4 90 84 08 53, http://www.kdab.com
KDAB - The Qt, C++ and OpenGL Experts

-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/pkcs7-signature
Size: 4329 bytes
Desc: Firma crittografica S/MIME
URL: <http://lists.qt-project.org/pipermail/interest/attachments/20200620/f1e91780/attachment.bin>


More information about the Interest mailing list