我可以使用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
二进制文件必须在多个平台上运行吗?在这种情况下可以有命名空间冲突吗?
答案 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.hpp
,thread.hpp
或function.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;:不!您应该避免使用该指令以避免名称冲突。