这可能是我在过去几年中看到的最奇怪的事情。
我有一个项目可以在两台完全不同的机器上完美构建(openSUSE Tumbleweed和ubuntu 14.04)。 我开始使用kubuntu 16.04的新机器,这个错误开始发生:
$ g++ -std=c++14 cout_qualif.cpp -lpng -o cout_qualif
In file included from cout_qualif.cpp:1:0:
debug_utils.h:19:19: error: invalid use of ‘::’
# define msg std::cout
Clang也指出了一个错误,但是有一个完全不同的消息:
$ clang -std=c++14 cout_qualif.cpp -lpng -o cout_qualif
In file included from cout_qualif.cpp:3:
In file included from /usr/include/png++/png.hpp:34:
In file included from /usr/include/png.h:317:
/usr/include/zlib.h:94:19: error: non-friend class member 'cout' cannot have a qualified name
z_const char *msg; /* last error message, NULL if no error */
^~~
./debug_utils.h:19:19: note: expanded from macro 'msg'
# define msg std::cout
~~~~~^
1 error generated.
我遇到的最简单的测试代码是:
#include <iostream>
#include "debug_utils.h"
#include <png++/png.hpp>
int main()
{
msg << "Start" << std::endl;
png::image< png::rgb_pixel > image("input.png");
image.write("output.png");
msg << "Finish" << std::endl;
return 0;
}
和“debug_utils.h”:
#ifndef DEBUG_UTILS_H
#define DEBUG_UTILS_H
#include <iostream>
# define msg std::cout
#endif // DEBUG_UTILS_H
事实证明“png.h”包含“zlib.h”并且定义了一个结构:
typedef struct z_stream_s {
// ...
z_const char *msg; /* last error message, NULL if no error */
此msg
成员正在触发错误。如果我在#include "debug_utils.h"
之后移动我的#include <png++/png.hpp>
一行,那么一切似乎都能正常工作。
现在最后问题: 为什么这台机器无法编译我的代码而另外两个可以?
其他信息:
Kubuntu 16.04:
$ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
$ clang --version
clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)
openSUSE Tumbleweed:
g++ is 7.1.1
Ubuntu 14.04:
Exact version not available at hand but I believe it is 4.9.x
答案 0 :(得分:1)
重温问题的原因
# define msg std::cout
在包含debug_utils.h之后,debug_utils.h中的将std::cout
替换为整个代码中msg
的任何实例。由于msg
是一个常见的短标识符,特别是对于消息缓冲区,因此无意中的替换始终是潜伏在代码中的风险。对此的解决方案显而易见:不要这样做。使用更长时间,不太可能重复替换或根本不做,并替换宏。在没有看到你的用例的情况下,我可能会用一个返回正确流的函数替换宏,编译器可以很容易地内联。
混淆和问题源于为什么宏替换中的错误仅在仅在三个候选PC中的一个上编译时由一段简单的测试代码产生。
答案是工具链和支持库的差异。出于这样或那样的原因,在这些PC中只有一个上,使用msg
标识符的第三方标头包含在测试程序包含的第三方标头中。其他两个遵循不同的包含路径来构建相同的程序,并避免绊倒不需要的替换。