Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!wuarchive!sdd.hp.com!spool.mu.edu!sol.ctr.columbia.edu!caen!uflorida!gatech!mcnc!decwrl!infopiz!lupine!rfg From: rfg@NCD.COM (Ron Guilmette) Newsgroups: comp.lang.c++ Subject: Maintaining header files **and** inline management Message-ID: <3699@lupine.NCD.COM> Date: 4 Feb 91 20:25:08 GMT References: <15917@reed.UUCP> Organization: Network Computing Devices, Inc., Mt. View, CA Lines: 150 In article <15917@reed.UUCP> minar@reed.bitnet (Nelson Minar,L08,x640,7776519) writes: > >In C, I barely managed to keep all my header files straight and organized. >C++ just compounds my problems. Yes. It's a tricky business. Awhile back I invented a simple set of procedures for handling both header files and for dealing reasonably with inline functions. Basically, I try to keep a pair of files (e.g. foobar.C and foobar.h) for each and every class. This convention seem to keep things nice and simple. After I started doing that, I found that an awful lot of the time when I was making some change in the foobar.C file, I'd have to make a related change also in the foobar.h file. I got tired of having to edit one file (e.g. the foobar.C file) and then edit another (e.g. the foobar.h file) and then switch back again, so I invented a system where I could effectively keep all of the stuff from *both* the foobar.C and foobar.H files physically together in one file, thus making editing of related items (e.g. interface and implementation) easier. Basically, my system is this. For each class, I now create one file (e.g. foobar.C). It will look kind of like this: class foobar { ... public: foobar (); ... }; extern int foobar_related_variable; //############################################################## int foobar_related_variable = 0; foobar::foobar () { ... } Both the interface and the implementation are now in one file (which makes editing easier). The first part (i.e. the part above the line of ####'s is the interface part. The lower part is the implementation part. Now the real trick is that my makefiles (which use some special features of GNU make) automatically create or update the foobar.h file from the above foobar.C file on an "as needed" basis. To do this, I have to make use of a special category of files that don't otherwise exist. I call them ".timestamp" files. For each .C file, there is a rule in the makefile that says that there is a corresponding .timestamp file which depends on the .C file. In this example, that would be like: foobar.timestamp: foobar.C (Actually, GNU make lets me use a generic rule like "%.timestamp: %.C".) Anyway, the procedure for making a .timestamp file from a .C file is simple and fast. Basically it just involves some clever uses of grep and awk to find the line of #####'s in the .C file and then hacking off the hunk of the .C file which is above that line. This "interface" part then becomes the .timestamp file. Each time a .timestamp file gets updated (which happens each time the corresponding .C file got modified) an additional check takes place as a side-effect of the creation of the new .timestamp file. The side-effect is that the contents of the new .timestamp file are compared against the current contents of the corresponding .h file (using diff). If there are differences, it indicates that the most recent set of changes I made to the .C file actually *did* affect the "interface" part. In such cases, the (now stale) .h file is replaced by a copy of the current contents of the .timestamp file. (In cases where there was no .h file already, the action is the same as if the .h and .timestamp files were found to be different, i.e. the contents of the .timestamp file are copied into a newly created .h file.) This updating of the .h file causes the "last modification time" of the .h file to get changed. That's good because now make will know that it should re-make anything that depends upon that .h file. Note however that if the last set of changes that I made to my .C file did not cause the upper "interface" part to change, then I will get a new .timestamp file, but its contents will still be the same as the old contents of the .h file, so diff will yield an exit code of zero and thus, the .h file will *not* be touched at all. That's good because as long as I don't modify the "interface" part, I want to avoid unnecessary recompilations of stuff that depends upon just the interface part. This scheme allows me to prevent neadless recompilations from occuring. The neat part is that I actually break down my files into *three* parts like so: class foobar { ... public: foobar (); int inline_member (); int non_inline_member (); ... }; //################################################################ inline int foobar::inline_member () { ... } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% int foobar::non_inline_member () { ... } Note that all inlines function are placed in the middle part. Now, if I want to build a small (or easily debuggable) version of my program, I use -Dinline="" and I have my makfiles treat everything above the line of ###'s as the "interface" part. However, when I want to build a fast production version of my program, I simply delete all of the existing .h files and re-make everything using the line of %%%'s as the dividing line between the interface and implementation parts. In the latter case, everything above the line of %%%%'s goes into the (automatically generated) .h file, and thus, I get all of my inline functions being inlined all over the place. This whole scheme is really trivial to implement using the special "generic rules" features of GNU make. Doing this stuff with some plain vanilla make program would be quite cumbersome. >Are constructs like '#pragma once' just entirely common in C++ headers? I >find them ugly.. I invented #pragma once. It is a non-standard GNU extension to the GNU C preprocessor. It is actually quite useful. What don't you like about it? Just saying that it is "ugly" is not very descriptive. I suppose that I could say the same about you, but that doesn't necessarily mean that you are a bad person. :-) -- // Ron Guilmette - C++ Entomologist // Internet: rfg@ncd.com uucp: ...uunet!lupine!rfg // Motto: If it sticks, force it. If it breaks, it needed replacing anyway.