宏定义天坑记录
事件原委与推理过程
在编译一个使用了Protobuf
的项目时出现了如下报错
[yb@VM-8-7-centos boost_searcher]$ make
g++ -o http_server http_server.cc data/raw_html.pb.cc -std=c++11 -lboost_system -lboost_filesystem -lpthread -ljsoncpp -lprotobuf
In file included from /usr/local/protobuf/include/google/protobuf/io/coded_stream.h:132,
from data/raw_html.pb.h:23,
from index.hpp:11,
from searcher.hpp:10,
from http_server.cc:43:
/usr/local/protobuf/include/google/protobuf/io/coded_stream.h: In member function 'std::ptrdiff_t google::protobuf::io::EpsCopyOutputStream::GetSize(uint8_t*) const':
/usr/local/protobuf/include/google/protobuf/io/coded_stream.h:835:5: error: 'LOGLEVEL_4' is not a member of 'google::protobuf'; did you mean 'LOGLEVEL_INFO'?
835 | GOOGLE_DCHECK(ptr <= end_ + kSlopBytes); // NOLINT
| ^~~~~~~~~~~~~
大致意思就是LOGLEVEL_3这玩意没被定义过,非常莫名奇妙的错误
当发现是google/protobuf
下的头文件的问题,直接就头大了
编译过程中的语法错误就是两个极端,
- 要么是自己的代码有语法错误,改一下就行
- 但这是官方头文件文件报的语法错误啊,错误肯定不可能出现在人家,又不能调试,那没点时间和运气,这问题怕是搞不定
但我多少是有点运气在的,无意间交换了源代码中的两个头文件,居然编译过了!!!!
发现只要"searcher.hpp"
定义在"HttpServer.hpp"
前面,就能编过
啊,这就有点玄幻了啊
思考一下,这两个头文件内,应该是有某些依赖关系,或者跟编译的顺序有关
试了一下
这样也能过
也就是说,searcher.hpp
中一定有某些东西要出现在HttpServer.hpp
前面
又瞅了一眼报错
既然报错从你来,就拎着你测吧(整个头文件包含树中,这个文件处于末梢,再向末梢就是官方文件了)
放这里,诶果然又能编过了
接下来,就拿它沿着整条头文件包含树,一直向下试,看哪里出现了问题
直到到达一个log.hpp
的头文件,不能再向下时
发现,当raw_html.pb.h
在FATAL
上时能编译过,在其下面则无法编过
啊,小东西,终于找到你了啊
如果你观察仔细,发现这个3,在前面的报错里也有点端倪
当把这个宏定义为4时,果然变成了LOGLEVEL_4
原因分析
从上面推理中可以看出,显然是这个宏定义的问题
google/protobuf
的头文件中一定也有一个FATAL宏的定义,或者出现了FATAL关键词
由于C语言对宏的处理发生在预处理阶段,只进行简单无脑的文本替换
在所有的头文件中,我们都写了#pragma once
,也就意味着在整个头文件包含体系中,只要出现第一次FATAL
的宏定义,那以后的所有FATAL
宏定义都将被忽视,一些不该被进行宏替换的地方也会被替换
自然,当我们把google/protobuf
的头文件放在我们的FATAL之前,也就不会受我们的宏定义影响
总结
虽然某些时候,对宏的使用能让我们的代码简单玄妙不少,但坑也是真的坑啊
有了上面的经验教训,宏这种东西以后还是尽量少用了,毕竟不是每次都这么好运气
如果替换宏的话,大家可以尝试使用const
、static const
或者枚举的语法