Prototyping in an imperfect world....

Karl Heuer karl at haddock.ima.isc.com
Wed Jul 4 04:28:00 AEST 1990


[Note: To conserve vertical space I've paraphrased heavily and eliminated some
newlines from C code examples; the style questions herein deal with the use of
the macros, not the whitespace convention.  --kwzh]

In article <MaWxonu00as9AA2kM1 at andrew.cmu.edu> ghoti+ at andrew.cmu.edu (Adam Stoller) writes:
>[Prototypes are good, but it's also good to have code that's sufficiently
>portable that it can be compiled on a pre-ANSI implementation of C.]
>About the only way I've seen people deal with it was by doing something like:
>[#1]
>	#ifdef __STDC__
>	foo(int x, char c, long d)
>	#else
>	foo(x, c, d) int x; char c; long d;
>	#endif
>
>[#2: A proposed solution using a bunch of macros that expand into the correct
>keywords and punctuators in the two C universes:]
>	int foo PP_((x, c, d)) int x S_  char c S_  long d E_ { ... }

Yuck.  In my opinion, #2 is worse than #1; the improvement in maintainability
by having only one set of declarations is more than offset by the pain of
having to use what is, in effect, a new language.  Consider instead...

#3: Use prototypes only for declarations, and continue to use the obsolescent
syntax for definitions (but with a declaring prototype, preferably in a
header, already in scope).
	#ifdef __STDC__
	extern int foo(int, int, long);
	#endif
	int foo(x, c, d) int x; int c; long d; { ... }
Since Classic C can't pass `char' by value, the middle argument should
actually be declared `int' to minimize surprises.

#4: Just use prototypes.  On a system with no ANSI compiler, use
	deproto <foo.c >temp.c; cc -c temp.c; mv temp.o foo.o; rm temp.c
or something equivalent.  (This can be tucked into a makefile, or a shell
script that emulates `cc'.)  This is the approach that I use myself; it also
has the advantage that, when ANSI is ubiquitous and nobody cares about Classic
C anymore, the maintenance programmer won't have to do a thing.

The drawback, of course, is that you have to have a deprotoizer.  One such is
`unprotoize', which hangs off of gcc.  My preference is to maintain one that
is entirely self-contained (it doesn't even require lex), and is short enough
that you could type it in from the hardcopy listing if you had to.

The bad news is that, in order to meet this latter requirement, the language
it accepts is trimmed to the bone.  It doesn't handle arguments of type array
or function, or pointers to such, but the former are useless and the latter
are too rare to be worth the trouble.  A more serious problem is that it's
tuned to my personal coding style.  I hope to get back to it someday and make
it less picky about horizontal whitespace at least, but for now it's provided
as-is.  (Send me any improvements, but keep the program small.)

There's a compile-time debugging option that will make it indicate at what
point the attempted parse failed on each line.

Karl W. Z. Heuer (karl at kelp.ima.isc.com or ima!kelp!karl), The Walking Lint
--------cut here--------
/*
 * deproto 1.00 by Karl Heuer.  public domain.
 *
 * expects a prototype to match one of these two patterns:
 *   decl ::= \t \q*\i(\t` \q+'?`, \t` \q+'?'*`, ...'?);
 *   defn ::= \t \q*\i(`void|\t \q*\i`, \t \q*\i'*'`, ...'?) {
 * `' denotes grouping, | alternation, * Kleene closure, + positive closure,
 * ? option, \i identifier, \t type (single word or known multi-word type,
 * optionally preceded by a storage class); \q type qualifier or literal star;
 * other characters literal.  examples:
 *   int main(int argc, char **argv) {
 *   extern int rand(void);
 *   static int myprintf(char const *fmt, ...) {
 */
#include <stdio.h>
#include <string.h>

#define MAXLINE 2048
#define MAXARGS 32

static char buf[MAXLINE];
static char *larg[MAXARGS], *marg[MAXARGS], *rarg[MAXARGS];

#define isword(c) (c>='a'&&c<='z'||c>='A'&&c<='Z'||c>='0'&&c<='9'||c=='_')
#if defined(DEBUG)
#define doskip(k) { fprintf(stderr, "%d <%.*s|%.*s>\n", k, p-buf, buf, strlen(p)-1, p); goto skip; }
#else
#define doskip(ignore) goto skip
#endif

static int skiptype(pp) register char **pp; {
#define p (*pp)
    static char *known[] = {
	"short int", "long int", "unsigned short int", "unsigned long int",
	"unsigned int", "unsigned char", "unsigned short", "unsigned long",
    };
    register char **k;
    for (k = known; k < known+sizeof(known)/sizeof(char *); ++k) {
	register int len = strlen(*k);
	if (strncmp(p, *k, len) == 0 && !isword(p[len])) {
	    p += len;
	    return (1);
	}
    }
    if (strncmp(p, "struct ", 7) == 0) p += 7;
    else if (strncmp(p, "union ", 6) == 0) p += 6;
    else if (strncmp(p, "enum ", 5) == 0) p += 5;
    if (!isword(*p)) return (0);
    do ++p; while (isword(*p));
    return (1);
#undef p
}

static char *skipstar(p) register char *p; {
    for (;;) {
	if (*p == '*') ++p;
	else if (strncmp(p, "const ", 6) == 0) p += 6;
	else if (strncmp(p, "volatile ", 9) == 0) p += 9;
	else return (p);
    }
}

main() {
    char *p;
    register int i, n;
    register int isdecl, isdefn;
    while (fgets(buf, MAXLINE, stdin) != NULL) {
	p = buf;
	if (strncmp(p, "extern ", 7)==0 || strncmp(p, "static ", 7)==0) p += 7;
	if (!skiptype(&p)) doskip(0);
	if (*p++ != ' ') doskip(1);
	p = skipstar(p);
	if (!isword(*p)) doskip(2);
	do ++p; while (isword(*p));
	if (*p++ != '(') doskip(3);
	n = 0;
	isdecl = isdefn = 1;
	for (;;) {
	    larg[n] = p;
	    if (strncmp(p, "register ", 9) == 0) p += 9;
	    if (!skiptype(&p)) doskip(4);
	    if (*p != ' ') {
		isdefn = 0;
	    } else {
		++p;
		p = skipstar(p);
		if (!isword(*p)) isdefn = 0;
	    }
	    marg[n] = p;
	    while (isword(*p)) ++p;
	    rarg[n++] = p;
	    if (*p != ',' || p[1] != ' ') break;
	    p += 2;
	    if (*p == '.' && p[1] == '.' && p[2] == '.') { p += 3; break; }
	}
	if (*p != ')') doskip(5);
	if (isdecl && (p[1] == ';' || (p[1] == ' ' && p[2] == '{' && strncmp(larg[0], "void)", 5) == 0))) {
	    printf("%.*s%s", larg[0]-buf, buf, p);
	} else if (p[1] == ' ' && p[2] == '{' && isdefn) {
	    printf("%.*s", larg[0]-buf, buf);
	    for (i=0; i<n; ++i) {
		if (i != 0) printf(", ");
		printf("%.*s", rarg[i]-marg[i], marg[i]);
	    }
	    printf(") ");
	    for (i=0; i<n; ++i) printf("%.*s; ", rarg[i]-larg[i], larg[i]);
	    printf("%s", p+2);
	} else {
	skip:
	    printf("%s", buf);
	}
    }
    exit(0);
}



More information about the Comp.lang.c mailing list