1

This code sample works as I would expect:

v = QVariant("123456");
qDebug() << v; // QVariant(QString, "123456")
v.convert(QVariant::Int);
qDebug() << v; // QVariant(int, 123456)
v.convert(QVariant::String);
qDebug() << v; // QVariant(QString, "123456")

Where as this doesn't:

v = QVariant("0xBEEF");
qDebug() << v; // QVariant(QString, "0xBEEF")
v.convert(QVariant::Int);
qDebug() << v; // QVariant(int, 0)
v.convert(QVariant::String);
qDebug() << v; // QVariant(QString, "")

Is there any simple way to make the QVariant class work with hexadecimal strings so the second example would work something like this:

v = QVariant("0xBEEF");
qDebug() << v; // QVariant(QString, "0xBEEF")
v.convert(QVariant::Int);
qDebug() << v; // QVariant(int, 48879)
v.convert(QVariant::String);
qDebug() << v; // QVariant(QString, "48879")
Samuel Harmer
  • 4,264
  • 5
  • 33
  • 67

2 Answers2

2

When converting a qvariant to a numeric value you need to specify the base. Also make sure the value will fit in the int/long/longlong or then the conversion will fail for that reason instead.

QVariant v = QVariant("0xDEADBEAF");
bool test;
qDebug() << v.toString().toLongLong(&test,16);
qDebug() << test;

will produce the correct output

3735928495 
true 

while

QVariant v = QVariant("0xDEADBEAF");
bool test;
qDebug() << v.toString().toInt(&test,16);
qDebug() << test;

will output

0
false
Kimmeh
  • 991
  • 7
  • 21
  • Good point, and I've updated my question to reflect a more sensible example. However I think your reasoning for failure is incorrect. "0xBEEF" converts to 0 rather than 48879 which wouldn't overflow. Having said that, I would be happy to accept your answer if you remove the incorrect bits and update it to match the amended question, as it is a good solution, thank you. :) – Samuel Harmer Feb 09 '12 at 12:07
  • Yes and no. In the case of QVariant::convert() it fails first because it doesn't know which base to convert from. That happens in the "0xBEEF" case aswell. But if you specify the base, it then fails because it cannot convert "0xDEADBEEF" to a legal int due to overflow. Thus QVariant("OxBEEF").toString.toInt(&test,16) will succeed while the QVariant("OxDEADBEEF").toString.toInt(&test,16) will not. I'll update my answer. – Kimmeh Feb 09 '12 at 12:37
  • Correct, but the QVariant conversion routine for a number stored as a string makes an attempt based on the characters contained in the string (i.e. `"1" == 1`, but not in `"m1stake"`). I was hoping to make it do the same kind of attempt for a hex string stored in C++ notation (i.e. `"a" == 10` in `"0xa"` but not in `"di0xan"`). QVariant doesn't seem to bother making any such attempt (based on the outcome of my `"0xBEEF"` example). – Samuel Harmer Feb 09 '12 at 12:55
  • I think it just assumes its a base 10 number and fails if its not. The default for QString::toInt() is base 10, and i'm guessing thats the function that QVariant::convert() calls to do the conversion. – Kimmeh Feb 09 '12 at 13:38
  • Not so. *[And I'll tell you for why!](http://stackoverflow.com/a/9212227/594137)* And thanks again for both answering my question **and** making me find that bug in QVariant :) – Samuel Harmer Feb 09 '12 at 14:02
1

After the points made by this answer about numerical overflow, I did some investigation and it seems the QVariant's conversion routines are flawed. The easiest way way to convert a QVariant safely is by using QString's conversions instead. This also seems like the easiest way to use QVariant with hexadecimal strings. I've summarised my findings below. The comments would be the resulting output of making a call to qDebug() << v; at that point in the code.

Scenario 1: Number stored as a hexadecimal string

v = QVariant("0xBEEF"); // 'QVariant(QString, "0xBEEF") '
v = v.toString().toInt(0, 16); // 'QVariant(int, 48879) '

v = QVariant("0xDEADBEEF"); // 'QVariant(QString, "0xDEADBEEF") '
v = v.toString().toInt(0, 16); // 'QVariant(int, 0) '

The easiest and safest way of converting a string-which-represents-a-decimal-number into a number type is by using QString's toInt(bool * ok = 0, int base = 10) method.

Scenario 2: Number stored as a decimal string

v = QVariant("123456"); // 'QVariant(QString, "123456") '
v.convert(QVariant::Int); // 'QVariant(int, 123456) '

QVariant's convert(Type t) method works as one would expect in this scenario.

v = QVariant("2147483649"); // 'QVariant(QString, "2147483649") '
v.convert(QVariant::Int); // 'QVariant(int, -2147483647) '

v = QVariant("9223372036854775807"); // 'QVariant(QString, "9223372036854775807") '
v.convert(QVariant::Int); // 'QVariant(int, -1) '

However using the same method when the string contains a number that is too large for the data type causes a silent overflow, instead of doing something more useful like returning false and setting the QVariant to 0.

v = QVariant("2147483649"); // 'QVariant(QString, "2147483649") '
v = v.toString().toInt(); // 'QVariant(int, 0) '

v = QVariant("9223372036854775807"); // 'QVariant(QString, "9223372036854775807") '
v = v.toString().toInt(); // 'QVariant(int, 0) '

By using the same method as in Scenario 1, one can prevent this from happening and also use the optional *bool parameter of toInt() to be alerted of failures.

Community
  • 1
  • 1
Samuel Harmer
  • 4,264
  • 5
  • 33
  • 67