Tal Zion
Tal Zion

Reputation: 1441

Qt: QML Int overflow

In my Qt app I'm trying to pass a large integer value from my C++ code to QML.
In my C++ I have a Q_PROPERTY(int ...), but apparently int sometimes isn't enough for my app and I get an overflow.
Can I use long types or unsigned types in QML? How about dynamically sized int types?
From the QML documentation all I could find was int with a range of "around -2000000000 to around 2000000000".
Any help is appreciated! =)

Upvotes: 2

Views: 3197

Answers (2)

SR_
SR_

Reputation: 917

I find your question interesting because it looks trivial initially, and though raises many points in the way to get an answer.

I assume that you want to pass a 64 bit integer from C++ to QML. I explain later why.

QML basic type for a 64 bit integer

There is no 64 bit integral QML basic type, therefore you cannot expect your C++ value to be converted and stored in such a non-existing QML type property. As stated in the documentation, Qt modules can extend the list of available types. We can even think of a future where we could add our own types, but not nowadays :

Currently only QML modules which are provided by Qt may provide their own basic types, however this may change in future releases of Qt QML.

One may think that having their application compiled in a 64 bit platform should do the job, but it won't change a thing. There is a lot of answers about this question. To make it obvious, here is a comparison of the results using a 32 bit and a 64 bit compiler on the same computer :

On my computer (64 bit processor : Core i7) with a 64 bit system (Windows 7 64 bit) using a 32 bit compiler (mingw53), I've got

 4 = sizeof(int) = sizeof(long) = sizeof(int*) = sizeof(size_t)

I've put size_t to make it clear it cannot be used to denote a file size, it's not its purpose.

With a 64 bit compiler (msvc2017, x64), on the same system :

 4 = sizeof(int) = sizeof(long)
 8 = sizeof(int*) = sizeof(size_t)

Using a 64 bit compiler did not change sizeof(int), so thinking of an int to be the same size of an int* is obviously wrong.

In your case, it appears that you want to store a file size and that 32 bits weren't enough to represent the size of a file. So if we cannot rely on an int to do it, what type should be used ?

File size type size

For now, I assumed that a 64 bit integer should be enough and the right way to store a file size. But is it true ? According to cplusplus.com, the type for this is std::streampos. If you look for details on cppreference.com you find that std::streampos is a specialization of std::fpos and a description of its typical implementation :

Specializations of the class template std::fpos identify absolute positions in a stream or in a file. Each object of type fpos holds the byte position in the stream (typically as a private member of type std::streamoff) and the current shift state, a value of type State (typically std::mbstate_t).

And the actual type of a std::streamoff is probably a 64 bit signed integer :

The type std::streamoff is a signed integral type of sufficient size to represent the maximum possible file size supported by the operating system. Typically, this is a typedef to long long.

In addition, in the same page, you can read that :

a value of type std::fpos is implicitly convertible to std::streamoff (the conversion result is the offset from the beginning of the file).

a value of type std::fpos is constructible from a value of type std::streamoff

Let's look at what we have, again depending on the 32 or 64 bit target platform.

Using a 32 bit compiler I've got

  8 = sizeof(long long) = sizeof(std::streamoff)
 16 = sizeof(std::streampos)

It shows that, obviously, more information than is currently necessary to know a file size is stored in a std::streampos.

With a 64 bit compiler, on the same system :

  8 = sizeof(long long) = sizeof(std::streamoff)
 24 = sizeof(std::streampos)

Value of sizeof(std::streamoff) did not change. It may have. In both cases, it had to be large enough in order to store a file size. You may want to use std::streamoff to store the file size on the C++ side. You cannot rely on it being 64 bit. Let's assume that one wants to take the risk of making the assumption that it is 64 bit, as shown in these examples : still remains the problem of passing it to the QML as a basic type and, maybe, to the Javascript engine.

Nevertheless using a near-64 bit in QML

If you just need to display the size as a number in bytes, you'll need a text representation in the UI. You can stick with this idea, use a string, and forget about passing an integral value. If you need to do arithmetic on the QML side, using Javascript, you'll probably hit the 53 bit barrier of the Javascript Number unless you do not use the core language to do the arithmetic.

Here, if you make the assumption that a file size cannot be greater than 2^53, then using a double could work. You have to know how double type is handled on your target platforms as stated in this answer, and if you're lucky enough, it will have the same ability to store with no loss a 53 bit integral value. You can manipulate it directly in Javascript after, and this is guaranteed to work, at least in ECMA 262 version 8.

So is it OK, now ? Can we use doubles for any file size lower than 2^53 ?

Well… Qt does not necessarily implement the latest specification of ECMAScript and uses several implementations. In Webviews, it does use a rather common implementation. In QML, it uses its own, undocumented, built-in Javascript engine, based on ECMA 262 version 5. This version of ECMA does not explicitely mention a value for which all smaller integral values are guaranteed to be stored in a Number. That does not mean it won't work. Even if it had been mentionned, the implementation is only based on the specification and does not claim for compliance. You may find yourself in the unlucky position to have it working for a long time, and then discover that it does not anymore, in a newer release of Qt.

It looks like a lot of assumptions have to be made. All of them may be and may remain true during the whole life of the software based on it. So it is a matter of dealing with a risk.

Is there a way to lower that risk for one that would want to?

Not risking a bit of correctness for a few bits

I see two main paths :

  • not bothering with all this stuff at all. Using a double for instance, and doing whatever you want as long as it seems to work. If it's not working for cases that do not happen, why trying to handle them ? When it does not work, you'll deal with it.
  • trying to be rigorous, and to do it the more robust way. As far as I understand, a solution of this kind could be to keep all the file size arithmetics on the C++ side and passing only safe values to the QML side. For instance boolean values for testing and strings for displaying the value of the sizes (total size, remaining size, …). You do not need to worry about how many bits there is in anything.

An hint : the bittorent protocol uses strings to encode sizes.

The problem not limited to QML properties

The same problem may arise if you use Javascript to manipulate integral values with a Number instance, and use it as argument to an int parameter of a QML signal. For instance, declaring a signal like this :

  signal sizeChanged(var newSize) // Number class (maybe 53 bit) integral value possible

may handle cases that the following certainly cannot :

  signal sizeChanged(int newSize) // 32 bit int limitation

An example of failure is to pass (new Date()).getTime(). It is the same limitation that is not restricted to C++ property interactions with QML.

Upvotes: 1

ManuelH
ManuelH

Reputation: 866

unsigned int and signed int are directly convertable to int in QML.

If you are on a 32bit system, the unsigned int would get you from 0 to 4,294,967,295.

This however, conflicts with the documentation, which like you state, mentions a reduced range of around -2 billion to around +2 billion here.

There is no further information about support for long, or for any other type of larger integer values.

You could consider this question for the Qt Mailing list or Qt forums.

In the meantime, you may want to ask yourself, why you are passing such large integers around and if your application/design necessitates such a requirement.

Upvotes: 0

Related Questions