C++ include necessity checker?

Started by
23 comments, last by Hodgman 16 years ago
I'm working on a 100+ file source tree in a personal project and I'm convinced I could seriously improve the compile time by clearing out unnecessary #includes. Is there a tool that could check whether anything is referenced from within a header, if the header has been included previously in the chain, and possibly whether it's just a pointer reference so you could just declare the class instead of including the header? I'm running Linux if that makes any difference.
Advertisement
Yes; it's called 'makedepend'.
While makedepend does kind of do what I want, It's not quite what I'm looking for. Firstly, unless it has functionality not documented in the man-page, it's per-object, not per-file (would be find if I wanted just to clean .cpp files). Its output is also a bit tedious to work with (simply a list of tens or even hundreds of header files for each object file).

Just to clarify, I don't want a compile-time solution, I want to modify the source and header files themselves. For example, if a .cpp file had a function which used my console class for debugging purposes some time ago, but doesn't use it anymore, I'd like to get rid of the #include "console.h" from the top which I'd forgotten to take out. Or an example of one which is potentially more difficult to catch:
#include "b.h"class A {    B *ref;};

I'd like this to be optimised to
class B;class A {    B *ref;};
This hypothetical tool would be very useful to every C++ developer, so if someone has made one I'm sure they're charging money for it ;)

(I'd definitely convince my boss to buy such a tool for our system if it exists; our compile times are currently horrid too! I recently spent an entire fortnight doing nothing but decoupling 4 of my projects from other other 108...)
There's this. It doesn't look like it does exactly what you want, but could be helpful none the less. It's cheap, too.
Interesting, I might make this over summer if I have any time to spare. Certainly doesn't sound like an insurmountable task.

Thanks for the replies guys.
Quote:Original post by DeathCarrot
Interesting, I might make this over summer if I have any time to spare. Certainly doesn't sound like an insurmountable task.


Nothing in C++ looks like insurmountable task.

But then, you start coding...

Quote:Original post by DeathCarrot
Interesting, I might make this over summer if I have any time to spare. Certainly doesn't sound like an insurmountable task.


I've been thinking about a decent include checker for a while, and it seems like a pretty complex task to me.

Starting from the basic capabilities, the first thing I'd want it to be able to do is compile my headers standalone. Of course no one needs a tool for this, you just need a compiler. Run all the headers through a compile or syntax-check pass to make sure they all work independently of each other. You don't want headers that magically depend on other things to be included, so if you find a dependency you have to either resolve it via code or add an #include inside the problematic header.

The feature you want is to be able to detect when you've included headers that aren't doing anything. The way I'd go about it is to make the tool dump symbols defined by each header and symbols required by each source file. This could get pretty complex, and you'd pretty much have to co-opt an existing C++ parser to do it in any sane amount of time with few bugs. Maybe OpenC++ would do the trick, I'm not sure. Anyway, once you have the lists of symbols provided by and symbols needed, it's trivial to determine if a header is pointless.

Suggesting refactors such as forward declaring various classes would be an extra step on top of that, but if you've got a full C++ parser and know how to work with it at that point, this step shouldn't be too hard. A worse way to do the same thing would be to write a tool that just warns any time it finds a class that depends on another class (and thus can't be forward declared) and you could go through and fix them all manually.

My ultimate holy grail of include tools is one step farther than this... it takes the list of symbols provided by and symbols required and uses a heuristic to generate a completely new set of headers. This would be for projects (like Quake) that use the ill-advised technique of using a single header file for everything. I started splitting them up when I was playing with it long ago, but it became a chore and I realized that I'd rather just have an automated tool to do it. This isn't really as much of a problem in C++, but a tool that makes suggestions like "moving symbol x into header y could eliminate Z inclusions" would be awesome.

I eval'd IncludeManager a while back and it's ok. It was pretty slow but it did provide a lot of helpful visual information about headers, including 2 way dependencies and things like "oh we're pulling in 2M of extra headers because we mistakenly included blah.h in blah.cpp." But it didn't live up to my dreams, so I didn't end up buying it. Still it's pretty cheap and might be worth your time, especially with a small project. (100 files is a small project... :) )
Quote:it's trivial to determine if a header is pointless.


Is it?

The problem comes from the fact that there's no constraint anywhere that a header file must be self-sufficient. You can compose a class across several files, and includes are nothing more than plain text files, which are not given any sense even in a generated binary.

They just may contain some instructions that compiler will convert into code - but it's not really a requirement, except for main().

And even a trivial example becomes non-trivial
// class_prolog.hclass CLASS_NAME {// class_body.h#ifdef LINUXCLASS_NAME() {}#elseif UNIXCLASS_NAME(int x = 0) : xx(x) {}#include "something.inl"#endif// class_epilogue.h};// Foo.h#define CLASS_NAME Foo#include "class_prolog.h"#include "class_body.h"#include "class_epilog.h"#undef CLASS_NAME// Bar.h#define CLASS_NAME Bar#include "class_prolog.h"#include "class_body.h"#include "class_epilog.h"#undef CLASS_NAME


Now throw in some conditional compilation....
The only full-proof way to do it is to actually write a C++ compiler. Right now, just think how many ways a symbol can be declared such that it is hard to parse.

This topic is closed to new replies.

Advertisement