[comp.lang.c] Macros with multi-line results?

fritzson@PRC.Unisys.COM (Richard Fritzson) (01/11/91)

I am trying to write a macro which generates more than one line of
output. Defining a macro such as

	#define foo(a,b) body \
	 more body \
	 still more

and invoking it with

	foo (one, two)

produces:

	 body more body still more

as output from the C preprocessor. I would very much like to get

	body
	more body
	still more

somehow. Is this possible? 

I know that it shouldn't matter, but in this case, it does.

I would appreciate anyone's help with this.  Thanks.


Rich Fritzson				             fritzson@prc.unisys.com
Unisys - Center for Advanced Information Technology  (215) 648-2972

bjornmu@idt.unit.no (Bj|rn P. Munch) (01/11/91)

In article <16101@burdvax.PRC.Unisys.COM>, fritzson@PRC.Unisys.COM (Richard Fritzson) writes:
|> I am trying to write a macro which generates more than one line of
|> output. Defining a macro such as
|> 
|> 	#define foo(a,b) body \
|> 	 more body \
|> 	 still more
|> 
|> and invoking it with
|> 
|> 	foo (one, two)
|> 
|> produces:
|> 
|> 	 body more body still more
|> 
|> as output from the C preprocessor. I would very much like to get
|> 
|> 	body
|> 	more body
|> 	still more
|> 
|> somehow. Is this possible? 

Yes, with a little help from tr.  I had to do the same thing once, and
my solution was to write the macro like this:

      #define foo(a,b) body @\
        more body @\
        still more

, and then process it like this:  (foo.xc is the source with the macro)

      cpp -P foo.xc | tr '@' '\012' > foo.c

Then you can compile foo.c.  In my case, I had a 100 line macro
expanding to function bodies with statements in embedded SQL.  I had
to do this trick because the SQL statements had to be at the start of
a line for the preprocessor.

Even worse, I needed to use macro arguments that contained commas, but
that problem could be solved in the same way.....  :-)

Your cpp may choke on the @ sign, in which case you can try a "legal"
C character like ?, | or ~.  Just make sure you don't need that
character for its "real" purpose anywhere else in the file.

Good luck!

-----
Bj|rn P. Munch               | Div. of Comp. Science & Telematics,
bjornmu@idt.unit.no          | Norwegian Institute of Technology (NTH),
PhD Student                  | Trondheim, Norway
 (some filler words here)    | You can finger me @jod.idt.unit.no

henry@zoo.toronto.edu (Henry Spencer) (01/12/91)

In article <16101@burdvax.PRC.Unisys.COM> fritzson@PRC.Unisys.COM (Richard Fritzson) writes:
>I am trying to write a macro which generates more than one line of
>output...

You can't.

(At least, not unless your compiler's preprocessor has some nonstandard
feature that you can exploit to achieve it.)

The C preprocessor is not a general-purpose macro processor.
-- 
If the Space Shuttle was the answer,   | Henry Spencer at U of Toronto Zoology
what was the question?                 |  henry@zoo.toronto.edu   utzoo!henry

dave@aspect.UUCP (Dave Corcoran) (01/23/91)

In article <16101@burdvax.PRC.Unisys.COM>, fritzson@PRC.Unisys.COM (Richard Fritzson) writes:
> I am trying to write a macro which generates more than one line of
> output.
> 
> as output from the C preprocessor. I would very much like to get
> 
> 	body
> 	more body
> 	still more
> 
> somehow. Is this possible? 
> 

Here is an m4 solution, named perldef (in perlmac.m) using perl.


The macro which follows will define another macro which can be invoked
inline (via m4 (SunOS users use /usr/5bin/m4)) and can use any perl script.

Quoted constructs:

	'\''		to insert tics	
	\\n			to insert \n
	%%			to insert %

so the string

	" '\''%%s'\''\\n "

yields

	" '%s'\n "

in the output perl *script*.


Options to perl can be passed as $3 of the definition.

