【问题标题】:QT Type being defined as an int by an external library?QT 类型被外部库定义为 int?
【发布时间】:2020-12-02 14:06:51
【问题描述】:

我正在将一个项目集成到另一个项目中,并且在使用 QT 时遇到了一些构建问题。 我得到的问题是在声明 QMetaType::Bool 的 moc 文件中生成了一行。 它说“'int'之前的预期不合格ID”,然后是由该错误引起的几个括号错误。 根据我的研究,其他人说这是由于项目中将 Bool 定义为 int 的其他地方的 xlib.h。 我已经尝试过“#undef Bool”行,但它似乎不起作用。当我将它放在调用堆栈中最近的 cpp 文件的标题中时,上面提到的错误消失了,但它也破坏了我在所述 cpp 文件中声明的所有对象,给了我一堆未定义的引用错误,我知道不是真的。 所以两个问题:

  1. 尽管我显式调用了 QMetaType:: 命名空间,但为什么我从不同的库中获取了不同的 Bool 定义却收到错误消息?

  2. 有解决此问题的其他方法的想法吗?

编辑:这是在 moc 文件中导致问题的功能块。

#undef QT_MOC_LITERAL

static const uint qt_meta_data_FUNCTIONNAMEREDACTED[] = {

 // content:
       7,       // revision
       0,       // classname
       0,    0, // classinfo
       6,   14, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       0,       // signalCount

 // slots: name, argc, parameters, tag, flags
       1,    0,   44,    2, 0x0a /* Public */,
       3,    1,   45,    2, 0x0a /* Public */,
       5,    1,   48,    2, 0x0a /* Public */,
       6,    0,   51,    2, 0x0a /* Public */,
       7,    1,   52,    2, 0x0a /* Public */,
       9,    1,   55,    2, 0x0a /* Public */,

 // slots: parameters
    QMetaType::Void,
    QMetaType::Void, QMetaType::Int,    4,
    QMetaType::Void, QMetaType::Int,    4,
    QMetaType::Void,
    QMetaType::Void, QMetaType::Bool,    8,
    QMetaType::Void, QMetaType::QString,   10,

       0        // eod
};

【问题讨论】:

  • 关于 1.:xlib.h 的问题不是Bool,而是它被定义为宏(例如#define Bool int)。预处理(替换宏的步骤)继承自 C 并且完全与命名空间无关。这就是为什么(尤其是这样的)宏在 C++ 中被认为是最糟糕的。不幸的是,当xlib.h 成立时,C++ 还没有发明。 #undef Bool 的提示是正确的,但您必须将其插入正确的位置(QMetaType::Bool 第一次出现之前和#include <xlib.h> 之后)。
  • 结合 MOC 生成的代码,这可能不是那么容易。一种可能的解决方法:运行一次 MOC,然后编辑生成的文件。 (并防止 MOC 覆盖每个构建中的文件。)
  • 为了澄清起见,错误来自以下行:“QMetaType::Void, QMetaType::Bool, 8,” 这是一个生成的 moc_ 文件,不是我写的文件。还有 Scheff,他的评论刚刚出现:生成这个 moc 的 cpp 文件,我把 undef 行放在这里,它什么也没做。我在这个 cpp 文件的头文件中尝试了它,它通过我上面提到的那些未定义的引用错误破坏了它。
  • 如果#undef Bool 不起作用,则说明位置不正确。您知道一般预处理的工作原理吗?
  • 如果#undef Bool在某处无效,我会专门检查发生此错误的c++文件。(即使错误在标题中弹出,您也必须专注于c++已编译的文件。)您可以使用 -E 编译此特定文件,并将获得一个(非常长的)新源文件,其中每个宏替换都已完成。评估这肯定是非常乏味的。另一个选项是命令行参数。 (忘了哪个)它显示了执行的#includes。因此,您可能会意识到什么包括什么顺序。有了这个,你应该就能解开这个谜题了。

标签: c++ qt cmake qmake


【解决方案1】:

C++ 中的C preprocessor 是对 C 的继承。

粗略地说,它提供了超越(之前)编译器精确句法分析的文本替换。 (cppreference.com – Phases of translation)

因此,预处理器与 namespace 无关,这在 C 中不是问题(在 C 中根本不存在命名空间之类的东西),但它会在 C++ 中引起奇怪的影响。

虽然我第一次听说 xlib.h 的问题,但这是有关 windows.h 的常见奇怪问题的来源(它广泛使用宏在 ASCII 和 Unicode 风格的 API 函数之间切换)。

预处理的中间结果通常直接输入到后面的编译器阶段,这样作者就不会立即看到意外的替换。

尽管如此,每个 C 和 C++ 编译器都提供了一个仅预处理器运行的选项,它会发出生成的 C 或 C++ 源代码。因此,如果出现像 OP 这样的奇怪且难以解释的错误,可能值得至少访问发生错误的 C++ 源的预处理源文件。

(即使头文件报错,也必须追溯到源文件,因为#includes也是预处理的对象。)

这些预处理文件可能会变得非常大,因为许多标准和/或系统标头也可能包含(间接),导致单个 C++ 源文件有数千行。

另一个选项是启用有关包含的标头的诊断输出。

对于 MSVC++,相应的。选项是/showIncludes。默认情况下通常禁用此选项,因为即使仅列出包含通常会导致诊断输出冗长且不方便。


一旦一个宏(在直接或间接包含的标头中定义)被确定为问题来源,就可以考虑如何解决这个问题。

主要有两种选择:

  1. 更改#includes的顺序
  2. #undef 分别。在此源文件中相关的#include 之后或至少在最后一个#include 之后的宏。

这必须在每个相关的 C++ 源文件中完成。

它也可以在标题中完成,但这很容易出错,我不建议这样做。原因很简单:

宏可能已定义、未定义、再次定义,具体执行方式取决于确切的顺序,文件由编译器“看到”。 对于 C++ 源文件,这很清楚,因为它本身就是 translation unit。 相反,一个标头可能包含在其他标头之前或之后的各个位置,这在标头本身编写时很难预测。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    • 1970-01-01
    • 2014-10-06
    • 1970-01-01
    • 1970-01-01
    • 2013-07-03
    • 2017-11-21
    相关资源
    最近更新 更多