[comp.lang.c] C's Limited Macro Capabilities

bpm@psuecl.bitnet (Brian Moquin) (12/03/89)

A student in my C class asked me an interesting question:  can you have
conditional compilation directives embedded within '#define' macros?
The answer is no.  The student pointed out that this then
severely limits the macro capabilities of C.  He said that in assembler
(MASM, I'm sure), he can write macros which contain arguments that
determine how the macros get expanded.  I had trouble coming up with
a good analogous example in C, but here's one to think about:
        #define cast(flag,x)    #if flag=='I' \
                                        ((int)(x)) \
                                #elif flag=='F' \
                                        ((float)(x)) \
                                #endif
This is not legal C, but if it were I think it would enhance the power
of macro expansions significantly.  Thoughts?

poser@csli.Stanford.EDU (Bill Poser) (12/04/89)

A simple macro processor, like the one we have, is a Good Thing.
I am not so sure that a more powerful macro processor along the
same lines would be. For one thing, the syntax of macro languages
is nasty, and not conducive to good programming practice, and when one
writes complex macros, in effect one is making use of a meta-programming
language. So my gut reaction to proposals for non-trivial extensions to
cpp is generally one of the following, depending on the case at hand:

	(a) The problem really calls for make(1);
	(b) The problem is better handled by means of preprocessing by
		means of an independent language, e.g. AWK or ICON;
	(c) The user really should be using a language other than C.

This is isn't to say that one of these is always the solution or that
every extension to cpp is a bad idea, but it does seem to me that complex
macro expansions are fragile, hard to document, and easy to abuse.

bill@twwells.com (T. William Wells) (12/04/89)

In article <69517@psuecl.bitnet> bpm@psuecl.bitnet (Brian Moquin) writes:
: A student in my C class asked me an interesting question:  can you have
: conditional compilation directives embedded within '#define' macros?
: The answer is no.  The student pointed out that this then
: severely limits the macro capabilities of C.  He said that in assembler
: (MASM, I'm sure), he can write macros which contain arguments that
: determine how the macros get expanded.  I had trouble coming up with
: a good analogous example in C, but here's one to think about:
:         #define cast(flag,x)    #if flag=='I' \
:                                         ((int)(x)) \
:                                 #elif flag=='F' \
:                                         ((float)(x)) \
:                                 #endif
: This is not legal C, but if it were I think it would enhance the power
: of macro expansions significantly.  Thoughts?

This particular one can be dealt with in ANSI C, I think.
Something like:

#define cast_F(x)       ((float)(x))
#define cast_I(x)       ((int)(x))
#define cast(flag,x)    cast_##flag((x))

---
Bill                    { uunet | novavax | ankh | sunvice } !twwells!bill
bill@twwells.com

richard@calvin.EE.CORNELL.EDU (Richard Brittain) (12/04/89)

In article <11250@csli.Stanford.EDU> poser@csli.stanford.edu (Bill Poser) writes:
>
>A simple macro processor, like the one we have, is a Good Thing.
>I am not so sure that a more powerful macro processor along the
>same lines would be. For one thing, the syntax of macro languages
>is nasty, and not conducive to good programming practice, and when one
>writes complex macros, in effect one is making use of a meta-programming
>language. So my gut reaction to proposals for non-trivial extensions to
>cpp is generally one of the following, depending on the case at hand:
..... examples deleted

Another problem with using complex macros, particularly if they affect function
declarations or variable/array declarations, is that most source code analysis
tools, pretty printers etc. will break.  Running them on the output of cpp
doesn't cut it if part of the output involves line numbers into the source file.


Richard Brittain,                   School of Elect. Eng.,  Upson Hall   
                                    Cornell University, Ithaca, NY 14853
ARPA: richard@calvin.spp.cornell.edu	
UUCP: {uunet,uw-beaver,rochester,cmcl2}!cornell!calvin!richard

henry@utzoo.uucp (Henry Spencer) (12/04/89)

In article <69517@psuecl.bitnet> bpm@psuecl.bitnet (Brian Moquin) writes:
>A student in my C class asked me an interesting question:  can you have
>conditional compilation directives embedded within '#define' macros?
>The answer is no.  The student pointed out that this then
>severely limits the macro capabilities of C...

He is correct.  C is not intended to be the answer to all the world's
problems.  In particular, the C preprocessor is not intended to be an
all-singing-all-dancing Turing-capable macro language.  As Dennis put
it:  "If you want PL/I, you know where to find it."
-- 
Mars can wait:  we've barely   |     Henry Spencer at U of Toronto Zoology
started exploring the Moon.    | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

lmiller@aerospace.aero.org (Lawrence H. Miller) (12/05/89)

In article <69517@psuecl.bitnet> bpm@psuecl.bitnet (Brian Moquin) writes:

	That he thinks macros like this would be useful:

>        #define cast(flag,x)    #if flag=='I' \
>                                        ((int)(x)) \
>                                #elif flag=='F' \
>                                        ((float)(x)) \
>                                #endif
>This is not legal C, but if it were I think it would enhance the power
>of macro expansions significantly.  Thoughts?


But for each such example, you can easily write a macro that
is legal.  Why not just have your macro do what you really
want, which is casting an expression to a type:

#define cast(type,x)		((type)(x))

Larry Miller

erc@khijol.UUCP (Edwin R. Carp) (12/05/89)

In article <69517@psuecl.bitnet> bpm@psuecl.bitnet (Brian Moquin) writes:
>        #define cast(flag,x)    #if flag=='I' \
>                                        ((int)(x)) \
>                                #elif flag=='F' \
>                                        ((float)(x)) \
>                                #endif

Couldn't you do the same thing with:

#if flag=='I'
#define cast(flag,x) ((int)(x))
#elif flag=='F'
#define cast(flag,x) ((flaot)(x))
#endif
----------------------------------- cut here -----------------------------------
           Ed Carp	N7EKG/5 (28.3-28.5)	erc@puzzle!khijol
           Austin,  Tx; (home) (512) 445-2044
           Snail Mail:  1800 E. Stassney  #1205
                        Austin, Tx  78744

oz@yunexus.UUCP (Ozan Yigit) (12/05/89)

In article <11250@csli.Stanford.EDU> poser@csli.stanford.edu (Bill Poser) writes:
>A simple macro processor, like the one we have, is a Good Thing.
>I am not so sure that a more powerful macro processor along the
>same lines would be.

I do not know what "more powerfull macro processor" means. How powerful is
powerful ?? [This is a trick question :-)]

> For one thing, the syntax of macro languages
>is nasty, and not conducive to good programming practice, and when one
>writes complex macros, in effect one is making use of a meta-programming
>language.

Bad syntax is a design problem, not a generic problem of macro processors.
As for "conducive to good programming practice", say, what was that language
in which we "obfuscate" ?? Perl ?? Naah, but close !! :-)

