Qt: QML Int overflow

Qt: QML Int overflow

本文关键字:overflow Int QML Qt      更新时间:2023-10-16

在我的Qt应用程序中,我试图从我的c++代码传递一个大整数值到QML。
在我的c++中,我有一个Q_PROPERTY(int…),但显然int有时不足以满足我的应用程序,我得到一个溢出。
我可以在QML中使用长类型或无符号类型吗?那么动态大小的int类型呢?
从QML文档中,我所能找到的都是int,范围是"大约-2000000000到大约2000000000"。
感谢您的帮助!=)

我觉得你的问题很有趣,因为它最初看起来微不足道,但在获得答案的过程中提出了许多要点。

我假设你想从c++传递一个64位整数到QML。我稍后再解释原因。

64位整数的QML基本类型

没有64位整型QML基本类型,因此你不能指望你的c++值被转换并存储在这样一个不存在的QML类型属性中。如文档中所述,Qt模块可以扩展可用类型列表。我们甚至可以想象将来我们可以添加自己的类型,但现在不行:

目前只有Qt提供的QML模块可以提供它们的在Qt QML的未来版本中,这可能会改变。

有人可能认为在64位平台上编译他们的应用程序应该可以完成这项工作,但它不会改变任何事情。关于这个问题有很多答案。为了说明这一点,下面是在同一台计算机上使用32位和64位编译器的结果比较:

在我的电脑上(64位处理器:Core i7), 64位系统(Windows 7 64位),使用32位编译器(mingw53),我得到了

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

我把size_t放进去是为了清楚地表明它不能用来表示文件大小,这不是它的目的。

使用64位编译器(msvc2017, x64),在同一系统上:

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

使用64位编译器不会改变sizeof(int),因此认为intint*大小相同显然是错误的。

在你的例子中,看起来你想要存储一个文件大小,而32位不足以表示一个文件的大小。所以,如果我们不能依靠int来做到这一点,应该使用什么类型?

文件大小类型size

现在,我假设64位整数应该足够了,并且是存储文件大小的正确方法。但这是真的吗?根据cplusplus.com,它的类型是std::streampos。如果你在cppreference.com上查找详细信息,你会发现std::streamposstd::fpos的专门化,并描述了其典型的实现:

类模板std::fpos的专门化标识绝对流或文件中的位置。类型为fpos的每个对象都持有流中的字节位置(通常作为类型的私有成员)std::streamoff)和当前移位状态,类型为State的值(通常为std::mbstate_t).

std::streamoff的实际类型是可能是一个64位有符号整数:

类型std::streamoff是一个足够大的有符号整型表示操作程序支持的最大可能文件大小系统。通常,这是typedeflong long

另外,在同一页中,你可以读到:

类型为std::fpos的值隐式地转换为std::streamoff(转换结果是从开始的偏移量)文件)。

std::fpos类型的值可由std::streamoff类型的值构造

让我们看看我们有什么,同样取决于32位或64位的目标平台。

使用32位编译器,我得到

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

这表明,很明显,std::streampos中存储了比当前所需的更多的信息来了解文件大小。

对于64位编译器,在同一系统上:

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

sizeof(std::streamoff)的值没有变化。有可能。在这两种情况下,它都必须足够大才能存储一个文件大小。您可能希望在c++端使用std::streamoff来存储文件大小。你不能指望它是64位的。让我们假设有人想冒险假设它是64位的,如这些例子所示:仍然存在将其作为基本类型传递给QML的问题,也许,传递给Javascript引擎。

然而在QML

中使用近64位如果您只需要将大小显示为以字节为单位的数字,则需要在UI中使用文本表示。您可以坚持这个想法,使用字符串,而忘记传递整型值。如果你需要在QML端使用Javascript做算术,你可能会遇到JavascriptNumber的53位障碍,除非你不使用核心语言来做算术。

在这里,如果您假设文件大小不能大于2^53,那么使用double可以工作。您必须了解double类型在目标平台上的处理方式,如果您足够幸运,它将具有相同的能力,可以存储53位整数值而不会丢失。之后你可以直接在Javascript中操作它,这保证可以工作,至少在ECMA 262版本8中是这样。

现在可以了吗?

对于小于2^53的文件,我们可以使用double吗?Qt不一定实现ECMAScript的最新规范,而且使用了几个实现。在Webviews中,它确实使用了一个相当常见的实现。在QML中,它使用自己的、未记录的、内置的Javascript引擎,基于ECMA 262版本5。这个版本的ECMA没有明确提到一个值,保证所有较小的整型值都存储在Number中。这并不意味着它行不通。即使已经提到了,实现也只是基于规范,并没有要求遵从性。你可能会发现自己处于一个不幸的位置,让它工作很长一段时间,然后发现它不再工作了,在一个较新的Qt版本中。

看起来要做很多假设。在基于它的软件的整个生命周期中,所有这些都可能是正确的。所以这是一个处理风险的问题。

有办法降低这种风险吗?

不为几个位冒一点正确性的风险

我看到两个主要路径:

  • 根本不关心这些东西。例如,使用双精度对象,只要它似乎有效,就可以做任何您想做的事情。如果它对没有发生的案件不起作用,为什么要处理它们呢?当它不工作时,你会处理它。
  • 试图更严格,并以更稳健的方式来做。据我所知,这种解决方案可能是将所有的文件大小算术保留在c++端,只将安全值传递给QML端。例如,用于测试的布尔值和用于显示大小值的字符串(总大小,剩余大小,…)。你不需要担心任何东西有多少位。

提示:bittorrent协议使用字符串来编码大小。

问题不限于QML属性

如果您使用Javascript对Number实例操作整数值,并将其用作QML信号的int参数的参数,则可能出现相同的问题。例如,像这样声明一个信号:

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

可以处理下列语句肯定不能处理的情况:

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

失败的一个例子是通过(new Date()).getTime()。同样的限制并不局限于c++与QML的属性交互。

unsigned int和signed int在QML中可直接转换为int。

如果您在32位系统上,unsigned int将从0到4,294,967,295。

然而,这与文档相冲突,就像你所说的,这里提到了大约- 20亿到大约+ 20亿的缩小范围。

没有关于支持long或任何其他类型的更大整数值的进一步信息。

您可以在Qt邮件列表或Qt论坛中考虑这个问题。

同时,您可能想问自己,为什么要传递这么大的整数,您的应用程序/设计是否需要这样的要求。