[Someone asked me the question which is now 17.4b in the comp.lang.c FAQ list, and this was my reply.]

From: scs@eskimo.com (Steve Summit)
Subject: Re: A ``C'' Question
Date: Thu, 1 Jun 2000 11:43:30 -0700 (PDT)
Message-Id: <200006011843.LAA29337@mail.eskimo.com>

You wrote:
> I have a ``C'' programming question. On UNIX system, in /usr/include
> directory, there is a ctype.h.

As a general rule, you have to be careful when inspecting compiler-supplied header files, for example the ones in /usr/include. Often these make use of ``magic'' features which are not standard and sometimes not even intended for users to use. This question, however, has a reasonably straightforward answer, and it's not tied to any particular compiler.

> In this file, there is a prototype definition
> extern int isalpha __((int)).
> I can not understand <space>__((int)). What is meaning of that?
> My guess is that it is an argument to the function isalpha but
> how come there is a space in between.

This is actually a trick.

As you may know, there are two kinds of external function prototype declaration in C. The old-fashioned kind looked like

	extern int isalpha();
and says that isalpha is a function returning int. The newer, prototype form (which may be the only kind you're used to) looks like

	extern int isalpha(int);
and supplies the additional information that the isalpha function accepts one argument, of type int.

Sometimes it's useful if a piece of code (in this case, a header file) can be compiled both by ANSI-compatible and pre-ANSI compilers. Function prototypes, however, were introduced along with the ANSI Standard. Pre-ANSI compilers didn't understand function prototypes, and in fact a pre-ANSI compiler will generally report a ``syntax error'' when it sees a function prototype.

The first half of the trick, therefore, is to make the presence of the function prototype conditional. Suppose we had the macro

	#define PROTOTYPE(args) args
This is a fairly useless-looking macro; all it does is accept one macro argument and spit it back out. But suppose we were to write the prototype for isalpha as

	extern int isalpha PROTOTYPE((int));
After the preprocessor expanded the macro, we'd end up with

	extern int isalpha (int);
which is just like our original prototype (except for the insertion of an extra space, which doesn't matter).

(One additional question here is why we have to invoke the macro as PROTOTYPE((int)), with an extra set of parentheses. The reason is so that the preprocessor won't complain when we use the PROTOTYPE macro to help us declare a function that takes multiple arguments. If we invoked PROTOTYPE(int, double), the preprocessor would complain that we'd invoked the PROTOTYPE macro with two macro arguments, while it expected only one. But when we invoke it as PROTOTYPE((int, double)), as far as the preprocessor is concerned we're invoking it with one argument, which is ``(int, double)'', including one pair of parentheses.)

Now, however, we have a way to ``turn off'' the prototype. If we redefine the PROTOTYPE macro as

	#define PROTOTYPE(args) ()
the macro-ized declaration for isalpha would turn into
	extern int isalpha ();
and this would be acceptable to a pre-ANSI compiler. So, putting this all together, we might say

	#ifdef __STDC__
	#define PROTOTYPE(args) args
	#else
	#define PROTOTYPE(args) ()
	#endif

	extern int isalpha PROTOTYPE((int));

Now we have one scrap of code that's ``backwards compatible'' to a pre-ANSI compiler. (Obviously we only have to do the #ifdef/#define/#else thing once, then we can use the PROTOTYPE macro in lots of external function prototype declarations.)

The only problem with this is that

	extern int isalpha PROTOTYPE((int));
looks pretty ugly, due to that macro name PROTOTYPE sitting in there. Someone decided that it was worth trying to fix this.

The rule for identifiers in C is that they may consist of letters, digits, and underscore characters, and that the first character must not be a digit. This means that the first character can be an underscore. But it also means that identifiers consisting of only underscores are legal! Using a single or a double underscore as an identifier is a pretty sneaky trick, and we obviously can't use it too often, but if we've decided that writing backwards-compatible function declarations without that ugly PROTOTYPE name is so important that we're willing to devote this sneaky trick to the task, we can replace the macro name ``PROTOTYPE'' with the macro name ``__'', leading to:

	#ifdef __STDC__
	#define __(args) args
	#else
	#define __(args) ()
	#endif

	extern int isalpha __((int));

So that explains what you saw. (Personally, I don't think this is all worth it, and I don't use this trick, myself, but I've seen it in plenty of code written by others.)

Steve Summit
scs@eskimo.com

--
Programming Challenge #6: Don't just fix the bug.
See http://www.eskimo.com/~scs/challenge/.