如何避免C和C ++中的命名空间冲突

时间:2013-09-28 13:01:56

标签: c++ c gcc namespaces

我可以使用using namespace指令来避免标识符/变量名冲突,但是当大型项目中发生文件名或库名冲突时会发生什么。

C中,传统方法是使用#include_next指令递归添加文件。如何在不使用C++指令的情况下在#include_next中实现相同的功能,并解决应用程序和共享库中重复文件名的问题。例如,在AIX math.h中围绕class()函数的工作与名为“class”的标识符冲突。

/* GNU Lesser GPLv2.1 or later */
#ifndef FFMPEG_COMPAT_AIX_MATH_H
#define FFMPEG_COMPAT_AIX_MATH_H

#define class some_text

#include_next <math.h>

#undef class

#endif /* FFMPEG_COMPAT_AIX_MATH_H */

修改 我可以使用,例如class machine-instruction-set二进制文件必须在多个平台上运行吗?在这种情况下可以有命名空间冲突吗?

6 个答案:

答案 0 :(得分:27)

  

我可以使用namespace指令来避免标识符/变量名称冲突

恰恰相反,using namespace指令引入了冲突。您可以通过指定范围来解决冲突,例如std::vector<>boost::numeric::ublas::vector<>

  

...但是当大型项目中发生文件名或库名冲突时会发生什么?

通过系统化来轻松防止文件名冲突:organize your headers so that they mimic your namespaces,例如boost::numeric::ublas::vector<>来自#include <boost/numeric/ublas/vector.hpp>。并且不要在一个目录中堆叠不同库的头和源,以便您可以使用不同的目录前缀包含具有相同名称的头,例如#include <lzma/version.h>#include <linux/version.h>

答案 1 :(得分:7)

处理此问题的正确方法是以名称冲突不太可能的方式设置构建环境。

编写库的指南

例如,大多数第三方库不会将纯文件引入编译器的包含路径。相反,他们引入了包含文件的目录。您可以通过引入不同模块的子目录来提高灵活性。

考虑Boost的目录结构。在顶层,Boost仅向包含搜索路径引入单个名称:boost目录。这样,即使boost引入了许多可能会发生冲突的标头文件名(例如array.hppthread.hppfunction.hpp),但它们都被包含在子目录:

#include <boost/thread.hpp> // this is boost's header
#include "thread.hpp"       // some header specific to my current project
                            // no name clash :)

同样的概念用于Boost附带的不同库。例如,Boost lockfree和Boost assign都有一个queue.hpp标头。但是他们住在不同的子目录中,所以没有冲突:

#include <boost/lockfree/queue.hpp>
#include <boost/assign/std/queue.hpp>   // no clash :)

为了方便查找正确的头文件,Boost对包含文件和命名空间使用相同的结构:无锁队列位于boost::lockfree命名空间中,而分配队列头中的函数则转到boost::assign 。这样,不仅可以直接从包含文件中找到匹配的命名空间,反之亦然,它还可以降低命名空间冲突的可能性,因为命名空间冲突也可能在文件层的物理名称冲突中显现。

您可以根据自己的项目调整这些指南

  • 不要将裸包含文件直接引入包含路径。而是将它们组合在一个目录中,以避免使用名称污染包含路径。
  • 使用目录树进一步构建单个库中模块的包含文件。
  • 尝试将物理结构与逻辑结构相匹配。如果可能,文件系统包括路径应该类似于命名空间层次结构。

这首先避免了大多数名称冲突。问题是,如果你必须使用不遵守这些规则的第三方库,并且你得到一个不受你控制的冲突?

处理第三方图书馆名称冲突的指南

答案是残酷并通过构建环境强制实现分离。通过将冲突库移动到唯一可识别的子目录中来重新组织包含路径,以解决物理冲突。这通常是非关键的。逻辑冲突需要修补和重新编译,这更加不方便。但如果你真的遇到名称冲突,这肯定表明至少有一家图书馆供应商没有做好他们的工作,你应该考虑提交一个错误。

远离#include_next之类的临时修复,以修复物理冲突或预处理器定义以修复逻辑冲突。他们是肮脏的黑客,虽然他们可能暂时解决你的问题,他们很可能会回来并最终咬你。

答案 2 :(得分:4)

文件名冲突

将库放在单独的子目录中,并将父目录设置为搜索位置。所以而不是:

#include "zerz.h"  // supposed to be from libfoo
#include "zerz.h"  // supposed to be from libbar

你可以这样做:

#include "libfoo/zerz.h"
#include "libbar/zerz.h"

标识符冲突

使用pimpl idiom隔离与每个库交互的代码,以便由于传递包含而不会将冲突的标识符拖入数以万计的项目中。

例如,如果libfoo和libbar都有一个名为Frobnicate的函数,那么你想要隔离任何依赖于这些库的代码,这样就不需要包含那些库的头了,因此最终会得到一个冲突。只有实际调用Frobnicate的.cpp(或.c)文件才能#include库的头文件。这可以防止意外的传递包含,这通常是如何最终将Frobnicate的冲突声明包含在单个编译单元中。

pimpl习语通常是C ++的术语,但你可以在C中玩同一个游戏。重点是在标题中为库定义你自己的API。您的API应具有相同的功能,但使用不冲突的名称(例如,如果您不能使用名称空间,则添加唯一的前缀)。所有代码都应该能够包含该头而不会发生冲突。然后,您提供一个实现文件(.cpp或.c),这是#includes实际库头的唯一文件。该实现文件基本上将前缀函数的调用转发给实际的库函数。您所要做的就是避免在这个文件中发生冲突,这应该是可行的。

答案 3 :(得分:2)

首先,#include_next不是标准的,是gcc扩展,也是Clang支持的,但我不知道是否有其他编译器支持它。

处理文件名冲突的唯一方法是正确使用#include "foo.h"#include <foo.h>。前者是当前项目的本地项目,而后者用于系统库。通常#include "foo.h"也会搜索系统库,但不是相反。

无论如何,#include "foo.h"应该从查看源文件目录开始,你可以使用#include "../foo.h"等来做相对包含。如果在同一个项目中有文件名冲突,则必须使用不同的编译标志来设置不同的搜索路径(基本上是创建子项目)。

对于符号冲突,#define之前的#include是标准方式。

当然,最好的方法是首先注意避免这些问题。

答案 4 :(得分:1)

在C ++程序中使用C包含时使用的有用方法是在.h文件中使用以下代码段。

#ifdef __cplusplus
extern "C" {
#endif

<your code here...>

#ifdef __cplusplus
}
#endif

这将允许您的C ++对象与C对象链接。我不知道会走另一条路,所以也许这是一个部分答案。

答案 5 :(得分:0)

&#34;我可以使用using namespace指令来避免标识符/变量名冲突&#34;:不!您应该避免使用该指令以避免名称冲突。