7

记一次 pugixml 编译错误的解决

 11 months ago
source link: https://bianchengnan.gitee.io//articles/unexpected-pugixml-compile-error-after-solving-20000-more-compile-errors/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

前一阵子,平台在换基线,底层接口变了很多,因此引出了至少 20000 个编译错误。在加班改完这些编译错误后,没想到又遇到了一个诡异的编译错误。而且之前还解决过类似的编译错误,但是这次却没能第一时间找出罪魁祸首。一起看看这是一个什么编译错误吧。

诡异吧?标准模板库里(参考 vector 的实现)大量使用了这种技法( c++ 中典型的 placement new)。为什么这里就不行了呢?

解决这种诡异的编译错误,杀手锏是查看预编译生成的文件。好在 vs 已经提供了相应的支持。

/P 表示将预处理的输出写入到文件中(每个 .cpp 文件会生成一个对应的 .i 文件)。

/C 表示保留注释。

可以在有问题的源码上方加上独特的标记 // TODO:BCN check here,这样可以通过搜索快速定位到错误的代码的位置。强烈建议这么做,因为预处理输出的文件实在是太大了。

查看输出结果

vs 中按上图所示设置好,然后重新编译,编译完成后会生成如下几个 .i 文件,如下图:

注意: 相对于 .cpp 文件,.i 文件非常巨大。在这个简单的示例工程中,即使没写什么业务代码,生成出来的文件都有 11 MB 多,实际项目中产生的 .i 文件达到几十兆,上百兆是很轻松的。

大多数情况下,可以很明确的知道要查看哪个 .i 文件(可以通过错误提示来判断)。但是本例比较特殊,不知道应该查看哪个 .i 文件(报错的文件是 pugixml.cpp ,但是生成的 .i 文件中并没有名为 pugixml.i 的文件)。

请出 File Locator,设置搜索目录为 .i 文件所在的目录,设置文件名为 *.i,并设置 Containing text"// TODO:BCN check here",点击 Start 开始搜索:

很快就得到了搜索结果 —— 文件 PugiXmlCompileErrorDlg.i 的第 664200 行。

使用 EditPad 打开 PugiXmlCompileErrorDlg.i,按 Ctrl + g ,在弹出的界面中的行号位置输入 664200 即可跳转过去。但是 EditPad 中的行号和 File Locator 给出来的行号不匹配,664200 行并不是期待的注释内容。没关系,按 ctrl + f 重新搜索一遍,瞬间就搜到了(感叹一下 EditPad 的强大)。

注意看高亮内容,本来应该是 new (memory) xml_attribute_struct(page); 的,但是却在 new(memory) 之间多了一段”奇怪“的字符串。

看到这,我瞬间就明白是怎么回事了,相信有 MFC 开发经验的小伙伴儿应该也反应过来了:

DEBUG 版的 MFC 程序中,new 可能会被定义成 DEBUG_NEW

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

DEBUG_NEW 的定义如下:

// VC\atlmfc\include\afx.h
#define DEBUG_NEW new(THIS_FILE, __LINE__)

所以,new 最终被替换成了 new(THIS_FILE, __LINE__) ,于是就出现了这个奇怪的编译错误。

虽然找到了原因,但是还有一些细节没有弄清楚。

  1. 为什么标准模板库中可以这么用?在 pugixml 里不能这么用?
  2. 为什么这个编译错误报在 pugixml.cpp 中?明明工程中并没有添加 pugixml.cpp 这个文件啊?

因为这两个问题都比较简单,本文不打算深入探讨,直接给出结论。

问题1:标准库可以这么用是因为 —— 在包含标准库中的头文件时,new 还是 new,没有被定义成 DEBUG_NEW。而 pugixml 有问题,是因为在包含 pugixml.hpp 之前,new 被定义成了 DEBUG_NEW。如下图:

说明: 实际项目远比示例代码要复杂。不是那么容易能看出来的。

问题2:因为如果定义了 PUGIXML_HEADER_ONLY 并且没有定义 PUGIXML_SOURCE, pugixml.hpp 会在内部包含 pugixml.cpp

是的,你没看错,源文件也是可以通过 #include 包含的。

只需要把 #include "pugixml/pugixml.hpp" 移动到 #ifdef _DEBUG 上方即可。或者干脆注释掉 #define new DEBUG_NEW 这一行。

  • 虽然之前解决过类似的问题,但是在耗费大量体力和脑力之后,很难保持清醒的头脑。
  • EditPad 是最近发现的文本编辑神器,轻松打开上百兆(甚至上千兆)的大文件,秒杀大多数其它文本编辑器。而且有免费版,强烈推荐一波。
  • vs 中,可以通过 /P 编译选项来输出预编译的结果到文件中(.i 后缀的文件)。
  • 并不是只有 .h 文件才能被 include.cpp 文件也可以被 include

什么情况下可以 #include xxx.cpp,什么情况下不能呢?为什么?

欢迎留言讨论交流。

https://en.cppreference.com/w/cpp/language/new

https://en.wikipedia.org/wiki/Placement_syntax

https://www.enseignement.polytechnique.fr/informatique/INF478/docs/Cpp/en/cpp/memory/new/operator_new.html


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK