如何废弃一个C++头文件?

38
我想废弃一个C++头文件,这样如果有人在他们的代码中包含它,编译器会发出警告。 我知道可以废弃单个符号,例如使用C++14的[[deprecated]],但是是否有类似于头文件的东西?也许有一些聪明的技巧? 请注意,即使用户没有使用头文件中的任何内容,我也希望编译器发出警告。

13
#警告 不要打扰巨人。 - Eljay
9
值得注意的是,#warning 技术上来说不是可移植的 - 0x5453
是的,我的错,我应该明确说明我正在寻找MSVC、GCC和Clang编译器的解决方案。它们不一定要相同,但我希望每个编译器都有一个解决方案。到目前为止,似乎“#warning”对于MSVC不起作用。 - herr_shamanskiy
2
你想要一个最大的警告吗?因为有 #error - Hatted Rooster
1
不仅在技术上无法移植。我的意思是,这不仅仅是纯粹的抽象问题,只对纯粹主义者和教条主义者感兴趣。如果您的代码包括#warning,MSVC将会错误,这使得它在大多数情况下都不是一个有效的选择。使用#pragma代替;这个更广泛地支持,并且不会根据标准导致错误。像Hedley这样的东西可以为您提供一个编译器不可知的公共接口。 - Cody Gray
5个回答

44

这里是一种可能的(虽然也许不太优雅)的解决方案。

在头部插入以下代码:

// 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会触发已弃用警告。请注意,我将两个变量都放在匿名命名空间中,以避免可能的名称冲突。不过,正如注释中所指出的那样,如果在同一编译单元中声明了具有相同名称的变量,则仍然可能发生名称冲突。因此,如注释中所建议的那样,上面代码中的变量具有描述性名称,使名称冲突的可能性非常低。


1
通过将匿名命名空间嵌套在所有头文件符号所在的命名空间内,使其更加不可思议。 - Deduplicator

21

一种非标准但相当可移植的解决方案:

#pragma message("Header `foo.h` is deprecated!")

这个在GCC、Clang和MSVC中是可被接受的。GCC和Clang也可以接受无需使用( )的形式。

这不一定是一个“警告”,但应该足够好。


16

我建议将你的命名空间嵌套在一个同名的命名空间中,并从封闭的命名空间中使用它。

#pragma once

namespace your_namespace {
    namespace [[deprecated]] your_namespace {
        // old stuff
    }
    using namespace your_namespace;
}

这不应该使用来自your_namespace的任何内容污染您的全局命名空间,并且仍然会在包含头文件时发出警告。旧内容仍然可以通过以前的方式通过your_namespace::访问。

由于这是一个ABI破坏性变化,因此建议在弃用头文件时,如果您还没有执行,则也将库的主要版本号增加。


3
这是一次破坏ABI的更改。这并不是一个好主意,特别是如果目标是逐步淘汰过时代码。 - Deduplicator
1
@Deduplicator 有一定的道理。当标题变得过时时,会发生一次中断,当您决定升级到这个新版本的库时,也会发生中断。重新编译代码并不是一个很大的要求。 - Ted Lyngmo
3
是的。如果您的代码是可执行文件,您可以控制升级依赖项,并且没有第三方库与该代码交互。否则就会变得棘手。 - Deduplicator
1
去重器。非常好的观点。我添加了一条建议来处理它。 - Ted Lyngmo

12

这一个毫不羞耻地从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来尝试。


6
如果您的头文件有命名空间,我猜您可以在命名空间上使用[[deprecated]]?但这在匿名命名空间中不起作用。而且用户必须使用来自命名空间的某些内容才能使其起作用。
如果您可以将头文件放在命名空间中,则只需具有触发警告的using语句即可。如果这是目标,这也可能是隔离那些函数并确保用户更难使用它们的好方法。
namespace [[deprecated]] N {
    struct S {

    };
}

using N::S;

但是如果你负担不起命名空间,根据元素数量,你可能不想在所有元素上使用 using。也许这可能是合理使用 using namespace N; 的时候,但我不确定。

经过一些研究,你可以使用 #pragma message "Message" 来可能实现你想要的效果。请参见此答案

godbolt

这是一个指向Godbolt网站的链接,该网站提供在线编译器和反汇编器。它可以帮助程序员快速查看他们的代码在不同编译器和处理器上的编译结果。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接