Notes on Programming in C - Rob Pike, February 21, 1989(doc.cat-v.org) |
Notes on Programming in C - Rob Pike, February 21, 1989(doc.cat-v.org) |
That means that over time I effectively have comments for every line of code in the program, but it's associated metadata instead of being embedded inline, which means that the comments never go out of date, and their history is accurately tracked in version control.
I prefer to have the comments in the code, in a form that can be used to generate documentation on the side (eg. doxygen, epydoc, etc.) so that they can't get lost, and so that you can read them as you read the code. It is difficult to re-combine the git commit messages with the code, but not hard to extract the comments from the code to create documentation.
I, personally, always reread my code in the form of sentences. Something along the lines of: "okay, first I create a x. Then I pass y to it. I then set this flag..."
If at any point I hesitate or have trouble stating what a line of code does, I add these sentences as comments so someone else can understand easily/quickly (if I can't alter the code for some reason, that is).
By contrast, it's nigh impossible to forget to include a commit message, since failure to do so will cancel your commit.
Comments don't take up much space (reading or on disk) so don't think its bad to be verbose.
Nice physical metaphor. Pointers are ... pointy.
Why is maxval a better name than MaximumValueUntilOverflow?
The first lacks some extra information that I need to keep in mind every time I re-read that part of the code.
And while the potential of overflow might be obvious, how about:
minValueForTemperature instead of minval?
Complex variable names ought to be avoided because, simply, they hammer the programmer with a bunch of information every time they are used. Usually, when reading code, you're trying to wrap your head around how a procedure operates rather than the specifics of what it's operating on. Often, if you need to know more about what a variable represents, it's sufficient to refer to its declaration.
Thusly, I prefer to name my variables so that one can pick up the general idea of what they're for from the name and then I document any additional information at the variable's declaration, either using a comment or via the type system.
So, this is how I'd handle your examples:
int maxval; /* until overflow */
Temperature minval;I'd also avoid putting prepositions into variable names, but that's just a personal preference for keeping things to one adjective plus one noun where possible (less parsing overhead for my brain).
If I need a longer variable name to clarify the intent of the code, it's a signal from the code that maybe I should break the routine into smaller pieces.
I also find that smaller variable names makes it way faster to scan and thus understand.
YMMV ...
I highly recommend it.
I'm reminded of Perl when I look at that link pointer code. It's succinct, easy to write, works great most of the time, and nearly impossible to go back and decode later. You simply require too much context to find the one place where you stepped beyond the array and into no-man's land.
I love pointers; I think that like other sharp tools, they have their uses. However, like other sharp tools, they require training and attention to use properly. Lots of people lose fingers to saws every year; how many brain cells have you lost to debugging pointer mistakes?
Just the other day, I was trying to identify what was being done by someone's "idiomatic" code, and had to keep three vertical screen's worth of data up just to follow what was happening. All that code was wrong, but because they were using pointers "idiomatically", it took a long time to find their off-by-one error.
Granted, they had gone about 4 levels further with pointers than the given example in the article, but the premise is similar.
http://en.wikipedia.org/wiki/Pragma_once
His comments on include files are not the way the world has gone. In all my years writing C, I have never seen a C/C++ header which does not include the other headers it needs in order to compile cleanly.
He states: "Simple rule: include files should never include include files."
If you can find me one C project out there which has headers with zero #includes and forces each compilation unit including the header to include all the prerequisites needed for that header I would be genuinely interested. Even all OS header files include their pre-requisites and it is considered a bug if they do not do so. A more modern 21st century rule would be:
"Simple rule: include file order does not matter. Use include guards (or #pragma once) and always include only the prerequisites needed to cleanly compile and nothing more."
There are numerous examples of projects which follow that recommendation. I'm told, for example, that the recommendation on the excellent Blender 3-D graphics system is to avoid nested includes. Other examples:
NASA: http://sunland.gsfc.nasa.gov/info/cstyle.html
European Molecular Biology Open Software Suite (EMBOSS) http://emboss.sourceforge.net/developers/c-standards_new.htm...
Atacama Large Millimeter Array (astronomy): http://www.alma.nrao.edu/development/computing/docs/joint/00...
Clinical Neutron Therapy System (for radiation oncology): http://staff.washington.edu/jon/cnts/impl.pdf
A whole OS does this: Plan 9 from Bell Labs :)
For details see the paper by Rob Pike: How to Use the Plan 9 C Compiler
http://doc.cat-v.org/plan_9/4th_edition/papers/comp
Also the Plan 9 libraries are much cleaner and leaner than those in 'modern' *nix systems, which makes keeping track of includes much easier: http://man.cat-v.org/plan_9/2/intro
I've considered switching to this, but in the interest of maximum hypothetical portability, I've decided to stick with the #ifndef include guard. I've just checked the C1X standard draft I have, and #pragma once is still not in the standard (6.10.6.1 in the version I have).
As for your other points, I agree with the notion that a header file should include its dependencies, but no more. I suppose my thinking on this is influenced by the dependency tracking of Linux package managers such as apt.
> The result is often thousands of needless lines of code passing through the lexical analyzer, which is (in good compilers) the most expensive phase.
If only...
The way that part is worded seems a bit confusing.
#ifndef some_header
#include some_header
#endif #if defined(... all the compilers above ...)
# define PRAGMA_ONCE _Pragma("once")
#else
# define PRAGMA_ONCE
#endif
Specifically, MSVC and HP cc/aCC do not support it. Oracle cc/CC does the #ifndef/#define detection and internally does something similar to what the pragma enables in other compilers.He's right; nested includes are a modern C idiom.
> He's right; nested includes are a modern C idiom.
No doubt they are found a lot. The request was for counterexamples. Fashion/style vs engineering practice assertions may be passing like ships on a nighttime C here.
>...for projects ranging from MongoDB to Ruby and Python...
Python....why, even Tim Peters asserts that "Flat is better than nested"...wink...
http://www.python.org/dev/peps/pep-0020/
/irrelevant quote
In extreme cases, where a large number of header files are
to be included in several different source files, it is
acceptable to put all common #includes in one include file.
I would not trust anything else the author of that guide writes. EMBOSS says not to nest. The Atacama code suggests not nesting but then says: To avoid nested includes, use #define wrappers as follows:
The last pdf states: This prevents including the same item more than once when
a .c file includes several .h files (we rejected
alternatives involving conditional compilation as too
complicated).
I guess it boils down to preference since the language allows you to do either, I just don't put a lot of stock in these style guides. "Include what you use" means this: for every symbol (type,
function variable, or macro) that you use in foo.cc, either
foo.cc or foo.h should #include a .h file that exports the
declaration of that symbol.
I agree 100% with that statement. According to the style, foo.h can never #include anything.