[comp.lang.c] #if inside #define -- easy to do, but is it a good idea?

minow@decvax.UUCP (Martin Minow) (09/26/87)

From time immemorial, C has forbidden # control lines inside a
macro definition.  When a collegue begged for this capability,
I discovered it was trivial to add it to Decus CPP (which was posted
to Usenet mod.sources about 2-3 years ago).  It might be used
as follows:
	#define set_bit(bit)				\
	#if debug					\
	    printf("setting %04x\n", bit),		\
	#endif						\
		(device_register = (bit))

Decus CPP compiles macros by replacing formal parameters with the
appropriate actual parameter and "pushing" the entire string back on
the input file reader.  The only change I had to make was to recognize
the string "\ <newline>" during macro compilation and "expand" it as
"<newline>".  (Of course, the program is now non-transportable.)

While the process may be more difficult for a tokenizing pre-processor,
it shouldn't be too hard to implement.

I'm sure it's too late in the review cycle to put this into the Standard
(and it does violate the formal model), but it might be worth thinking
of for the future.

Or, did I forget something obvious?

Martin Minow
decvax!minow

david@sun.uucp (David DiGiacomo) (09/29/87)

In article <157@decvax.UUCP> minow@decvax.UUCP (Martin Minow) writes:
>From time immemorial, C has forbidden # control lines inside a
>macro definition.  When a collegue begged for this capability,
>I discovered it was trivial to add it to Decus CPP (which was posted
>to Usenet mod.sources about 2-3 years ago).  It might be used
>as follows:
>	#define set_bit(bit)				\
>	#if debug					\
>	    printf("setting %04x\n", bit),		\
>	#endif						\
>		(device_register = (bit))

It would be a nice feature to have, but it's not really necessary for your
example:

#if debug
#define	IFDEBUG(x) x
#else
#define IFDEBUG(x)
#endif

#define	set_bit(bit) \
	IFDEBUG(printf("setting %04x\n", bit);) \
	(device_register = (bit))

-- 
David DiGiacomo, Sun Microsystems, Mt. View, CA  sun!david david@sun.com

eao@anumb.UUCP (e.a.olson) (09/29/87)

> From time immemorial, C has forbidden # control lines inside a
> macro definition.  When a collegue begged for this capability,
> I discovered it was trivial to add it to Decus CPP (which was posted
> to Usenet mod.sources about 2-3 years ago).  It might be used
> as follows:
> 	#define set_bit(bit)				\
> 	#if debug					\
> 	    printf("setting %04x\n", bit),		\
> 	#endif						\
> 		(device_register = (bit))
> 
> (and it does violate the formal model), but it might be worth thinking
> of for the future.
> 
> Or, did I forget something obvious?
> 

    The point is that, as you said, your programs are non-portable. 
    You could kludge it with some definition like below or even 
    better make DEBUG a varargs function that compiles into something
    that prints out the args when DEBUG is compiled with debug and
    does nothing otherwise	(except then you'd lose the inline
    code speed ).  Matter of fact, this is probably pretty bad.


    BUT DON'T CHANGE THE COMPILER !!!


#if debug
#	define DEBUG(format, arg) printf(format, arg)
#else	
#	define DEBUG(format, arg) 0
#endif

#define set_bit(bit)				\
	DEBUG("setting %04x\n", bit),		\
	(device_Register = (bit))

rbutterworth@orchid.UUCP (09/29/87)

In article <29339@sun.uucp>, david@sun.uucp (David DiGiacomo) writes:
> In article <157@decvax.UUCP> minow@decvax.UUCP (Martin Minow) writes:
> >From time immemorial, C has forbidden # control lines inside a
> >macro definition.
> It would be a nice feature to have, but it's not really necessary for your
> example

