herr_shamanskiy
herr_shamanskiy

Reputation: 424

How can I deprecate a C++ header?

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.

Upvotes: 39

Views: 5315

Answers (5)

ShadowMitia
ShadowMitia

Reputation: 2533

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

Upvotes: 6

Ted Lyngmo
Ted Lyngmo

Reputation: 117298

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.

Upvotes: 16

francesco
francesco

Reputation: 7539

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.

Upvotes: 45

ComicSansMS
ComicSansMS

Reputation: 54589

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.

Upvotes: 12

HolyBlackCat
HolyBlackCat

Reputation: 96246

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.

Upvotes: 21

Related Questions