Variable arguments in macros

Jerry Leichter leichter at CS.YALE.EDU
Wed May 3 00:51:27 AEST 1989


In article <12839 at haddock.ima.isc.com>, (Karl Heuer) writes...
>In article <58345 at yale-celray.yale.UUCP> (Jerry Leichter) writes:
>>[allow an ellipsis in the macro parameters, and have a builtin macro _REST_
>>which] expands to the list of remaining arguments, including a leading comma
>>if there is at least one such argument.
> 
>Not good enough.  Consider:
>	#define f(a,b,...) (g(a,b),h(_REST_))
>in which, for syntactic correctness, _REST_ must exclude the leading comma.
>I suspect this was the major reason that variadic macros didn't make it into
>the Standard: there was no clean proposal that handled the case with zero
>additional args.

There are two answers to this complaint:

a)  So what?  What you've shown is that the facility I'm proposing is not as
general as it might be.  That's true of many parts of C's macro facility:  It
has enough power to do certain simple jobs, but no more.

b)  Actually, however, a detailed analysis shows that your example is wrong
anyway.  Look more closely at h.  Whether h is a function or a macro, it is
impossible for it to be correctlly callable with either no arguments OR with
at least one argument.  Both ANSI C "variadic" functions, and the "variadic"
macros that I'm proposing, MUST be called with at least one argument.  Hence,
correct uses of f MUST have at least three arguments, and we can simply
rewrite f as:

	#define f(a,b,c,...) (g(a,b),h(c _REST_))

- a formulation which has the additional advantage of generating more consis-
tent and meaningful error messages, always reporting that it is f, which the
programmer wrote, which was called with the wrong number of arguments, rather
than a function h he may never have heard of.

This argument generalizes:  If you use _REST_ in specifying the arguments to a
macro or function, the only problem you can run into is if _REST_ comes first,
just after the "(".  But then it cannot be the whole argument list, by the
reasoning which applied to h.  So you CAN construct examples which fail, like:

	#define f(a,b,...) (g(a,b),h(_REST_,NULL))

If this really worries you, I claim it shouldn't - you've defined the wrong
calling convention for h.  It would be much better to re-define h so that

	#define f(a,b,...) (g(a,b),h(_NARGS_ _REST_))

is the right calling sequence.

If you look at all places in C where a comma makes sense, you can quickly find
all the possible problems:

1.  Comma operator:  If you wanted:

	a = (_REST_)

just write

	a = (0 _REST_)

instead.

2.  Function and macro argument lists - I've already discussed this.

3.  Initializer lists.  Again, you can probably construct plausible examples,
most of which have plausible workarounds.

The facility I'm proposing is simple and useful.  Sure, you can add bells and
whistles - just having both _REST_ (with leading comma) and _NREST_ (no lead-
ing comma) will do it.  (Or do you also need _EREST_, with the comma at the
end?)  Or you could add a pre-processor ?: operator - I might be able to write
stuff like:

	#define f(a,b,...) (g(a,b),h((_NARGS_ > 0) #? (_REST_) #: (,) NULL))

(This might be better done as an ifthenelse pre-defined macro.)  But all this
goes way beyond what's needed to solve a significant problem which is not
currently solvable in a clean way - as the discussion of the 2- vs. 3-argument
BSD open() makes clear.
							-- Jerry



More information about the Comp.std.c mailing list