[comp.lang.c++] Maintaining header files **and** inline management

rfg@NCD.COM (Ron Guilmette) (02/05/91)

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.

loic@axis-design.fr (Loic Dachary) (02/07/91)

	An what about protoize ? Do you use it in the process you 
describe ? Will protoize ever compile with and up-to-date gcc ?

	Please to not consider this an agression. I'm just curious.

	Thanks,
			Loic
-- 
Loic Dachary 	loic@axis-design.fr or floic@afp.uucp 
Voice		+33 1 40 35 20 20

rfg@NCD.COM (Ron Guilmette) (02/11/91)

In article <LOIC.91Feb7074009@axis-design.fr> loic@axis-design.fr (Loic Dachary) writes:
>
>	An what about protoize ? Do you use it in the process you 
>describe ? Will protoize ever compile with and up-to-date gcc ?

I've already requested (in one of the GNU newsgroups) for people who
are interested in pre-testing protoize/unprotoize version 1.39.0 to
contact me via E-mail.

I have about 5 people who are supposed to be pre-testing the new version
now, but I've only gotten actual feedback from two of them so far.

If anybody else is interested in helping me pre-test protoize/unprotoize
version 1.39.0, please send me mail.  Note that you must be ready, willing,
and able to build the GNU C compiler (GCC) also since neither protoize
nor unprotoize can work without GCC.

Oh, yes!  You must also have access to the Internet (so that you can do
anonymous FTP's) in order to be a pre-tester.

-- 

// 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.

gwu@nujoizey.tcs.com (George Wu) (02/13/91)

In article <3699@lupine.NCD.COM>, rfg@NCD.COM (Ron Guilmette) writes:
|> 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.
|> 
|> [ a description of a system of generating header files from .C files
|>   deleted here for the sake of brevity ]

     I like Ron's method of generating header files from the .C file.  We
too have tried to do something similar, but rather generated the .C file
from the header file.  Obviously, this created problems since it's easier
to generate function prototypes from implementation code than to
automatically retrofit implementation code to new function prototypes.  We
eventually gave up this idea.

|> 
|> 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.

     For reasons into which I which I won't go, we didn't like GNU make.  I
think I'll give this method a try with imake.  Can anyone imagine any
problems I might encounter with imake?

							George

PS: For those unfamiliar with imake, it's an X utility program.  Imake
generates a makefile from a generic description and is used to generate
machine specific makefiles in the X source distribution.

----
George J Wu, Software Engineer        | gwu@tcs.com or uunet!tcs!gwu
Teknekron Communications Systems, Inc.| (415) 649-3752
2121 Allston Way, Berkeley, CA, 94704 | Quit reading news.  Get back to work.

gwu@nujoizey.tcs.com (George Wu) (02/13/91)

In article <1991Feb5.180503.24515@mathcs.sjsu.edu>,
horstman@mathcs.sjsu.edu (Cay Horstmann) writes:
|> In fact, the AWK script is very fast (although maybe Ron's method of
|> running diff on a header portion is faster) and I just rerun it with
|> each recompile. 
|> 
|> It doesn't matter much what method you or your shop uses for building .h
|> files, as long as it is not the manual "gosh, I guess it is time to look
|> at that .h file again" method. 

     Won't generating a new .h file with each re-compile of the .C file
cause *all* other .C files dependent upon the interface to be recompiled?
Ron preserves the original file, and hence the last modification date make
uses to determine if a recompile is necessary.  This would save a lot of
time in complex applications.

							George

----
George J Wu, Software Engineer        | gwu@tcs.com or uunet!tcs!gwu
Teknekron Communications Systems, Inc.| (415) 649-3752
2121 Allston Way, Berkeley, CA, 94704 | Quit reading news.  Get back to work.

rfg@NCD.COM (Ron Guilmette) (03/03/91)

In article <1746@tcs.tcs.com> gwu@nujoizey.tcs.com (George Wu) writes:
+In article <1991Feb5.180503.24515@mathcs.sjsu.edu>,
+horstman@mathcs.sjsu.edu (Cay Horstmann) writes:
+|> In fact, the AWK script is very fast (although maybe Ron's method of
+|> running diff on a header portion is faster) and I just rerun it with
+|> each recompile. 
+|> 
+|> It doesn't matter much what method you or your shop uses for building .h
+|> files, as long as it is not the manual "gosh, I guess it is time to look
+|> at that .h file again" method. 
+
+     Won't generating a new .h file with each re-compile of the .C file
+cause *all* other .C files dependent upon the interface to be recompiled?
+Ron preserves the original file, and hence the last modification date make
+uses to determine if a recompile is necessary.  This would save a lot of
+time in complex applications.

George has hit upon the real essence of my "tricky Makefile" scheme.  Using
this scheme allows you to avoid unnecessary recompilations in much the
same way as an Ada "library manager" tool would allow you to avoid
unnecessary recompilations of Ada compilation units.

(Please excuse my use of the `A-word' in this newsgroup.  I know that the
mere mention of it makes certain people violently ill, and I'll try to
avoid using it in future. :-)

-- 

// Ron Guilmette  -  C++ Entomologist
// Internet: rfg@ncd.com      uucp: ...uunet!lupine!rfg
// New motto:  If it ain't broke, try using a bigger hammer.