The contents of perlmac.m follows
---------------------8<--------------------------
define(perldef,
	`syscmd((echo ' '$2' | sed 's/^(//;s/^)//' `>/tmp/pm_$1))'
	`define($1,`syscmd(perl $3 /tmp/pm_$1 $'`1 ifelse($'`2,,,<<@@
$'`2
@@))')'
)
---------------------8<--------------------------

Two forms of invocation can be used: (where xx is the macro definition)


1.
	xx([arg1 ... argN])

This form places arg1 -> argN in arg list (i.e. @ARGV).
Note the *absence* of *any* commas in the arg list.


2.
	xx(,arg1.1 arg1.2
	arg2
	arg3
	arg4 arg4.1) 

This presents the arg list as the "here-is" form.

	i.e.
	perl scriptfilename <<@@
	arg1 arg1.1 arg1.2
	arg2
	arg3
	arg4 arg4.1
	@@

Note the usage of the leading comma and absence of any commas in
the balence of the list.


An example of how to use follows: (tst.c.m)
                                     (^^^^ -- is this an ok convention?)

Note that the placement of the 2nd inner parens, which allows commas to
be used in the script, must be the first and last characters in the
macro; the perldef macro strips them out using:

	sed 's/^(/^)/' 

so all lines with leading parens column 1 will be deleted. This is a
bug(feature).


tst.c.m
---------------------8<--------------------------
include(perlmac.m)

perldef(xx,(
	for $i (@ARGV) {
		printf qq/#define %s %d\n/,$i,++$j;
	}
))

perldef(yy,( 
	for $i (@ARGV) {
		@l=split(/ */,$i);
		@l[0]=~y/a-z/A-Z/;
		$m=join("",@l);
		$i=~y/a-z/A-Z/;
		printf qq/#define %s "%s"\n/,$i,$m;
	}
))

perldef(zz,(
	for $i (@ARGV) {
		$j=$i;
		$i=~y/a-z/A-Z/;
		printf qq/	printf("-- %%d -- %%s\\n",$j,$i);\n/;
	}
	print qq/	printf("'\''this is in single quotes'\''\\n");\n/;
	## note that $HOME contains "/" so qq/ will not work
	print qq@	printf("this is my current dir - ' $HOME '\\n");\n@;
))


define(`vars',one two three four five six)
define(`vars',vars seven eight nine ten eleven)

xx(vars)
yy(vars)

main()
{
	zz(vars)
}
---------------------8<--------------------------

m4 tst.c.m >tst.c

Invocation given the above yields: (leading blank lines deleted)

#define one 1
#define two 2
#define three 3
#define four 4
#define five 5
#define six 6
#define seven 7
#define eight 8
#define nine 9
#define ten 10
#define eleven 11

#define ONE "One"
#define TWO "Two"
#define THREE "Three"
#define FOUR "Four"
#define FIVE "Five"
#define SIX "Six"
#define SEVEN "Seven"
#define EIGHT "Eight"
#define NINE "Nine"
#define TEN "Ten"
#define ELEVEN "Eleven"


main()
{
		printf("-- %d -- %s\n",one,ONE);
	printf("-- %d -- %s\n",two,TWO);
	printf("-- %d -- %s\n",three,THREE);
	printf("-- %d -- %s\n",four,FOUR);
	printf("-- %d -- %s\n",five,FIVE);
	printf("-- %d -- %s\n",six,SIX);
	printf("-- %d -- %s\n",seven,SEVEN);
	printf("-- %d -- %s\n",eight,EIGHT);
	printf("-- %d -- %s\n",nine,NINE);
	printf("-- %d -- %s\n",ten,TEN);
	printf("-- %d -- %s\n",eleven,ELEVEN);
	printf("'this is in single quotes'\n");
	printf("this is my current dir -  /home/dave \n");

}
-- 
David Corcoran		      -@@
uunet!aspect!dave	        ~
In a society where anything goes eventually everything will.