[comp.lang.c] full lint

jwf@munsell.UUCP (Jim Franklin) (02/24/88)

Summary: you can use some simple #define macros to let lint do full type
checking on /*VARARGS*/ procedure calls with (keyword, value) parameters.
Read on (about 100 lines) for the details ...

We have some procedures that take a lot of optional arguments.  We
clearly want to use varargs.h, and we like the style used by SUN in the
various windowing functions, i.e., a zero-terminated list of (keyword,
value) pairs.  The (keyword, value) pairs improve readability and permit
procedure parameters to be specified in any order.  For example,

        #define PIC_COLOR_MODEL_VALUE   1
        #define PIC_ADDR_VALUE          2
        #define PIC_ACTIVE_IMAGES_VALUE 3

        enum color_model_e {
                color_xyz, color_abc
        };
        #define ACTIVE_X                1
        #define ACTIVE_Y                2
        #define ACTIVE_Z                4

        main ()
        {
                char           *pic_addr = NULL;

                alloc_picture (PIC_COLOR_MODEL_VALUE, color_xyz,
                               PIC_ADDR_VALUE, pic_addr,
                               PIC_ACTIVE_IMAGES_VALUE, ACTIVE_X + ACTIVE_Y,
                               0);
        }

The problem with this is that because alloc_picture() must be declared
/*VARARGS*/, you lose all type checking from lint.  This makes errors in
the calling parameters very difficult to find.  However, with some #define
trickery, I found that you can have varargs and keyword parameters and
type checking, with no runtime penalty.

You need one #define macro for each (keyword, value) pair, and a collection
of functions of the form

<type>
<type>_arg (x)
<type>  x;
{
        return (x);
}

where <type> is the data type of your parameters (int, char *, etc.).
For the example above, you define

        #ifndef lint

        #define PIC_COLOR_MODEL(x)      PIC_COLOR_MODEL_VALUE, (x)
        #define PIC_ADDR(x)             PIC_ADDR_VALUE, (x)
        #define PIC_ACTIVE_IMAGES(x)    PIC_ACTIVE_IMAGES_VALUE, (x)

        #else

        #define PIC_COLOR_MODEL(x) \
                PIC_COLOR_MODEL_VALUE, color_model_e_arg(x)
        #define PIC_ADDR(x)             PIC_ADDR_VALUE, char_p_arg(x)
        #define PIC_ACTIVE_IMAGES(x)    PIC_ACTIVE_IMAGES_VALUE, int_arg(x)

        enum color_model_e
        color_model_e_arg (x)
        enum color_model_e x;
        {
                return (x);
        }

        int
        int_arg (x)
        int             x;
        {
                return (x);
        }

        char           *
        char_p_arg (x)
        char           *x;
        {
                return (x);
        }

        #endif

        main ()
        {
                char           *pic_addr = NULL;

                alloc_picture (PIC_COLOR_MODEL (color_xyz),
                               PIC_ADDR (pic_addr),
                               PIC_ACTIVE_IMAGES (ACTIVE_X + ACTIVE_Y),
                               0);
        }

If lint is undefined (i.e., you are just compiling) then the call to
alloc_picture() expands to

        alloc_picture (  1, (color_xyz),
                         2, (pic_addr),
                        3, ( 1 +  2),
                       0);

If lint is defined, it expands to

        ... all the <type>_arg() functions (deleted for brevity)

        alloc_picture (  1, color_model_e_arg(color_xyz),
                         2, char_p_arg(pic_addr),
                        3, int_arg( 1 +  2),
                       0);

Lint can now do full type checking on alloc_picture's variable argument list.
This is nice ...
-----
{harvard!adelie,{decvax,allegra,talcott}!encore}!munsell!jwf

Jim Franklin, Eikonix/EPPS, 23 Crosby Drive, Bedford, MA 01730
Phone: (617) 663-2115 x4015