Question

Banned.h is a list of ANSI C functions that Microsoft is trying to persuade programmers to deprecate. I already know how to enforce automatic inclusion of banned.h (such as the answer to Ensuring that headers are explicitly included in CPP file). Unfortunately, that answer has an important drawback.

Forced inclusion in project settings always includes the forced header first. Unfortunately, most of Microsoft's own system header files are not banned.h compliant, and many third party library headers are also not banned.h compliant. Microsoft claims that their usage of the banned APIs are safe, and has no intention of changing this situation. The primary way to use the system headers, then, is to include them before banned.h.

One way to solve this is to make a kind of meta_banned.h, which includes all possible system headers that are used anywhere in your project, as well as all the headers for any non-compliant third-party libraries, prior to including the regular banned.h. This is the first thing I did, but it causes an unfortunate hit to compile time because some of the third-party libraries have extensive headers.

We ended up with a hybrid approach--the meta_banned.h includes some system headers, and then the project settings for certain exceptional CPP files include the third-party headers as part of the force-include settings on that particular file. This works, but it's ugly and painful to maintain. The per-CPP-file forced includes in particular seem ugly.

What I really want is a method to ensure that banned.h is included somewhere in each file, not necessarily at the top. As long as it's included at some point, I consider the file compliant. If it's not included, I want to generate a build error. Any suggestions?

Was it helpful?

Solution 2

After much debate, we found a method that we like, but it involved other substantial changes to the project. We turned on pre-compiled headers. Within the pre-compiled header, we first include any and all system headers (and other third-party library headers) that are not compliant with banned.h, and then we include banned.h.

This has a lot of properties that we care about:

  1. It doesn't slow down the build, even when rarely used headers are included. Actually, a full build now takes less than half as long as it previously did, because pre-compiled headers are an optimization.
  2. You can't inadvertently leave out banned.h, because the compiler enforces inclusion of the pre-compiled header. Pre-compiled headers are a whole-project setting in Visual Studio, and individual files have to explicitly opt-out if they don't want to use them. So banned.h is cleanly enforced everywhere.
  3. We can either force-include the pre-compiled header to avoid changing every source file, or we can add it to the top of every source file. This provided a nice migration path. All of our exception rules for particular source files went away.
  4. All the compilers we care about (GNU C++, clang, Visual C++) support pre-compiled headers, and the support is similar enough that it doesn't require #ifdefs or similar hacks.

There is one downside that caught us: If you have mixed C++ and C source files in your project, you will need to have separate banned.h files for them. You can't use the same PCH file for both languages. Since all of our C source was from a third party library, we decided to make a separate lib file for it and link it in further downstream, which solved the problem. Now we don't require any customized project settings for individual source files, the project compiles quickly, and we're fully enforcing banned.h.

OTHER TIPS

Ok, I would never in a million years do this, but you could do something like this:

Force auto inclusion of this:

// forcebanned.h
#define for "You must include banned.h"

Then add this:

// mybanned.h
#undef for
#include banned.h

But don't do that. Better is to make the inclusion of banned.h a coding standard, and rely on code reviews to catch it.

If you are really insistent on enforcing this technically, you could have a custom build step that grep'd files for "banned.h" prior to compilation, failing the build if the line isn't found. (Or use some static analysis tool that tags deprecated functions.) Doing so as part of the build keeps your code simpler, and more portable.

You could, instead of using banned.h, write a Clang analysis program that analyzes your source code. You can use it to enforce whatever coding rules or guidelines you like, not just the banning of a specific set of C functions, and it can also query the source location of any given code and implement location-specific rules.

Licensed under: CC-BY-SA with attribution
scroll top