Subject: Re: a new KNF (and some comments)
To: None <lukem@cs.rmit.edu.au>
From: Jason Thorpe <thorpej@nas.nasa.gov>
List: tech-misc
Date: 01/20/2000 17:26:28
On Fri, 21 Jan 2000 11:50:29 +1100
Luke Mewburn <lukem@cs.rmit.edu.au> wrote:
> Well, there seems to be considerable debate on what the KNF should be.
> Some people seem to believe that we should totally revise the coding
> style, some want it updated to ANSI, some don't want to change at all.
>
> Here's what I propose:
[snip]
Luke, I really appreciate your taking the time to do that. I understand
that it's a pain when dealing with coding style, because it really is
like the Crusades for some people (including myself; I'm glad I agree
with most of this stuff :-)
I do have a few change requests, tho...
> #include <sys/cdefs.h>
> #ifndef lint
> __COPYRIGHT("@(#) Copyright (c) 1999\n\
> The NetBSD Foundation, inc. All rights reserved.\n");
> __RCSID("$NetBSD$");
> #endif /* not lint */
Actually, I think the __COPYRIGHT() and __RCSID() should be changed in the
header files to provide their own ;'s, and the source and guide updated
appropriately. This way if you conditionally compile out the RCS IDs, the
compiler won't choke on you (nor will lint(1)).
> /*
> * ANSI function declarations for private functions (i.e. functions not used
> * elsewhere) go at the top of the source module. Only the kernel has a name
> * associated with the types. I.e. in the kernel use:
> * void function(int a);
> * in user-land use:
> * void function(int);
Please nuke the "kernel version" of that. As I recently discovered, this
can have somewhat annoying side-effects if you're scanning a tree with
e.g. id-utils for an instance of a global variable, and function prototypes
have an argument in them of the same name.
> /*
> * Macros are capitalized, parenthesized, and should avoid side-effects.
> * If they are an inline expansion of a function, the function is defined
> * all in lowercase, the macro has the same name all in uppercase. If the
> * macro needs more than a single statement, use do { ... } while (0), so
> * that * a trailing semicolon works. Right-justify the backslashes; it
> * makes it easier to read.
> */
> #define MACRO(x, y) do { \
> variable = (x) + (y); \
> (y) += 2; \
> } while (0)
Could we change that to read:
#define MACRO(x, y) \
do { \
variable = (x) + (y); \
(y) += 2; \
} while (0)
I find it a little easier to quickly find beginnings and ends of macro
bodies that way... and it makes it a bit more consistent in the presence
of widely-varying macro name lengths.
> /* Enum types are capitalized. */
> enum enumtype {
> ONE,
> TWO
> } et;
>
> /*
> * When declaring variables in structures, declare them sorted by use, then
> * by size, and then by alphabetical order. The first category normally
> * doesn't apply, but there are exceptions.
> * XXX: change the above
> * Each variable gets its own line.
> * Attempt to line-up the entries, using appropriate tabs.
I think we should also put a blurb in here about bitfields. I find it
hard to keep track of the following:
struct foo {
u_int8_t bar:1;
u_int8_t baz:5;
u_int8_t zap:2;
};
I think that should be written as:
struct foo {
u_int8_t bar:1,
baz:5,
zap:2;
};
...to drive home that they're all part of the same byte.
> while ((ch = getopt(argc, argv, "abn")) != -1)
...in the case of large while(), if(), etc. bodies which technically don't
require { }'s, I personally like to add them for clarity. I generally define
"large" as "more than a few lines".
> switch (ch) { /* Indent the switch. */
> case 'a': /* Don't indent the case. */
> aflag = 1;
> /* FALLTHROUGH */
> case 'b':
> bflag = 1;
> break;
> case 'n':
> num = strtol(optarg, &ep, 10);
> if (num <= 0 || *ep != '\0')
> errx(1, "illegal number -- %s", optarg);
> break;
> case '?':
> default:
> usage();
> /* NOTREACHED */
> }
> argc -= optind;
> argv += optind;
>
> /*
> * Space after keywords (while, for, return, switch). No braces are
> * used for control statements with zero or only a single statement.
> *
> * Forever loops are done with for's, not while's.
> */
> for (p = buf; *p != '\0'; ++p);
I've found bugs related to extra ;'s at the end of statements like this.
How about:
for (p = buf; *p != '\0'; ++p)
/* nothing */ ;
? For clarity.
> /* No spaces after function names. */
> if (error = function(a1, a2))
> exit(error);
That should read:
if ((error = function(a1, a2)))
but I personally prefer:
if ((error = function(a1, a2)) != 0)
...for additional clarity.
Unfortunately, not all of these suggestions can be implemented with
indent(1). However, for new code, I think these few changes will help
code clarity quite a bit; I certainly find them helpful in my own code.
-- Jason R. Thorpe <thorpej@nas.nasa.gov>