0

I have a multimap with QVariant as key, but it's not working with QByteArray.
The funcion map.values("\xc2\x39\xc7\xe1") is returning all the values of the map.
This is a small example:

#include <QCoreApplication>
#include <QMultiMap>
#include <QVariant>

int main(int argc, char *argv[])
{
  QCoreApplication a(argc, argv);

  QMultiMap<QVariant, QString> map;
  QByteArray b1("\xc1\x39\xc7\xe1");
  QByteArray b2("\xc1\x39\xc7\xe2");

  map.insert(QVariant(b1), "TEST1");
  map.insert(QVariant(b2), "TEST2");

  QStringList values = map.values(QByteArray("\xc1\x39\xc7\xe1"));

  return a.exec();
}

I tried also using a QMap to see what happens and it adds only an element to the map.
Can someone explain me this behavior?
What am I doing wrong?

Fausto01
  • 171
  • 14
  • Please include a [mre]. The example you've given shouldn't even compile. – Passerby Dec 02 '21 at 10:18
  • Simply insert the code in a main. Anyway I edited the question. – Fausto01 Dec 02 '21 at 10:24
  • And this compiles for you? Which version of Qt are you using? – Passerby Dec 02 '21 at 10:28
  • Qt 5.14.2, you need to create the project from QtCreator. If you need it, I'll post also the project file, so you can open it directly – Fausto01 Dec 02 '21 at 10:31
  • My Qt 6.2.1 installation complains about not having an `operator<(const QVariant, const QVariant)`... – Botje Dec 02 '21 at 10:32
  • 1
    `QVariant::operator<` has been deprecated for a *long* time and doesn't even appear in [current `Qt5` documentation](https://doc.qt.io/qt-5/qvariant.html). – G.M. Dec 02 '21 at 10:33
  • Unfortunately I cannot upgrate the Qt version for this project, do you no if there's a solution or simply I cannot use QVariant in QMultiMap? – Fausto01 Dec 02 '21 at 10:36

2 Answers2

1

The problem is the unavailability of a suitable operator<. You can use this hack to show the desired behaviour:

bool operator<(const QVariant& lhs, const QVariant& rhs)
{
    if (lhs.userType() == QMetaType::QByteArray && rhs.userType() == QMetaType::QByteArray)
    {
        return lhs.toByteArray() < rhs.toByteArray();
    }
    // The rest is up to you.
    return true;
}
Passerby
  • 808
  • 1
  • 5
  • 9
  • I think I found another solution, if I insert the QByteArray with toHex() it works. I am not understanding why, the toHex() function returns another QByteArray. Maybe because the returned QByteArray is printable? – Fausto01 Dec 02 '21 at 10:42
  • 1
    The deprecated `operator<` on QVariant calls `Qvariant::compare`, which [ends up comparing `toString()` of both QVariants](https://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/kernel/qvariant.cpp?h=5.12.12#n4111), at which point I lost track/interest. – Botje Dec 02 '21 at 10:54
1

It appears to be a bug in Qt, because the operator QVariant::operator<() does not provide a total ordering, even though QByteArray::operator<() does. And QMap relies on that (see QMap documentation).

QByteArray b1("\xc1\x39\xc7\xe1");
QByteArray b2("\xc1\x39\xc7\xe2");
QVariant v1(b1);
QVariant v2(b2);

assert(b1 < b2 != b2 < b1);  // works as expected for QByteArray
assert(v1 != v2);            // make sure the values are actually not equal
assert(v1 < v2 != v2 < v1);  // fails for QVariant(QByteArray)

So a QByteArray works as a key to a QMap, but a QVariant(QByteArray) does not.

SebDieBln
  • 3,303
  • 1
  • 7
  • 21