如何弃用C++标头?
我想弃用 C++ 头文件,以便如果有人在他们的代码中包含它,编译器会发出警告。
我知道我可以弃用单个符号,例如,使用 C++14 [[deprecated]],但是标题是否有类似的东西?也许有什么巧妙的伎俩?
请注意,即使用户不使用标题中的任何内容,我也希望编译器发出警告。
回答
这是一个可能的(尽管可能不太优雅)解决方案。
在标题中插入这样的代码
// badheader.hpp
namespace {
[[deprecated("This header is deprecated")]]
constexpr static int badheader_hpp_is_deprecated = 0;
constexpr static int please_dont_use_badheader_hpp = badheader_hpp_is_deprecated;
}
这会创建一个不推荐使用的变量badheader_hpp_is_deprecated。的初始化please_dont_use_badheader_hpp触发弃用警告。请注意,我将这两个变量放在一个匿名命名空间中,以避免可能的名称冲突。尽管如此,正如评论中所指出的,如果在同一个编译单元中,在匿名命名空间内声明了一个具有相同名称的变量,则仍然可能发生名称冲突。出于这个原因,正如评论中所建议的,上面代码中的变量有一个描述性的名称,使得名称冲突的可能性很大。
回答
一个非标准但相当便携的解决方案:
#pragma message("Header `foo.h` is deprecated!")
这被 GCC、Clang 和 MSVC 接受。GCC 和 Clang 也接受不带( ).
这不一定是“警告”,但应该足够好。
回答
我建议用一个同名的命名空间包围你的命名空间,using它来自封闭的命名空间。
#pragma once
namespace your_namespace {
namespace [[deprecated]] your_namespace {
// old stuff
}
using namespace your_namespace;
}
这不应该用来自的任何内容污染您的全局命名空间,your_namespace并且如果您包含标题,仍然会发出警告。旧的东西仍然可以your_namespace::像以前一样访问。
由于这是 ABI 重大更改,如果您在弃用标头时还没有这样做,我也建议您逐步升级库的主要版本。
- 是的。如果您的代码是可执行文件,您可以控制升级依赖项,并且没有与该代码交互的第 3 方库。否则,它会变得毛茸茸的。
- 不过,这是一个 ABI 突破性的变化。这不是一个好主意,特别是如果目标是逐渐淘汰 cruft。
回答
这是从range-v3 库中无耻地复制过来的:
#ifdef __GNUC__
#define RANGES_PRAGMA(X) _Pragma(#X)
#define RANGES_DEPRECATED_HEADER(MSG) RANGES_PRAGMA(GCC warning MSG)
#elif defined(_MSC_VER)
#define RANGES_STRINGIZE_(MSG) #MSG
#define RANGES_STRINGIZE(MSG) RANGES_STRINGIZE_(MSG)
#define RANGES_DEPRECATED_HEADER(MSG)
__pragma(message(__FILE__ "(" RANGES_STRINGIZE(__LINE__) ") : Warning: " MSG))
#endif
RANGES_DEPRECATED_HEADER("Yikes! A deprecated header!")
用Compiler Explorer试试吧。
回答
如果您的标题有一个命名空间,您可以[[deprecated]]在上面使用我猜吗?但这不适用于匿名命名空间。用户必须使用顶部空间中的某些东西才能使其工作。
如果您可以将标题放在命名空间中,那么您所要做的就是有一个using会触发警告的语句。这也是一个好主意,可以隔离这些功能,并确保用户在使用它们时遇到更多困难(如果这是一个目标)。
namespace [[deprecated]] N {
struct S {
};
}
using N::S;
但是,如果您负担不起命名空间,根据元素的数量,您可能不想using对所有元素都使用 a 。也许这可能是合法拥有 的一个案例using namespace N;,但我不确定。
经过一些研究,您可以使用#pragma message "Message"来实现您似乎想要的东西。看到这个答案
神箭