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.
The creation of header files are needed generally while writing large C programs so that the modules can share the function definitions, prototypes etc. Function and type declarations, global variables, structure declarations and in some cases, inline functions; definitions which need to be centralized in one file.
Header files serve two purposes. System header files declare the interfaces to parts of the operating system. You include them in your program to supply the definitions and declarations you need to invoke system calls and libraries.
These are the directories that gcc looks in by default for the specified header files ( given that the header files are included in chevrons <>); 1. /usr/local/include/ --used for 3rd party header files. 2. /usr/include/ -- used for system header files.
You request to use a header file in your program by including it with the C preprocessing directive #include, like you have seen inclusion of stdio.h header file, which comes along with your compiler.
In those cases, only the header name is subject to deprecation, not the contents. Note: you can deduce which headers are subject to this kind of deprecation by matching their names in the document you link to. Obviously, this only works for standard headers.
The Microsoft-specific __declspec (deprecated) declaration modifier is also a better choice in many cases than the deprecated pragma. The [ [deprecated]] attribute and __declspec (deprecated) modifier allow you to specify deprecated status for particular forms of overloaded functions.
[ [deprecated ("Replaced by bar, which has an improved interface")]] void foo (int); The message must be a string literal. For further details, see “Marking as deprecated in C++14”. Can you use [ [deprecated]] in a macro?
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.
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.
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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With