Newsgroups: comp.lang.c Path: utzoo!henry From: henry@utzoo.uucp (Henry Spencer) Subject: Re: Prototypes Message-ID: <1988Mar27.040856.19521@utzoo.uucp> Organization: U of Toronto Zoology References: <2550049@hpisod2.HP.COM> <7412@brl-smoke.ARPA> <3351@chinet.UUCP> Date: Sun, 27 Mar 88 04:08:56 GMT > ... Declare it in foo.h... When you write foo.c, be sure to include foo.h... > Indeed, you have stated foo's signature twice. > However, the compiler should reject an attempt to > compile foo.c if the two instances do not match. There is still one headache with this, a more general flaw of foo.h schemes: significant information is still present in two different files, and keeping them in step can be a headache (even if failures are detected at compile time). I've been experimenting with a simple solution to this that works moderately well. It's a variant on the idea of automatically generating function prototypes from the function code (which isn't simple to do and doesn't address the other things that one might want to put in a header). The idea is to imbed the foo.h information in foo.c, marked so that it can be extracted automatically. This still means you have to *write* it twice, but the two instances can be *together*, which makes synchronized changes much easier. The particular marking convention I use is based on an extension of another notation I already use: beginning all lines of a multi-line comment with " *" except for a line in the comment at the head of each function describing the function, which begins with " -". (This isn't original, although I no longer remember exactly who gave me the idea.) So I use "=" to mark a line meant for foo.h. This may be obscure without an example, so here's how I might start an implementation of strcpy() (ignore the leading tabs): /* - strcpy - copy string from b to a = extern char *strcpy(char *a, char *b); * * Using algorithm XYZ to run fast on machine ABC. */ char * strcpy(a, b) char *a; char *b; { /* ... */ A simple "egrep '^ -'" will give me the "definition" lines for all the functions, and a slightly more complex bit of code (see below) will build a suitable header file. Note that I can put *anything* into the header file this way, not just function prototypes. Note also that I can write ANSI prototypes even for a non-ANSI compiler; my header-file builder takes an "old compiler" option that comments out the parameter list. (It also turns C++-style // comments in the = lines into C comments, because otherwise it's hard to get C comments onto such lines.) Without further ado, here's what I call "mkh"; I recommend it. (Oh, ignore the -p option, it's still experimental.) ----------- X# mkh - pull headers out of C source XPATH=/bin:/usr/bin ; export PATH X Xpeel=' /^ ==*[ ]/ /\/\//s;//\(.*\);/*\1 */; X s/^ ==*[ ]//' X Xegrep='^ =[ ]' X Xfiles= Xfor a Xdo X case "$a" X in X -o) # old (pre-function-prototype) compiler X peel='/^ ==*[ ][^#]/{ /^\([^\/]*[a-zA-Z0-9_)]\)(\(.*\))/s;;\1(/*\2*/); X } X '"$peel" X echo '#ifdef __STDC__' X echo '#error "header file prepared with mkh -o"' X echo '#endif' X ;; X X -p) # include private declarations X egrep='^ ==*[ ]' X ;; X X *) X files="$files $a" X ;; X esac Xdone X Xfor f in $files Xdo X egrep "$egrep" $f | sed "$peel" Xdone ----------- -- "Noalias must go. This is | Henry Spencer @ U of Toronto Zoology non-negotiable." --DMR | {allegra,ihnp4,decvax,utai}!utzoo!henry