tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: KNF and the C preprocessor



On Mon, Dec 10, 2012 at 07:37:14PM +0000, David Laight wrote:
> On Mon, Dec 10, 2012 at 09:36:35AM -0600, David Young wrote:
> > What do people think about setting stricter guidelines for using the
> > C preprocessor than the guidelines from the past?  Example guidelines:
> ...
> > 4 Computed constants.  The result of a function call may not be used
> >   in a case-statement, even if the function evaluates to a constant at
> >   compile time.  You have to use a macro, instead.
> 
> The alternative to constants would be C enums.
> However C enums are such 2nd class citizens that they have problems
> of their own.

I'm not sure you mean quite the same thing.  An example of what I mean
by "computed constant" would be something like f(Y) where Y is some
other constant and f(X) can always be evaluated to a constant at compile
time: f() may not be a function, not even a static/inline function, if
f(Y) appears in a case statement.

(Actually, if f() is an inline function and the compiler optimization
level is turned up, GCC will let you put f(Y) in a case statement.  Turn
the optimization level down, though, and you get a compile error.)

> > The C preprocessor MUST NOT be used for
> > 
> > 1 In-line code: 'static inline' subroutines are virtually always better
> >   than macros.
> 
> That rather depends on your definition of better.

It comes down to the ease of reading/understanding/writing a macro like

        #define M(x, y)                                         \
        do {                                                    \
                ...                                             \
                ...                                             \
                ...                                             \
        } while (0)

when something like

        static inline void
        M(int x, int y)
        {
                ...
                ...
                ...
        }

will do.  The guideline can be re-phrased, "reach for a function
before a hairy macro; use a hairy macro only when nothing else will
do."  When I say "hairy macro" I mean one like WM_INIT_RXDESC() in
sys/dev/pci/if_wm.c: the extra underscores, parens, and backslashes
badly clutter the code.  Was the same code written as a static or static
inline function, first, found wanting, and converted to a macro?  Or was
the author in the habit of using a macro, first?  I'm pretty sure that
the code is a macro for the latter reason.

> a) #define macros tend to get optimised better.

Better even than an __attribute__((always_inline)) function?

> b) __LINE__ (etc) have the value of the use, not the definition.

I certainly don't want to rule out the careful use of __LINE__ or
__func__.

> > 2 Configuration management: use the compiler & linker to a greater
> >   extent than the C preprocessor to configure your program for your
> >   execution environment, your chosen compilation options, et cetera.
> 
> Avoiding #ifdef inside code tends to be benefitial.
> But, IMHO, there isn't much wrong with using #defines in header files
> to remove function calls.

Example?

> Using the compiler gets to be a PITA because of the warning/errors
> about unreachable code.

I wrote the guidelines in 2010 and they sat in a draft form ever since.
I no longer remember what I had in mind when I wrote "compiler" above.

> > 3 Virtually anything else. :-)
> 
> There are some very useful techniques that allow a single piece of
> source to be expanded in multiple ways.

I don't disagree.  I don't want to discourage the use of the C
preprocessor altogether, just to make sure its use is measured against
the potential headaches.

Dave

-- 
David Young
dyoung%pobox.com@localhost    Urbana, IL    (217) 721-9981


Home | Main Index | Thread Index | Old Index