【问题标题】:How can I ensure no code uses an API?如何确保没有代码使用 API?
【发布时间】:2013-08-13 06:14:02
【问题描述】:

我想禁止在我拥有的代码库中使用 iostream(出于各种原因)。有没有办法在使用该 API 时检查符号文件或强制编译器发出错误?

【问题讨论】:

  • 对一个疯狂问题的疯狂回答:在调用编译器之前暂时移动 iostream 的头文件。编译器完成后将其移回。 (这都可以塞进 Makefile 中。)
  • 好的,这是一个真正的解决方案。使用此处的方法:stackoverflow.com/questions/2726993/… 将新路径添加到您的 g++ 链接列表。在那里包含一个 iostream 标头,但要确保该标头中存在语法错误(或某些东西)。只要包含它,它就会炸毁编译。
  • 使用标题改进@skishore 解决方案:使用#error 预处理器指令提供友好的错误消息。
  • 也只有iostreamsistreamsostreams?只是cincout 对象(以及cerr 和宽流变体)甚至是stringstreamfstream 变体中的实例?
  • 如果<string> 包含<iostream> 怎么办?与 C 不同,C++ 对此没有限制,skishore 的解决方案会打破这一点。

标签: c++


【解决方案1】:

一个简单的方法是提供一个虚拟的iostream 实现,它只会抛出一个编译时错误。

以下示例假设使用 GCC 工具链 - 我想该过程与其他编译器类似。

首先,创建您的虚拟 iostream 文件:

#error 'Use of iostream is prohibited'

要演示的一些虚拟应用程序代码:

#include <iostream>

int main (int argc, char** argv) {
    std::cout << "foo!";
    return 0;
}

编译如下(假设虚拟iostreammain.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 在下面的 cmets 中指出的那样,针对 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 - 它在编辑器中以错误标记突出显示。

【讨论】:

  • -1 表示在cout 之前没有std::。说真的,请不要添加using
  • 这会在包含 iostream 但不使用任何流的代码中生成误报。它也无法捕获手动声明 std::cout 的代码。
  • @DeadMG:真的吗? -1 表示与问题完全无关的内容?你是对的,它应该在那里(现在也是),但我认为它不值得放弃整个答案。
  • 编译器根本不需要通过文件实现标准头文件包含——事实上#include &lt;iostream&gt; 甚至不需要存在甚至使用名为iostream 的文件。理论上,编译器可以从数据库、互联网或通过心灵感应撒上魔法酱。
  • @NikBougalis:我非常清楚编译器不需要实现标准包含但它想要。这就是为什么当我在 GCC 中给出我的示例时,我添加了一个免责声明,表明它可能无法以完全相同的方式与其他编译器一起工作。对于它的价值,这绝对确实适用于 GCC - 我在发布之前确实对其进行了测试。
【解决方案2】:

这是一个令人讨厌的 hack,但它应该可以工作。

C 标准(因此也包括 C++ 标准)允许在 #include 指令中使用预处理器标记。这也称为“计算包含”。

因此,如果有人尝试使用 iostream,在您的 makefile(或 IDE 项目设置中的编译器选项)中添加类似 -DiostreamCFLAGS 的内容应该会可靠地中断构建。

当然,如果使用空宏,错误消息不会提供太多信息,但您可以改用 -Diostream=DUDE_DONT_USE_IOSTREAM 之类的内容,它会显示如下错误:DUDE_DONT_USE_IOSTREAM: file not found

如果您以后改变主意,您也可以毫不费力地再次关闭它。只需删除构建选项即可。

【讨论】:

  • -1 用于当 OP 未提及他的编译器时特定于编译器的 hack。
  • 真的吗,DeadMG?果然你的编译器理解/D,如果它不理解-D。您是否试图在@Mac 的答案中找到比上述更荒谬的反对票,或者您今天有什么问题? :)
  • 即使编译器既不支持-D 也不支持/D,那么如果我正确理解答案,您也可以将#define iostream DUDE_DONT_USE_IOSTREAM 放在文件顶部。
  • @MaciejPiechotka:确实如此。 :) 但是当然你必须在每个文件中都这样做,在 makefile 中(或在项目设置中)使用 -D 会更好,更少干扰(尽管它仍然使用预处理器)并且更舒适。
  • @Damon 我更多的是回复 DeadMG 抱怨这可以以“便携式”方式完成。
【解决方案3】:

您检查符号文件的想法是可行且非常现实的。 virtual ~ios_base(); 是所有流都将继承的单一方法,由于它是虚拟的且非平凡的,因此不容易内联。因此,它在目标文件中的存在非常强烈地表明了 IOstream 的使用。

【讨论】:

    【解决方案4】:

    除了 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 静态链接到某物,则此方法有效)。

    【讨论】:

    • @DeadMG:然后呢? C++ 在文本文件中,grep 对 grep 文件进行操作,因此 grep 可以对 C++ 进行操作。以类似的方式,git 不是 C++,但我在处理 C++ 项目时使用它。
    【解决方案5】:

    不,一点也不。对于非常有限的子集,您可以提供自己的定义,从而导致链接器在重复项处出错。这将是 very 未定义的行为。一个很好的部分是不受此影响的模板。如果不做诸如删除 iostream 标头之类的激烈操作,或者使用 Clang 之类的编译器并修改源代码,您真的无能为力。

    【讨论】:

    • 没有对你投反对票,但是 AFAIK 禁用语言内置的功能已经超出了 C++ 标准的范围,所以提到做 X 来禁用语言内置的 Y 可能是 UB 排序的适得其反。此外,这个答案似乎更像是一个长评论,而不是一个实际的答案。
    • 你给出了一个愚蠢的理由来否决这个问题中的每个答案,这样人们就会赞成你的答案。好吧,我认为这有点适得其反。
    猜你喜欢
    • 2015-11-20
    • 1970-01-01
    • 1970-01-01
    • 2017-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多