An excellent and succinct article by Michael Barr entitled “What Belongs in a C .h Header File?” triggered the thoughts I am about to write down, for he forgot to mention comments as worthy items to be included. When conducting Advanced C courses, as I sometimes do, I have always emphasised the need for usage comments in header files. Header files provide the user’s interface to the implementation in the corresponding .c files, but the latter are often uninteresting to the user and may not even be available. Proprietary libraries, for example, are typically shipped in object form with their header files. It follows, therefore, that any comments which might be valuable to the user should be in the header file.
As Michael implies, the most important items in a good header file are the prototypes of the functions which the user must call to access the corresponding code. These, above all, should be commented to tell the user what can be done with them. You know the sort of thing: pretty, formatted comments surrounded by asterisks, or whatever. We hate to have to write them but we know that they matter. Yet, how often do you see them in a header file? Quite often, they are put into the source file, with the function definitions, instead. Not much use there if the source file is unavailable to the user. And even if the source file is available, it’s a distraction for the user to have to wade through it.
But simplistic recommendations have their downsides. Here are some personal musings on commenting code:
Commenting C programs
Here, the simplest principles work well:
- Put usage comments in the header file.
- Don’t put irrelevant implementation comments here.
- Put implementation comments (plus the private usage comments for static functions) in the source file.
- Think carefully about whether public usage comments should be duplicated here, because of the danger of inconsistencies which can misdirect maintenance. It is only marginally less convenient for a maintainer to have the header file open in a separate window than it is to scroll around the source file (or open a separate view of it). I no longer duplicate the public usage comments.
- Pay attention to the fine line between under-commenting and over-commenting.
- Think first about writing self-documenting code.
Commenting C++ programs
The simple principles described above, for C, go sadly awry when if comes to C++. A C++ header file usually has much more in it than a simple list of global function prototypes. Typically it will contain a single class declaration but there may sometimes be a need to declare more than one class in a file, and there may even be classes declared within classes. Add to this the possibility of namespace declarations and the high probability, in many embedded systems, of extern “C” constructs and we see that a C++ header file has a much more complex structure that does a C header file. It is important for the user of the class(es) to be able easily to understand this structure and to be able to see at a glance, for example, what is public or protected (i.e. of direct interest to a user) and what is private (i.e. of interest only to the designer/maintainer and to the compiler).
Putting a lengthy, fancy function description in front of each method prototype in the class, even with appropriate indentation, makes it much more difficult for the user to discern the structure and to see the ancillary information referred to above.
So, what to do? I’ve used several approaches but I’ve yet to settle on a “best” one. Indeed, the concept of “best” hinges to some extent on the approach, if any, taken with regard to external code documentation – a subject for another article.
In the meantime, here are my thoughts:
- Attribute comments must go into the class declaration; it’s the only chance we get (except for static attributes, to be pedantic). This is not really a problem as such comments are usually one-liners.
- If lengthy, as they usually are, method comments should not be present in the class declaration but should appear later in the header file:
- All inline methods (and for templates, all methods) should be defined in the header file but outside of the class declaration. Each one can then be preceded by its description.
- Each public or protected method whose implementation cannot be in the header file should nevertheless have its description in the header file. Ideally, this description would precede a second, “freestanding” prototype of the method – e.g.
MyReturnType MyClass::myMethod(MyParamType myParam);
- but such a prototype declaration is not permitted. However, a pseudo-prototype like this can be included within the comment itself. I’m not entirely happy with this as there is the danger, once again, of inconsistencies developing. However, I’m much less happy about putting the comment out of sight (and possibly out of reach) in the source file, instead.
- If their implementations are not required to be in the header file, private methods could have their descriptions relegated to the source file. However, my preference with C++, to minimise confusion, is to put all usage comments in the header file. We have to do that for most private attributes, anyway.
Feedback very welcome!