Reputation: 1441
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
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.
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 ?
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 typefpos
holds the byte position in the stream (typically as a private member of typestd::streamoff
) and the current shift state, a value of typeState
(typicallystd::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 atypedef
tolong long
.
In addition, in the same page, you can read that :
a value of type
std::fpos
is implicitly convertible tostd::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 typestd::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.
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?
I see two main paths :
An hint : the bittorent protocol uses strings to encode sizes.
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
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