我想禁止在我拥有的代码库中使用 iostreams(出于各种原因)。有没有办法可以检查符号文件或在使用该 API 时强制编译器发出错误?
5 回答
一个简单的方法是提供一个虚拟iostream
实现,它只会抛出一个编译时错误。
下面的例子假设一个 GCC 工具链——我想这个过程与其他编译器类似。
首先,创建您的虚拟 iostream 文件:
#error 'Use of iostream is prohibited'
一些用于演示的虚拟应用程序代码:
#include <iostream>
int main (int argc, char** argv) {
std::cout << "foo!";
return 0;
}
编译如下(假设 dummyiostream
并且main.cpp
在工作目录中):
g++ -I. main.cpp
编译失败并出现以下错误:
In file included from main.cpp:2:0:
./iostream:1:2: error: #error 'Use of iostream is prohibited'
main.cpp: In function 'int main(int, char**)':
main.cpp:4:2: error: 'cout' is not a member of 'std'
额外的好处:通常在该文件中声明的符号(例如cout
这里)是未定义的,因此也会在编译器输出中被标记。因此,您还可以获得指向您使用禁止 API 的确切位置的指针。
更新: Visual C++ 2012 的说明。
正如@RaymondChen 在下面的评论中指出的那样,针对 Visual C++ 量身定制的解决方案可能对 OP 更有用。因此,以下概述了我在 Visual C++ 2012 下实现与上述相同的过程。
首先,使用上面的 C++ 代码创建一个新的控制台项目。还要创建我上面描述的虚拟 iostream 标头,并将其放在一个容易找到的目录中(我将我的放在主项目源目录中)。
现在,在解决方案资源管理器中,右键单击项目节点并从下拉列表中选择“属性”。在出现的对话框中,从左侧的树中选择“VC++ 目录”。将包含虚拟 iostream 文件的目录添加到右侧显示的包含目录列表中,并用分号与其他目录分隔:
在这里,我的项目名为 TestApp1,我只是将其主目录添加到$(IncludePath)
已经存在的目录中。请注意,添加而不是附加很重要 - 列表中目录的顺序决定了搜索顺序,因此如果$(IncludePath)
出现在您的自定义目录之前,系统标题将优先使用您的虚拟标题 - 而不是您想要的。
单击确定,然后重建项目。
对我来说,这样做会导致 VC++ 控制台中出现以下错误(为简洁起见稍作编辑):
error C1189: #error : 'Use of iostream is prohibited'
IntelliSense: #error directive: 'Use of iostream is prohibited'
IntelliSense: namespace "std" has no member "cout"
请注意,IntelliSense 还会发现(现在)非法使用cout
- 它在编辑器中以错误标记突出显示。
这是一个讨厌的黑客,但它应该工作。
C 标准(以及因此的 C++ 标准)允许在#include
指令中使用预处理器标记。这也称为“计算包含”。
因此,如果有人尝试使用 iostream,在您的 makefile(或 IDE 项目设置中的编译器选项)中添加类似-Diostream
的CFLAGS
内容应该会可靠地破坏构建。
当然,如果使用空宏,错误消息不会提供太多信息,但您可以改用类似 的-Diostream=DUDE_DONT_USE_IOSTREAM
内容,这将显示如下错误:DUDE_DONT_USE_IOSTREAM: file not found
。
如果您以后改变主意,您也可以毫不费力地再次关闭它。只需删除构建选项。
您检查符号文件的想法是可行且非常现实的。virtual ~ios_base();
是所有流都将继承的单一方法,并且由于它是虚拟的且非平凡的,因此不容易内联。因此,它在目标文件中的存在是 IOstream 使用的一个非常强烈的指示。
除了 Mac 提到的编译器辅助方法之外,您还可以使用通用搜索功能。例如(我假设 zsh shell - 对于 bash 没有,**
并且在 Windows 上您需要找到如何使用 Powershell 进行操作):
# Find all mentioning on `iostream` `cin` in all files ending in cc in all subdirectories of current directory
grep iostream **/*.c
grep cin **/*.cc
如果您不想/不能使用命令行,您可以使用您喜欢的编辑器并搜索不需要的符号。
我通常结合两种方法:
- 编译,尤其是具有大量模板的大型项目,编译速度很慢,而搜索速度很快,因此您的搜索效率更高
- 另一方面,搜索操作并不准确,可能会遗漏一些东西。所以我会使用标题技巧来验证上一步中完成的解决方案
- 作为最终验证,您可以在编译后搜索符号。如果您在没有优化的情况下进行编译,这将特别有用。您可以使用
objdump
或类似(取决于平台)并注意导入的符号(如果您不说使用 iostreams 静态链接到某些东西,则此方法有效)。
一点都不。对于非常有限的子集,您可以提供自己的定义,从而导致链接器在重复项处出错。不过,这将是非常未定义的行为。一个很好的部分是不受此影响的模板。如果不执行诸如删除 iostream 标头之类的激烈操作,或者使用诸如 Clang 之类的编译器并修改源代码,那么您真的无能为力。