38

I would like to deprecate a C++ header so that if someone includes it in their code, the compiler issues a warning.

I know that I can deprecate individual symbols, for example, using C++14 [[deprecated]], but is there a similar thing for headers? Maybe some clever trick?

Note that I want the compiler to issue a warning even if the user doesn't use anything from the header.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
herr_shamanskiy
  • 414
  • 4
  • 9
  • 13
    `#warning Do not disturb the giant.` – Eljay Jul 08 '21 at 18:30
  • 9
    Worth noting that `#warning` technically [isn't portable](https://stackoverflow.com/q/171435/3282436). – 0x5453 Jul 08 '21 at 18:35
  • Yeah, my bad, I should have specified that I am looking for a solution for the MSVC, GCC and Clang compilers. It doesn't have to be the same solution but I would like to have something for each compiler. So far it seems that `#warning` doesn't work for MSVC. – herr_shamanskiy Jul 08 '21 at 18:43
  • 2
    Do you want a warning at max? Because there is `#error`. – Hatted Rooster Jul 08 '21 at 19:10
  • 1
    Not just "technically" unportable. By that, I mean, it's not merely an abstract concern only of interest to purists and dogmatists. MSVC will *error* if your code includes `#warning`, which makes it not a valid choice in most circumstances. Use a `#pragma` instead; that's supported much more widely and won't ever result in an error per the standard. Something like [Hedley](https://nemequ.github.io/hedley/) wraps this up for you in a compiler-agnostic public interface. – Cody Gray - on strike Jul 09 '21 at 04:43
  • Strongly related: [Is there a way to deprecate a namespace?](https://stackoverflow.com/questions/22806878/is-there-a-way-to-deprecate-a-namespace) and [C++ mark as deprecated](https://stackoverflow.com/questions/295120/c-mark-as-deprecated) – Cody Gray - on strike Jul 09 '21 at 04:48

5 Answers5

44

Here is a possible (albeit perhaps not too elegant) solution.

Insert in the header a code like that

// 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;
}

This creates a deprecated variable badheader_hpp_is_deprecated. The initialization of please_dont_use_badheader_hpp triggers the deprecated warning. Notice that I put both variables inside an anonymous namespace, to avoid possible name conflicts. Still, as noted in the comment, a name clash could still happen if, in the same compilation unit, a variable with the same name is declared inside the anonymous namespace. For this reason, as suggested in the comments, variables in the code above have a descriptive name, rendering name clash high improbable.

francesco
  • 7,189
  • 7
  • 22
  • 49
  • 1
    Make it even more improbably by stashing the anonymous namespace inside the namespace where all the headers symbols are located. – Deduplicator Jul 10 '21 at 13:23
21

A non-standard, but fairly portable solution:

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

This is accepted by GCC, Clang, and MSVC. GCC and Clang also accept the form without ( ).

This is not necessarily a "warning", but should be good enough.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
16

I suggest surrounding your namespace with a namespace with the same name and using it from within the enclosing namespace.

#pragma once

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

This shouldn't taint your global namespace with anything from your_namespace and still give the warning if you include the header. The old stuff will still be accessible via your_namespace:: as before.

Since this is an ABI breaking change, I recommend stepping the major version of the library too if you didn't already when deprecating the header.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • 3
    That is an ABI-breaking change though. Which isn't that good an idea especially if the aim is gradually phasing out cruft. – Deduplicator Jul 10 '21 at 13:25
  • 1
    @Deduplicator True to a degree. The breakage hits once (when the header becomes deprecated) and it hits when you decide to upgrade to this new version of the library. Recompiling your code wouldn't be a big ask. – Ted Lyngmo Jul 10 '21 at 13:36
  • 3
    Yes. If your code is the executable, you have control over upgrading the dependency, and there are no 3rd party libraries interacting with that code. Otherwise, it gets hairy. – Deduplicator Jul 10 '21 at 13:44
  • 1
    Deduplicator Very good point. I added a recommendation to deal with it. – Ted Lyngmo Jul 10 '21 at 14:00
12

This one is shamelessly copied from the range-v3 library:

#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!")

Try it with Compiler Explorer.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
6

If your header has a namespace, you could use the [[deprecated]] on it I guess? But this doesn't work on anonymous namespaces. And the user has to use something from the headspace for it to work.

If you can put the header in a namespace, then all you have to do is have a using statement that will trigger the warning. This could be also a good idea to isolate those functions, and making sure users have more difficulty using them if that's an objective.

namespace [[deprecated]] N {
    struct S {

    };
}

using N::S;

But if you can't afford the namespace, depending on the number of elements, you probably don't want to use a using on all of them. Maybe that could be a case for legitimately having a using namespace N;, but I'm not sure.

After some research, you could use #pragma message "Message" to possibly also achieve what you seem to be wanting. See this answer

godbolt

Federico Navarrete
  • 3,069
  • 5
  • 41
  • 76
ShadowMitia
  • 2,411
  • 1
  • 19
  • 24