man*_*m-n 12 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 */
Run Code Online (Sandbox Code Playgroud)
编辑:
我可以使用,例如class machine-instruction-set二进制文件必须在多个平台上运行?在这种情况下可以有命名空间冲突吗?
Max*_*kin 27
我可以使用namespace指令来避免标识符/变量名称冲突
恰恰相反,using namespace指令引入了冲突.您可以通过指定范围来解决冲突,例如std::vector<>vs boost::numeric::ublas::vector<>..
...但是当大型项目中发生文件名或库名冲突时会发生什么?
通过系统化可以轻松防止文件名冲突:组织标题,以便模仿您的名称空间,例如boost::numeric::ublas::vector<>来自#include <boost/numeric/ublas/vector.hpp>.并且不要在一个目录中堆叠不同库的头和源,以便您可以使用不同的目录前缀包含具有相同名称的头,例如#include <lzma/version.h>vs #include <linux/version.h>..
处理此问题的正确方法是以名称冲突变得不太可能的方式设置构建环境.
例如,大多数第三方库不会将纯文件引入编译器的包含路径.相反,他们引入了包含文件的目录.您可以通过引入不同模块的子目录来提高灵活性.
考虑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 :)
Run Code Online (Sandbox Code Playgroud)
Boost附带的不同库使用相同的概念.例如,Boost lockfree和Boost assign都有一个queue.hpp头.但它们存在于不同的子目录中,所以没有冲突:
#include <boost/lockfree/queue.hpp>
#include <boost/assign/std/queue.hpp> // no clash :)
Run Code Online (Sandbox Code Playgroud)
为了使查找正确的头文件变得容易,Boost对包含文件和命名空间使用相同的结构:无锁队列位于boost::lockfree命名空间中,而来自assign queue头的函数则转到boost::assign.这样,不仅可以直接从包含文件中找到匹配的命名空间,反之亦然,它还可以降低命名空间冲突的可能性,因为命名空间冲突也可能在文件层的物理名称冲突中显现.
您可以根据自己的项目调整这些指南
这首先避免了大多数名称冲突.问题是,如果您必须使用不遵守这些规则的第三方库,并且您得到的控制范围之外的冲突会怎样?
答案是残酷并通过构建环境强制分离.通过将冲突库移动到唯一可识别的子目录中来重新组织包含路径,以解决物理冲突.这通常是非关键的.逻辑冲突需要修补和重新编译,这更加不方便.但如果你真的遇到名称冲突,这肯定表明至少有一家图书馆供应商没有做好他们的工作,你应该考虑提交一个错误.
远离特殊修复,例如#include_next修复物理冲突或预处理器定义以修复逻辑冲突.他们是肮脏的黑客,虽然他们可能暂时解决你的问题,他们很可能会回来并最终咬你.
将库放在单独的子目录中,并将父目录设置为搜索位置。所以而不是:
#include "zerz.h" // supposed to be from libfoo
#include "zerz.h" // supposed to be from libbar
Run Code Online (Sandbox Code Playgroud)
你可以这样做:
#include "libfoo/zerz.h"
#include "libbar/zerz.h"
Run Code Online (Sandbox Code Playgroud)
使用pimpl 习惯用法来隔离与每个库交互的代码,以便冲突的标识符不会由于传递包含而被拖入无数的项目中。
例如,如果 libfoo 和 libbar 都有一个名为 的函数Frobnicate,那么您希望隔离依赖于这些库的任何代码,以便其他任何内容都不必包含这些库的标头,从而最终导致冲突。只有实际调用的 .cpp(或 .c)文件Frobnicate才应该 #include 库的头文件。这可以防止意外的传递包含,这通常会导致包含Frobnicate在单个编译单元中的冲突声明。
pimpl 习惯用法通常用 C++ 术语表示,但您可以在 C 中玩同样的游戏。重点是在标头中为库定义您自己的 API。您的 API 应具有相同的功能,但使用不冲突的名称(例如,如果无法使用命名空间,则添加唯一的前缀)。所有代码都应该能够包含该标头而不会发生冲突。然后,您提供一个实现文件(.cpp 或 .c),这是唯一 #include 实际库头的文件。该实现文件本质上是将前缀函数的调用转发给实际的库函数。您所要做的就是避免在这个文件中发生冲突,这应该是可行的。