The difference between a meta programming language as opposed to a "real"
one is a matter of abstraction, if done properly.

>So my gut reaction to proposals for non-trivial extensions to
>cpp ...

I think CPP is beyond hope for future extensions. One could, however build
more interesting macro processors that can resemble CPP for the most part,
or perhaps not resemble it at all... There has been some subset cpps in
the past, and I have seen supersets as well. Bless that "little languages"
approach.

oz
-- 
There are two kinds of fool.   	       	           Internet:  oz@nexus.yorku.ca
One says, "This is old, and therefore good"        Uucp:  uunet!utai!yunexus!oz
And one says "This is new, and therefore Better"   Bitnet: oz@[yulibra|yuyetti]
              John Brunner (The Shockwave Rider)   Phonet: +1 416 736-5257x3976

gwyn@smoke.BRL.MIL (Doug Gwyn) (12/05/89)

In article <69517@psuecl.bitnet> bpm@psuecl.bitnet (Brian Moquin) writes:
>This is not legal C, but if it were I think it would enhance the power
>of macro expansions significantly.  Thoughts?

My thought is that it's irrelevant.
C's preprocessing facilities were never meant to serve as a general
macro processing language.

) (12/06/89)

In article <69517@psuecl.bitnet> bpm@psuecl.bitnet (Brian Moquin) writes:
>A student in my C class asked me an interesting question:  can you have
>conditional compilation directives embedded within '#define' macros?
>The answer is no.  The student pointed out that this then
>severely limits the macro capabilities of C.  He said that in assembler
>(MASM, I'm sure), he can write macros which contain arguments that
>determine how the macros get expanded.  I had trouble coming up with
>a good analogous example in C, but here's one to think about:
>        #define cast(flag,x)    #if flag=='I' \
>                                        ((int)(x)) \
>                                #elif flag=='F' \
>                                        ((float)(x)) \
>                                #endif

In (Reiser) cpp you can accomplish this sort of thing with token pasting
and a few additional macros:

#define	_CAT(a,b)	a/**/b
#define	cast(flag,x)	_CAT(_CAST,flag)(x)
#define	_CASTI(x)	((int) (x))
#define	_CASTF(x)	((float) (x))

cast(I, foo)
cast(F, bar)

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

Tim_N_Roberts@cup.portal.com (12/06/89)

Re: Discussion of C Pre-processor limitations:

Remember that most (all?) Unix implementations include M4, which is
a rather powerful macro expansion facility.  It would be perfectly
feasible to have your makefile call M4 before invoking cc.

TNR@cup.portal.com                |  I Survived The
...!sun!portal!cup.portal.com!tnr |  Great Quake of '89.

peter@ficc.uu.net (Peter da Silva) (12/07/89)

In article <24736@cup.portal.com> Tim_N_Roberts@cup.portal.com writes:
> Remember that most (all?) Unix implementations include M4, which is
> a rather powerful macro expansion facility.  It would be perfectly
> feasible to have your makefile call M4 before invoking cc.

C does not imply UNIX. It's fun to bitch about MS-DOS specific junk floating
around in here, but assuming that UNIX == C is worse.
-- 
`-_-' Peter da Silva. +1 713 274 5180. <peter@ficc.uu.net>.
 'U`  Also <peter@ficc.lonestar.org> or <peter@sugar.lonestar.org>.

      "If you want PL/I, you know where to find it." -- Dennis

tps@chem.ucsd.edu (Tom Stockfisch) (12/07/89)

In article <62664@aerospace.AERO.ORG> lmiller@batcomputer.UUCP (Lawrence H. Miller) writes:
>In article <69517@psuecl.bitnet> bpm@psuecl.bitnet (Brian Moquin) writes:
>>        #define cast(flag,x)    #if flag=='I' \
>>                                        ((int)(x)) \
>>                                #elif flag=='F' \
>>                                        ((float)(x)) \
>>                                #endif
>>This is not legal C, but if it were I think it would enhance the power
>
>But for each such example, you can easily write a macro that
>is legal.

It is not always possible to write a legal macro, at least not easily.
For instance, suppose we want to define "nearest neighbors" of a lattice
with periodic boundary conditions (neighbors of the edges wrap around to
the other edge), and we want addressing to be very fast.

int	lat[DIM*DIM];

# define XNeighbor(i)	# if (i)%DIM == DIM-1
				( (i) - DIM + 1 )
			# else
				( (i) + 1 )

# define YNeighbor(i)	# if (i) >= DIM*(DIM - 1)
				( (i) + DIM  )
			# else
				( (i) - DIM*(DIM - 1) )

...
	switch (s)
	{
	case 0:	DoSomething( lat[XNeighbor(s)] + lat[YNeighbor(s)] );
		break;
	case 1:	DoSomething( lat[XNeighbor(s)] + lat[YNeighbor(s)] );
		break;
	...
	case DIM*DIM-1:	DoSomething( lat[XNeighbor(s)] + lat[YNeighbor(s)] );
		break;
	default: assert(0);
	}

When I have had to do this, I simply eliminate the "#" before the "if" and the
"else", and depend on the compiler to optimize out all of the branches that
can't be reached (in this case, half of them), and ignore the deluge of 
"constant in conditional context" warnings from lint.

M4 is usually of little or no help because too many C things are used
in the constant arithmetic.

-- 

|| Tom Stockfisch, UCSD Chemistry	tps@chem.ucsd.edu

scs@itivax.iti.org (Steve Simmons) (12/08/89)

In article <69517@psuecl.bitnet> bpm@psuecl.bitnet (Brian Moquin) writes:
>This is not legal C, but if it were I think it would enhance the power
>of macro expansions significantly.  Thoughts?

Keep complex macro processing in macro processing languages, leave a
well-defined standard (well....) alone.  See m4.

Shucks, if it were up to me there wouldn't be a cpp at all.
-- 
Steve Simmons	       scs@iti.org         Industrial Technology Institute
	'"You're not a big name on Usenet until someone puts
	  you in their .sig file."		-- Anonymous'