[vox-tech] C header files question

Tim Riley vox-tech@lists.lugod.org
Thu, 18 Jul 2002 09:10:46 -0700


Jeff Newmiller wrote:

> On Wed, 17 Jul 2002, Peter Jay Salzman wrote:
>
> > during the course of writing a C program, often i'll change an
> > implementation of the way i do things.
> >
> > functions get added and deleted, but often i'll forget what header file
> > was added for a particular function.
> >
> > i know there are header files which aren't needed anymore.
> >
> > is there a utility that lets you know which header files aren't needed
> > for a particular file of C code?
>
> Sounds like something a "lint" or compiler might do, but lclint doesn't
> appear to do this.
>
> I try to keep the total number of includes in any given file to a minimum.
> Commenting a header you suspect of being unnecessary and attempting to
> recompile is an effective solution for small numbers of headers.
>
> One way to minimize headers is to define your own "modules":

>
> +-myio.h------------+
> | int do_input();   |
> | void do_output(); |
> +-------------------+
>
> +-myio.c-------------------------------------+ +-mytoplevel.c------+
> } #include "myio.h"                          | | #include "myio.h" |
> | #include <stdio.h> /* scanf(), printf() */ | | int main() {      |
> | int do_input() { ... }                     | |  do_input();      |
> | void do_output() { ... }                   | |  ...              |
> +--------------------------------------------+ |  do_output();     |
>                                                +-------------------+
>
> stdio.h here is representative of some large, complicated headers
> associated with a powerful library.

You're on the right track by writing modules with
corresponding header files, e.g. my_io.h and my_io.c.
However, I would take this a step further and suggest
writing your modules as abstract datatypes.

An ADT is a module that has a data structure defined
in the .h file and an instantiation function in the .c file. The
instantiation function allocates
the memory for the data structure and then returns a
pointer to it. All of the other functions in the
.c work on this data structure only. (This is how
classes in C++ are supposed to work.)

A good example of an ADT is FILE which is defined
in stdio.h. The instantiation function is fopen(). It
returns a pointer to FILE which you then pass to
fgets(), fseek(), ftell(), and the many other functions
to do the work. Whereas it's interesting to know how
these functions work, it's not necessary.

Usually when you write an ADT you have an object
defined in a single .h file and all the code for that
object written in a single .c file, e.g. foo.h and foo.c.

Example:
Let's say you have person.dat that has delimited records
containing name,address, etc. Then you should have a
corresponding person.h and person.c which work on this
file only.

person.h
---------
#ifndef PERSON_H
#define PERSON_H

typedef struct
{
    char *name;
    char *address;
    char *city;
    /* other information omitted */
} PERSON;

PERSON *new_person( char *person_filename, char *name_to_search );
#endif

person.c
---------
PERSON *new_person( char *person_filename, char *name_to_search )
{
    PERSON *person = (PERSON *)calloc( 1, sizeof( PERSON ) );
    FILE *person_file;
    char input_buffer[ 1024 ];

    if ( ! ( person_file = fopen( person_filename ) ) )
    {
        fprintf(stderr, "Error in %s/%s(): file open error in %s.\n",
                __FILE__, __FUNCTION__, person_filename );
        return (PERSON *)0;
    }

    /* Code to search and populate person structure omitted */

    fclose( person_file );
    return person;
} /* new_person() */

Summary:
By grouping your modules into identifiable categories
and creating an abstract datatype for each module,
your code will start to read like an essay.

Just my humble opinion.

-- Tim Riley

>
>
> While this example may seem trivial, in a large program the fact that only
> myio.c depends on some huge external library can have far reaching
> effects.  One effect is shortened compilation for all the modules that
> depend on myio.h but don't directly include stdio.h.  This also localizes
> the dependency, so if you decide to use <gtk-1.2/gdk/gdk.h> instead of
> <stdio.h>, you don't even have to recompile any of the rest of your object
> modules.  From a complexity control perspective, this is a good thing. :)
>
> If you have more than 5 or 10 includes, you should probably ask yourself
> if you are doing too much in one file.
>
> ---------------------------------------------------------------------------
> Jeff Newmiller                        The     .....       .....  Go Live...
> DCN:<jdnewmil@dcn.davis.ca.us>        Basics: ##.#.       ##.#.  Live Go...
>                                       Live:   OO#.. Dead: OO#..  Playing
> Research Engineer (Solar/Batteries            O.O#.       #.O#.  with
> /Software/Embedded Controllers)               .OO#.       .OO#.  rocks...2k
> ---------------------------------------------------------------------------
>
> _______________________________________________
> vox-tech mailing list
> vox-tech@lists.lugod.org
> http://lists.lugod.org/mailman/listinfo/vox-tech