1. Allow including only with a special definition
This solution has already been proposed by @KamilCuk. In essence, error if the header was included without some I_KNOW_WHAT_I_AM_DOING_AND_WANT_TO_INCLUDE_BIG_HEADER_H
definition prior to the #include
.
Of course, an incredibly stupid, or malicious developer can bypass this restriction and still include the header.
2. Hide the header as well as possible
You can locate the header inside a secret folder in your include/
directory, or just outside of it. If the headers of your library are located in mylib/include
, you can put the header in:
mylib/include/mylib/implementation_secrets/BigHeader.h
, which makes it possible for your headers to include it, but would make it less convenient for other developers to do it
mylib/secret_include/BigHeader.h
, in which case you can include it via relative paths from other headers, e.g. #include "../../secret_include/BigHeader.h"
Of course, a malicious developer can still include it, even if it's in a weird place.
3. Force linker errors
You can exploit the fact that external symbols can only be defined in one translation unit, unless they are inline
:
// BigHeader.h
int YOU_MUST_NOT_INCLUDE_ME_YOU_FOOL = 0;
The result is that if BigHeader.h
is included in more than one place, you will get a linker error. Obviously, this is only somewhat useful, because it also breaks you own code if you include it more than once.
4. Whitelist source files
You can use GCC's __BASE_FILE__
macro which expands to the name of the cpp file which is currently compiled. Force a compiler error if this is not what you expect.
// BigHeader.h
constexpr std::string_view includer = __BASE_FILE__ ;
static_assert(includer == "blessed_file.cpp",
"Cannot include BigHeader.h from " __BASE_FILE__);
However, even with this method, a malicious or stupid developer can rename their source file to blessed_file.cpp
and bypass this restriction.
5. Count includes
Perhaps the most over-engineered solution yet:
// BigHeader.h
inline int count_include() {
constexpr int max_includes = 3;
static int counter = 0;
if (++counter > max_includes) {
throw ":("; // TODO: throw an exception, call std::terminate, etc.
}
return counter;
}
// dynamic initialization, one function call per including TU
static const int call_count_include = count_include();
In every cpp file which includes BigHeader.h
, the definition of count_include()
is always the same, and the same counter is incremented.
However, the definition of call_count_include
is unique for each cpp file, and count_include()
will be called once for each.