【问题标题】:Qt: QML Int overflowQt:QML Int 溢出
【发布时间】:2017-10-23 00:32:44
【问题描述】:

在我的 Qt 应用程序中,我试图将一个大整数值从我的 C++ 代码传递给 QML。
在我的 C++ 中,我有一个 Q_PROPERTY(int ...),但显然 int 有时对于我的应用程序来说是不够的,而且我会溢出。
我可以在 QML 中使用长类型或无符号类型吗?动态大小的 int 类型怎么样?
从 QML 文档中,我只能找到范围为“-2000000000 到 2000000000 左右”的 int。
任何帮助表示赞赏! =)

【问题讨论】:

    标签: c++ qt qml qt5 integer-overflow


    【解决方案1】:

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

    我假设您想将一个 64 位整数从 C++ 传递到 QML。我稍后会解释原因。

    64 位整数的 QML 基本类型

    没有 64 位整数 QML basic type,因此您不能期望您的 C++ 值被转换并存储在这种不存在的 QML 类型属性中。如文档中所述,Qt 模块可以扩展可用类型的列表。我们甚至可以想到未来我们可以添加自己的类型,但现在不行:

    目前只有 Qt 提供的 QML 模块可以提供它们的 自己的基本类型,但是这可能会在 Qt QML 的未来版本中发生变化。

    人们可能认为在 64 位平台上编译他们的应用程序应该可以完成这项工作,但这不会改变任何事情。关于这个问题有a lot of answers。为了清楚起见,这里是在同一台计算机上使用 32 位和 64 位编译器的结果比较:

    在使用 32 位编译器 (mingw53) 的 64 位系统(Windows 7 64 位)的计算机(64 位处理器:Core i7)上,我有

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

    我已经把size_t 明确表示它不能用来表示文件大小,它不是its purpose

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

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

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

    在您的情况下,您似乎想要存储文件大小,而 32 位不足以表示文件的大小。所以如果我们不能依靠int 来做,应该使用什么类型?

    文件大小类型大小

    目前,我假设 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) 的值没有改变。它可能有。在这两种情况下,它都必须足够大才能存储文件大小。您可能希望使用std::streamoff 在 C++ 端存储文件大小。你不能依赖它是 64 位的。 假设有人想冒险假设它是 64 位的,如这些示例所示:仍然存在将其作为基本类型传递给 QML 甚至可能传递给 Javascript 引擎的问题。

    不过在 QML 中使用接近 64 位

    如果您只需要将大小显示为以字节为单位的数字,则需要在 UI 中使用文本表示。您可以坚持这个想法,使用字符串,而忘记传递整数值。 如果您需要在 QML 端使用 Javascript 进行算术运算,除非您不使用核心语言进行算术运算,否则您可能会点击 Javascript Number53 bit barrier

    在这里,如果您假设文件大小不能大于 2^53,那么使用 double 可以工作。您必须知道double 类型在您的目标平台上是如何处理的,如this answer 中所述,如果您足够幸运,它将具有相同的能力来无损失地存储 53 位整数值。之后您可以直接在 Javascript 中对其进行操作,并且保证可以工作,至少在 ECMA 262 版本 8 中是这样。

    现在可以了吗?我们可以对小于 2^53 的任何文件大小使用双精度吗?

    嗯……Qt不一定实现ECMAScript的最新规范,使用several implementations。 在 Webviews 中,它确实使用了rather common implementation。 在 QML 中,它使用自己的、未记录的、内置的 Javascript 引擎,基于 ECMA 262 第 5 版。这个版本的 ECMA 没有明确提到一个值,所有较小的整数值都保证存储在 Number 中。这并不意味着它不会工作。即使已经提及,实施也只是基于规范,并不要求合规。你可能会发现自己很不幸地让它工作了很长时间,然后在更新的 Qt 版本中发现它不再工作了。

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

    有没有办法降低想要这样做的人的风险?

    不冒一点正确性的风险

    我看到了两条主要路径:

    • 根本不关心这些东西。例如,使用双精度,只要它看起来有效,就可以做任何你想做的事情。如果它不适用于没有发生的情况,为什么要尝试处理它们?当它不起作用时,你会处理它。
    • 努力做到严谨,并以更稳健的方式做到这一点。据我了解,这种解决方案可能是将所有文件大小算法保留在 C++ 端,并仅将安全值传递给 QML 端。例如用于测试的布尔值和用于显示尺寸值的字符串(总尺寸、剩余尺寸……)。您无需担心任何事物中有多少位。

    提示:bittorent protocol 使用字符串来编码大小。

    问题不仅限于 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 交互的限制相同。

    【讨论】:

      【解决方案2】:

      unsigned int 和signed int 在QML​​ 中可以directly 转换为int。

      如果您在 32 位系统上,无符号整数会将您从 0 带到 4,294,967,295。

      但是,这与文档相冲突,就像您所说的那样,它提到了大约 -20 亿到 +20 亿here 的减少范围。

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

      您可以为Qt Mailing listQt forums 考虑这个问题。

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

      【讨论】:

      • 我的应用程序是一个 bittorrent 客户端,所以我将 torrent 的大小(以字节为单位)传递给 QML。这只能让我下载大小约为 2GB 的种子。我可以以千字节而不是字节为单位传递种子的大小,但我宁愿不这样做。我只是尝试使用无符号整数,它确实有效,尽管它可以让我达到 4GB,但我仍然认为这还不够。
      • “提到一个缩小的范围”...因为那是有符号 32 位整数的范围?
      • @TalZion 您可以对低 32 位和高 32 位使用两个整数,从而有效地获得 64 位值。
      • @peppe ...确实是...但是规定的范围不允许完全表达无符号的 32 位整数...并且文档明确指出可以直接表达 unsigned int作为 QML 中的 int 类型。你看到矛盾了吗?
      猜你喜欢
      • 1970-01-01
      • 2016-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多