Reputation: 424
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
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
Upvotes: 6
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
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
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
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