OK, here's a real example where it would be useful
(in fact I can't see any nice way of doing this without the ability):

    #if !defined(compiler_A)
    #if !defined(compiler_B)
    #if !defined(compiler_C)
    ^%$%^#^%$&^% syntax error since we don't know this compiler.

    #else  /* compiler_C */
    #   define ARGSUSED /*ARGSUSED*/
    #endif /* compiler_C */

    #else  /* compiler_B */
    #   define ARGSUSED #argsused
    #endif /* compiler_B */

    #else  /* compiler_A */
    #   define ARGSUSED #pragma argsused
    #endif /* compiler_A */

goes into a header file, and a program can simply use the manifest
ARGSUSED
in the appropriate places.

Without this ability, the source gets rather ugly if it is to run
on all three compilers.

ado@elsie.UUCP (10/03/87)

< > > From time immemorial, C has forbidden # control lines inside a
< > > macro definition.
< ...here's a real example where it would be useful...
<     #if !defined(compiler_A)
<     #if !defined(compiler_B)
<     #if !defined(compiler_C)
<     ^%$%^#^%$&^% syntax error since we don't know this compiler.
< 
<     #else  /* compiler_C */
<     #   define ARGSUSED /*ARGSUSED*/
<     #endif /* compiler_C */
< 
<     #else  /* compiler_B */
<     #   define ARGSUSED #argsused
<     #endif /* compiler_B */
< 
<     #else  /* compiler_A */
<     #   define ARGSUSED #pragma argsused
<     #endif /* compiler_A */
< 
< goes into a header file, and a program can simply use the manifest
< ARGSUSED
< in the appropriate places.

Rather than allowing # directives in macros, it suffices to do macro
substitution in directives (as is done by the 4.3BSD C compiler and by the
System V Release 2 compiler, but not by the DECUS C preprocessor).  The
above code then becomes

	#ifdef compiler_A
	#define USEDARGS	pragma argsused
	#elif compiler_B
	#define USEDARGS	argsused
	#elif compiler_C
	#define USEDARGS	/*ARGUSED*/
	#else
		!Funky!Stuff!
	#endif
and a program can simply use
	#USEDARGS
in the appropriate places.
-- 
ado@vax2.nlm.nih.gov	ADO, VAX, and NIH are trademarks of Ampex and DEC.

minow@decvax.UUCP (Martin Minow) (10/04/87)

Several responses suggested that the capabilities of # rescanning
could be accomplished at execution time -- especially since modern
compilers prune "dead-code" so a sequence like:
	#define DEBUG	0
	#define DLOG(a)	((DEBUG) ? printf a : 0)
	   ... DLOG(("%d", x));
generates no extraneous code.

There are other needs, however.  For example, here is example where
it would be nice to be able to generate # commands inside of macros.
It builds a symbol table of keywords:

	char *table[] = {
	    "foo",
	#		define SY_foo	0
	    "bar",
	#		define SY_bar	1
	    NULL
	};

The macro would look something like

	#define SYM(name, n)			\
(1)	    #name,				\
(2)	    #define SY_##name	n

And would be used as

	SYM(foo, 0)
	SYM(bar, 1)

Notes:
(1)	# is used here to "stringize" its argument.  This means that
	SYM(define) or SYM(if) will not work.  This problem is due
	to the ambiguity of the unary # operator.  I suppose you
	could hack around it by writing (1) as
		"" ## #name,

(2)	Note that C doesn't give the programmer the tools to generate
	flexible counters.  I.e, CPP is not a general purpose macro
	processor.  Of course, K&R said this first.

It should be pointed out that if you use command files, makefiles or
their equivalent, it is very easy to build special purpose table
generators that are automatically invoked when the appropriate module
is compiled.  The realworld (say, constructing menus for a window
system) requires more complex table constructions than my simple
extension would allow.  Yacc and lex users have known this for years.

Martin Minow
decvax!minow

	

gwyn@brl-smoke.ARPA (Doug Gwyn ) (10/06/87)

In article <163@decvax.UUCP> minow@decvax.UUCP (Martin Minow) writes:
-(2)	Note that C doesn't give the programmer the tools to generate
-	flexible counters.  I.e, CPP is not a general purpose macro
-	processor.  Of course, K&R said this first.
-It should be pointed out that if you use command files, makefiles or
-their equivalent, it is very easy to build special purpose table
-generators that are automatically invoked when the appropriate module
-is compiled.  The realworld (say, constructing menus for a window
-system) requires more complex table constructions than my simple
-extension would allow.  Yacc and lex users have known this for years.

I believe this is essentially the position X3J11 has taken in response
to requests for more generic preprocessing capabilities (other than #
and ##, for which the need had been convincingly demonstrated by the
non-portable kludges that programmers had widely resorted to).  The
idea is to fix up the specification that was already there, not dream
up new, powerful facilities when viable alternatives exist.  In
particular, the C preprocessor cannot be conveniently turned into a
general macro processor without invalidating lots of existing correct
code.  But don't quote me..

tim@ism780c.UUCP (Tim Smith) (10/07/87)

In article <7510@elsie.UUCP> ado@elsie.UUCP (Arthur David Olson) writes:
< 	#define USEDARGS	/*ARGUSED*/

This doesn't do anything useful on the various cpp's I have tried
it on.  The /*ARGUSED*/ is taken as a comment before the #define
is processed.
-- 
Tim Smith, Knowledgian		{sdcrdcf,uunet}!ism780c!tim
				tim@ism780c.isc.com

rbutterworth@orchid.UUCP (10/08/87)

In article <7454@ism780c.UUCP>, tim@ism780c.UUCP (Tim Smith) writes:
> <     #define USEDARGS	/*ARGUSED*/
> This doesn't do anything useful on the various cpp's I have tried
> it on.  The /*ARGUSED*/ is taken as a comment before the #define
> is processed.

But that was for "COMPILER_C", you probably haven't used the cpp
that comes with that version :-)

There is also:

#if defined(bsd)
#   define USEDARGS //**/*ARGSUSED*/**//
#endif

which works fine on the bsd 4.3 lint.
(The second /**/ isn't needed but make it look more balanced.)