如果库的包含路径设置不符合预期,如何引发编译错误
How to throw a compile error if the include paths for a library were set up not as intended
我们的C++库包含一个文件,其名称(被认为)等于标准库的一个标头。在我们的例子中,这是"String.h",Windows认为它与"String.h"相同,但为了这个问题,它可以是标准库中使用的任何其他文件名。
通常情况下,这种文件名模糊性不是问题,因为用户应该设置包含路径,只包括库文件夹的父文件夹(因此需要包括"LibraryFolder/String.h"),而不包括包含标头的文件夹。
但是,有时用户会错误地将include路径直接设置为包含文件夹。这意味着"String.h"将代替"String.h"包含在用户代码和标准库头中,从而导致许多编译错误,初学者可能不容易解决或理解这些错误。
在编译期间,是否有可能在我们库的头中检测到这种错误设置的包含路径,并根据对包含路径的某种检查,通过指令立即抛出编译#警告或#错误?
没有故障保护的方法。如果编译器找到另一个文件,它不会抱怨。
然而,你可以制作它,这样你就可以检测到它。在你自己的LibraryName/string.h
中,你可以定义一个唯一的符号,比如
#define MY_STRING_H412a55af_7643_4bd6_be5c_4315d3a1e6b7
然后稍后在依赖代码中,您可以检查
#ifndef MY_STRING_H412a55af_7643_4bd6_be5c_4315d3a1e6b7
#error "Custom standard library path not configured correctly"
#endif
同样,您可以使用它来检测何时包含了错误版本的库。
[编辑-根据注释]标题包含可以概括为:
- 分析
#include
行以确定要查找的标头名称 - 根据
<Foo.h>
或"Foo.h"
形式,确定要搜索的位置集(通常是目录) - 以依赖于实现的方式解释标头名称。(通常作为相对路径)。请注意,这不一定是字符串,例如MSVC不将
视为字符串转义符
- 如果找到了标头(通常,如果找到了文件),请将
#include
行替换为该文件的内容。否则,编译失败
(括号中的"通常"适用于MSVC、GCC、clang等,但理论上编译器可以直接从git存储库而不是磁盘文件进行编译)
这里的问题是,想象中的测试(头名称的拼写)必须位于包含的头文件中。该测试必然是被替换的#include
线路的一部分,因此不再存在,无法进行测试。
C++17引入了__has_include
,但这并不影响分析:它仍然必须出现在包含的头文件中,并且#include "Foo.h"
中的字符序列不可用。
[旧]可能最简单的方法,尤其是对于初学者来说,就是有一个LibraryName/LibraryName.h
。希望这个名字是独一无二的。
这样做的好处是,一旦成功,用户就可以将#include "LibraryName.h"
替换为#include "String.h"
,因为您知道路径是正确的。
也就是说,"String.h"
是在问问题。Windows不区分大小写。
使用namespace
s。在您的情况下,这将转化为以下内容:
MyString/String.h
namespace my_namespace {
class string {
...
}
}
现在,为了确保您的std::string
或任何其他名为string
的类没有被意外地而不是my_namespace::string
使用(通过任何方式,包括但不限于错误地设置包含路径),您需要使用其完全限定名称(即my_namespace::string
)来引用您的类型。通过这样做,可以避免任何命名冲突,并且如果不包含正确的头文件(除非实际上存在另一个名为my_namespace::string
的类,而该类不是您的),则可以保证会出现编译错误。还有其他方法可以避免这些冲突(例如using my_namespace::string
),但我更愿意明确我正在使用的类型。然而,这种解决方案成本高昂,因为它可能需要对整个代码库进行更改(将所有strings
更改为my_namespace::string
)。
稍微不那么麻烦的替代方案是将报头String.h
的名称更改为类似MyString.h
的名称。这将很快引入编译错误,但需要更改#include "String.h" into
#include"MyString.h"`中的所有include(与第一个选项相比,应该工作量要小得多)。
到目前为止,我想不出任何其他方式需要更少的努力。由于您正在寻找一种适用于所有类似场景的解决方案,如果我是您,我会选择namespace
,并一劳永逸地解决问题。这将防止代码中可能存在的任何其他现有/将来的命名冲突。
- boost::进程间消息队列引发错误
- C++中函数的向量返回类型引发错误
- 记录重新引发错误的函数
- 3D 纹理大小会影响程序输出,而不会引发错误
- 当 std 数组初始化太小时,C++会引发错误吗?
- std::排序为排序自定义对象时出现的向量引发错误
- 在标准向量中将元素分配给 Eigen::Vector2d 会引发错误
- 初始化对象时引发错误
- C++调用lua_dostring来加载具有"require('cjson')"的Lua Scrip引发错误:cjson.so:未定义的符号:lua_getfield
- 为什么这种类型的函数原型会引发错误?
- C++编译器在一个源文件中的一个函数调用中引发错误,但在具有相同函数调用的另一个源文件中不会引发错误
- 析构C++引发错误
- C++ strcpy_s复制到新的字符数组时引发错误
- 为什么下面的代码没有引发错误
- VS15使用constexpr和字符串文字引发错误C2975
- Assimp 4 加载缺少的材料不会引发错误
- C++结构到字节*引发错误
- 使用库编译会引发错误
- 如何在类内分配多维数组?当行>列时会引发错误?
- 在CLion中使用CMake编译带有参数的C++会引发错误,而在终端中编译则不会