[comp.lang.c] Unnecessary Macros

edp@pyr.gatech.EDU (Eric D. Price) (09/26/88)

In article <8577@smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <23@datcon.UUCP> sar@datcon.co.uk (Simon A Reap) writes:
>>    z = square(x) + square(y);
>>Does anyone know how to get round this problem?  Please!!
>
>	z = x*x + y*y;

Hear hear.  This latter construction is MUCH clearer and doesn't require
wandering through the source module ... and possibly gobs of header files.
-- 
Warren Tucker
...!{akgua,allegra,amd,hplabs,ihnp4,seismo,ut-ngp}!gatech!gitpyr!edp

karl@haddock.ima.isc.com (Karl Heuer) (09/27/88)

In article <8577@smoke.ARPA> gwyn@brl.arpa (Doug Gwyn) writes:
>In article <23@datcon.UUCP> sar@datcon.co.uk (Simon A Reap) writes:
>>	z = square(x) + square(y);
>>Does anyone know how to get round this problem?  Please!!
>	z = x*x + y*y;

Doug's answer is certainly a better way to write the given example, but is not
particularly enlightening if you want
	z = square(*p++) + square(getchar());

I dislike the original macro, but the fact remains that there is no good way
to write this in C.  (There are several bad ways, and we could argue over
which is the least bad, but it really depends on the circumstances.)

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

swilson%thetone@Sun.COM (Scott Wilson) (09/27/88)

In article <8078@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
>In article <8577@smoke.ARPA> gwyn@brl.arpa (Doug Gwyn) writes:
>>In article <23@datcon.UUCP> sar@datcon.co.uk (Simon A Reap) writes:
>>>	z = square(x) + square(y);
>>>Does anyone know how to get round this problem?  Please!!
>>	z = x*x + y*y;
>
>Doug's answer is certainly a better way to write the given example, but is not
>particularly enlightening if you want
>	z = square(*p++) + square(getchar());
>
>I dislike the original macro, but the fact remains that there is no good way
>to write this in C.  (There are several bad ways, and we could argue over
>which is the least bad, but it really depends on the circumstances.)

Maybe I'm missing the point, but why does a good old fashion function seem
to be out of the question.  Or better yet, why not do it like getc/fgetc
and define a macro for square and have a function fsquare when side effects
are questionable.  And just to make it complete have another function
fdsquare to square doubles.  This way you use square when there is no
doubt and the functions when there is.  Does this sound reasonable?

--
Scott Wilson		arpa: swilson@sun.com
Sun Microsystems	uucp: ...!sun!swilson
Mt. View, CA

mcdonald@uxe.cso.uiuc.edu (09/27/88)

>>In article <23@datcon.UUCP> sar@datcon.co.uk (Simon A Reap) writes:
>>>	z = square(x) + square(y);

>Maybe I'm missing the point, but why does a good old fashion function seem
>to be out of the question.  

I certainly hope you aren't a programmer. The answer is blatently
obvious: calling overhead. Why in the would would anyone ever even
consider writing a function call to perform a single multiplication?
The only possible reason is the IBM mentality: "let's write code so bad
that it runs so slow that the sucker's will have to buy faster iron!".

Actually, one is reduced to using temporaries:

temp1 = fun1();
temp2 = fun2();

z = temp1*temp1 + temp2+temp2; 
and hoping the optimizer removes them. 

firth@sei.cmu.edu (Robert Firth) (09/27/88)

In article <70279@sun.uucp> swilson@sun.UUCP (Scott Wilson) writes:
In article <8078@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
In article <8577@smoke.ARPA> gwyn@brl.arpa (Doug Gwyn) writes:
In article <23@datcon.UUCP> sar@datcon.co.uk (Simon A Reap) writes:

[several ways to define "square(x)" in C that don't quite work]

Full circle, I think.  We're about back where we started with the
position that the only effective solution is an exponentiation
operator, and let the compiler do the grunt work.

jlh@loral.UUCP (Physically Pffft) (09/27/88)

I was sure someone else would mention this so I didn't keep a copy of the
original article to quote.  In general, someone is making a macro
to do z = x*x + y*y, but using a variable 'temp' to do it.  I was under the
impression this was a Very Bad Idea for 2 reasons.  Lets assume Mr C Wizard
has left the company 6 months ago and now it's up to Miss Molly to change
the code.  First, she could use this power macro in a function that didn't
have the variable temp defined.  No problem, the compiler spits out the
undefined variable.  On the other hand, suppose she wants to put this macro
into a loop.  She says to herself 'self, here's a variable temp that ain't used
here'.  So we get

	for(temp = 0; temp < wanna_quit; temp++){
		z = power(x,y);
		some_other_stuff;
	}

Well boys and girls, lets just hope this blows up in a way thats immediatly
obvious.  As a third scenario, combine the first two scenarios.  Poor Miss
Molly adds the power macro to a function that already has temp defined
and is definately needed.  And lets say it's used in such a way that
her changes work fine and dandy, after all, she doesn't use temp in any
of her changes, but changes the original code in some insidious way.
Again, lets hope the result of changing temp is immediatly obvious.

The moral?  Well, if you have to use a temp variable in a macro then
PASS the bloody thing.

#define power(x,y, tmp) (tmp = x*x + y*y, x = tmp)

result = power(x, y, temp);

This ensures that the poor overworked sucker who ends up maintaining your
code knows damn well that your macro requires a temp variable to work right.


							Jim



-- 
Jim Harkins 
Loral Instrumentation, San Diego
{ucbvax, ittvax!dcdwest, akgua, decvax, ihnp4}!ucsd!sdcc6!loral!jlh

scs@athena.mit.edu (Steve Summit) (09/28/88)

In article <225800075@uxe.cso.uiuc.edu> mcdonald@uxe.cso.uiuc.edu writes:
>>>In article <23@datcon.UUCP> sar@datcon.co.uk (Simon A Reap) writes:
>>>>	z = square(x) + square(y);
>
>>Maybe I'm missing the point, but why does a good old fashion function seem
>>to be out of the question.  
>
>I certainly hope you aren't a programmer. The answer is blatently
>obvious: calling overhead. Why in the would would anyone ever even
>consider writing a function call to perform a single multiplication?

I certainly hope you aren't a programmer! :-)

A very few of us (it seems) believe that efficiency is not the
most important thing in the world, and that code cleanliness,
ease of maintenance, the principle of least surprise, etc., are.
We would write a function call to perform a single multiplication
precisely because the hacker's solution (use a macro) has so many
potential unpleasant surprises, as indicated by this discussion.

If (and only if) after-the-fact profiling revealed that the
function call overhead was the cause of noticeable and
undesirable inefficiency, would we replace it with something
quicker, probably the explicit x*x Doug recommended, introducing
an explicit temporary (i.e. not trying to get the preprocessor to
do it for us) if x was a complicated expression or involved side
effects.

I know there are many people who think that efficiency _i_s the
most important thing in the world.  (Someone called Andrew
Koenig's, I believe, attitude "execrable" for advocating
cleanliness over efficiency.)  It boils down to a question of
priorities.  Some find it intolerable if something simple like
squaring involves function call overhead.  I find it intolerable
if what appears to be a function call fails mysteriously because
it happens to be implemented as a macro.

                                            Steve Summit
                                            scs@adam.pika.mit.edu

firth@sei.cmu.edu (Robert Firth) (09/28/88)

In article <23@datcon.UUCP> sar@datcon.co.uk (Simon A Reap) writes:

	[ suggesting the form z = square(x) + square(y)]

>Maybe I'm missing the point, but why does a good old fashion function seem
>to be out of the question.  

In article <225800075@uxe.cso.uiuc.edu> mcdonald@uxe.cso.uiuc.edu writes:

>I certainly hope you aren't a programmer. The answer is blatently
>obvious: calling overhead. Why in the would would anyone ever even
>consider writing a function call to perform a single multiplication?

This prompted me to collect some data.  The machine is a MicroVax II
running Ultrix, and the times are user time.

(a) time for a simple inline single-precision multiplication of a static
    variable, ie "x := x*x":

	5.74 usec

(b) time for a simple call to an Assembler code "square" routine,
    with single-precision operands, operation, and result:

	11.23 usec

So the overhead is about 100%.  Pretty bad.

But how about this?

(c) time for a simple inline 'C' multiplication with single-precision
    operands and result, but a double-precision operation

	11.50 usec

The question is, why would anyone ever even consider writing a function
call?  Answer: it is not only safer than the C code, it's faster.  Unless
you're lucky enough to have a newer compiler that junks the bogus floating
point semantics.

swilson%thetone@Sun.COM (Scott Wilson) (09/29/88)

>>Maybe I'm missing the point, but why does a good old fashion function seem
>>to be out of the question.  
>
>I certainly hope you aren't a programmer. The answer is blatently
>obvious: calling overhead.

Actually I am a programmer, but please don't tell Sun what an idiot
I am or they'll want their money back :-).  If you look at what I wrote,
I said "out of the question" not "less efficient".  I am very aware
of calling overhead and why it can be bad.  What I was responding to
was the attitude that there just isn't any good way to square a number
in C.  The solution I suggested was to use a macro when there were no
possible ill side effects and use a function when there were (just like
getc and fgetc).  So what is wrong with that?  Through the years I've
learned one important thing: correctness first, efficiency later.  There's
nothing terribly exciting about a program that works incorrectly and
does it very quickly at the same time.

--
Scott Wilson		arpa: swilson@sun.com
Sun Microsystems	uucp: ...!sun!swilson
Mt. View, CA

djones@megatest.UUCP (Dave Jones) (09/29/88)

>>In article <23@datcon.UUCP> sar@datcon.co.uk (Simon A Reap) writes:
>>	z = square(x) + square(y);
> 
>Maybe I'm missing the point, but why does a good old fashion function seem
>to be out of the question.  
> 


It is not out of the question by any means.  It is just that on
some implementations, especially those with hardware integer multiply,
and for some applications, the function call overhead may add
significantly to the execution time.

As has already been pointed out, the "morally correct" way to do it is,

    z = x*x + y*y;

avoiding both the SLM* and the function call.


     *SLM /ess el em/ n.  (comp. acrnm.)  Silly little macro.


P.s.

I attempted to post the following directly to Mr. Reap, but I could
not find a path.

Dear Mr. Reap,

    I hope you are not too put off by any response to your question
    which you may have seen. Most of the programmers here in the 
    U.S. are quite polite and helpful.  A few are not, and the net 
    attracts them. I suspect that most of these fellows have yet to
    hold down a full time programming job producing real products
    which must compete in the marketplace.

    That almost invariably cures such arrogance.  After two or three 
    years, they generally discover, that (1) they are not as good as 
    they thought they were, (2) they are not as good as most of 
    the people who have been doing it longer, and (3) in this 
    business, it is very easy to look like a jerk.

    Please, don't ask me how I know.


                   Best wishes,


                    Dave    

nevin1@ihlpb.ATT.COM (Liber) (09/29/88)

In article <7173@aw.sei.cmu.edu> firth@bd.sei.cmu.edu (Robert Firth) writes:
>>[several ways to define "square(x)" in C that don't quite work]

>Full circle, I think.  We're about back where we started with the
>position that the only effective solution is an exponentiation
>operator, and let the compiler do the grunt work.

In other words, there is no solution within the framework of C.  There are
a number of large number of functions that I would rather have as macros
(especially when I have the same operation for different argument types),
but can't because I'm not allowed to (in general) assume that parameters
passed will behave as simple variables w/o side effects.  Just fixing the
problem by changing square(x) to an operator (no, I DON'T want to start up
that discussion again!) doesn't help with the more general problem, and is,
in effect, just another kludge to C.

What is really wanted is to be able to declare macros so that it would have
the same semantics as an inlined function would (somewhat like C++
inlining, except that type checking would not be done on the arguments.
Then again, maybe type checking ought to be done on the arguments...).
When do the proposals for dpANS C II start? :-)
-- 
 _ __		NEVIN J. LIBER  ..!att!ihlpb!nevin1  (312) 979-4751  IH 4F-410
' )  )  "I catch him with a left hook. He eels over. It was a fluke, but there
 /  / _ , __o  ____  he was, lying on the deck, flat as a mackerel - kelpless!"
/  (_</_\/ <__/ / <_	These are NOT AT&T's opinions; let them make their own.

nevin1@ihlpb.ATT.COM (Liber) (09/29/88)

In article <7213@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes:
>A very few of us (it seems) believe that efficiency is not the
>most important thing in the world,

Must be some who programs in the academic environment, not the commercial
environment. :-)

>and that code cleanliness,
>ease of maintenance, the principle of least surprise, etc., are.

But I want BOTH!  Unfortunately, in this instance, C macros are not
powerful enough to give me both.  (I'm wishing more and more that C allowed
a way of allowing macros to act like functions with respect to passing
parameters -- not quite inline functions, but close.)

>We would write a function call to perform a single multiplication
>precisely because the hacker's solution (use a macro) has so many
>potential unpleasant surprises, as indicated by this discussion.

If readability is of that much concern, why not use a language more suited
for the job?  The fact is that one of the reasons C is chosen is because of
its efficiency over other languages.

>If (and only if) after-the-fact profiling revealed that the
>function call overhead was the cause of noticeable and
>undesirable inefficiency, would we replace it with something
>quicker,

Effectively, you would do a tweak.  Personally, I do not believe that
efficiency can be tweaked in to a program; it has to be designed in from
the beginning.  Assuming infinitely fast CPUs and infinite memory are
assumptions that we can no longer afford to make.  Tweaking should be saved
for fixing the *unforseen* bottlenecks.

>introducing
>an explicit temporary (i.e. not trying to get the preprocessor to
>do it for us) if x was a complicated expression or involved side
>effects.

Well, if x really is a complicated expression, then the code probably isn't all
that clean anyway.  As long as you always have clean code, you shouldn't
have problems with macros.  It's when you want to make your macros safe
from unclean code that you run into problems.

>I find it intolerable
>if what appears to be a function call fails mysteriously because
>it happens to be implemented as a macro.

Then macros shouldn't look like function calls!  Since C does not
self-document this fact at every macro invocation, you need to set up a
convention that distinguishes macros from functions (ie, all macros that
are not guaranteed to to behave as a function call are denoted in all
caps).


I am not trying to say that I think efficient code is more important than
readable code.  Personally, I would like clean, self-documenting code that
is efficient (the best of all worlds).  In many situations, however, a
tradeoff needs to be made, and this tradeoff should not be resolved by
blanket statements such as "Efficiency is better than readability" (or
vice-versa).

What we have found with C macros is that they are not good for all the
situations we want to use them for.  They are inadequate replacements for
function calls; the question is:  is it worth modifying them to make them
more useful?  IMHO, I think that this would be a good thing (for ANSI C II
:-)).
-- 
 _ __		NEVIN J. LIBER  ..!att!ihlpb!nevin1  (312) 979-4751  IH 4F-410
' )  )  "I catch him with a left hook. He eels over. It was a fluke, but there
 /  / _ , __o  ____  he was, lying on the deck, flat as a mackerel - kelpless!"
/  (_</_\/ <__/ / <_	These are NOT AT&T's opinions; let them make their own.

cik@l.cc.purdue.edu (Herman Rubin) (09/29/88)

In article <1851@loral.UUCP>, jlh@loral.UUCP (Physically Pffft) writes:
< I was sure someone else would mention this so I didn't keep a copy of the
< original article to quote.  In general, someone is making a macro
< to do z = x*x + y*y, but using a variable 'temp' to do it.  I was under the
< impression this was a Very Bad Idea for 2 reasons.  Lets assume Mr C Wizard
< has left the company 6 months ago and now it's up to Miss Molly to change
< the code.  First, she could use this power macro in a function that didn't
< have the variable temp defined.  No problem, the compiler spits out the
< undefined variable.  On the other hand, suppose she wants to put this macro
< into a loop.  She says to herself 'self, here's a variable temp that ain't used
< here'.  So we get
< 
> 	for(temp = 0; temp < wanna_quit; temp++){
> 		z = power(x,y);
> 		some_other_stuff;
> 	}
> 
> Well boys and girls, lets just hope this blows up in a way thats immediatly
> obvious.  As a third scenario, combine the first two scenarios.  Poor Miss
> Molly adds the power macro to a function that already has temp defined
> and is definately needed.  And lets say it's used in such a way that
> her changes work fine and dandy, after all, she doesn't use temp in any
> of her changes, but changes the original code in some insidious way.
> Again, lets hope the result of changing temp is immediatly obvious.
> 
> The moral?  Well, if you have to use a temp variable in a macro then
> PASS the bloody thing.
> 
> #define power(x,y, tmp) (tmp = x*x + y*y, x = tmp)
> 
> result = power(x, y, temp);
> 
> This ensures that the poor overworked sucker who ends up maintaining your
> code knows damn well that your macro requires a temp variable to work right.

No, this doesn't necessarily work either.  Suppose that this code is part
of something else (a very likely situation) which uses a variable temp.
Then the value of that variable is clobbered.

What is needed is something like

#define fc(x,y) (LOCAL tmp1,tmp2; tmp1=x; tmp2=y; fc(x,y)=tmp1*tmp1+tmp2*tmp2)

which should be interpreted correctly even if x or y happens to be tmp1 or tmp2.

That is, the LOCAL designation should provide a tag which separates the local
variables from the rest, and disambiguates name collision.  The user should not
have to worry about whether the local designations in a block, or in a #define,
etc. conflict with global.  I believe that this also addresses the problem of
code maintenance by someone other than the code writer.

I do not know to what extent compiler implementations do this, but I doubt
that a #define is treated other than a macro expansion, and I do not see
how the problem can be avoided without something like this.

-- 
Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907
Phone: (317)494-6054
hrubin@l.cc.purdue.edu (Internet, bitnet, UUCP)

bill@proxftl.UUCP (T. William Wells) (09/29/88)

In article <1851@loral.UUCP> jlh@loral.UUCP (Physically Phffft) writes:
: [A long section explaining a bug easily caused by temps used in macros]
:
: The moral?  Well, if you have to use a temp variable in a macro then
: PASS the bloody thing.
:
: #define power(x,y, tmp) (tmp = x*x + y*y, x = tmp)
:
: result = power(x, y, temp);
:
: This ensures that the poor overworked sucker who ends up maintaining your
: code knows damn well that your macro requires a temp variable to work right.

We have a coding standard that makes globals always distinct from
locals: globals *always* start with a capital letter; locals,
never.  Given this, we can handle this problem with

int     Square_temp;             /* Macro only works for ints, oh well. */
#define square(x) (Square_temp = (x), Square_temp * Square_temp)

I would never do this (not without a compelling reason, anyway),
but should someone do so here, at least they'd not get bitten by
the local-global problem.

---
Bill

You can still reach me at proxftl!bill
But I'd rather you send to proxftl!twwells!bill

charette@edsews.EDS.COM (Mark A. Charette) (09/29/88)

In article <70616@sun.uucp>, swilson%thetone@Sun.COM (Scott Wilson) writes:
> learned one important thing: correctness first, efficiency later.  There's
> nothing terribly exciting about a program that works incorrectly and
> does it very quickly at the same time.
> 
> --
> Scott Wilson		arpa: swilson@sun.com
> Sun Microsystems	uucp: ...!sun!swilson
> Mt. View, CA

I'm not so sure about that. I've written a few incorrect programs that ran
fast and were TERRIBLY exciting. ;') 
-- 
Mark Charette                        "Unmitigated seriousness is always out of
Electronic Data Systems               place in human affairs." - G. Santayana
750 Tower Drive              Voice: (313)265-7006
Troy, MI 48007-7019          charette@edsews.eds.com     uunet!edsews!charette 

peter@ficc.uu.net (Peter da Silva) (10/01/88)

In article <8813@ihlpb.ATT.COM>, nevin1@ihlpb.ATT.COM (Liber) writes:
> What is really wanted is to be able to declare macros so that it would have
> the same semantics as an inlined function would (somewhat like C++
> inlining, except that type checking would not be done on the arguments.

I don't know how C++ inlining works but one of the long-standing things
on my wishlist is:

inline putc(c, fp)
char c;
FILE *fp;
{
	if(--fp->_cnt >= 0)
		*fp->ptr++ = c;
	else
		_flsbuf(c, fp);
}
-- 
Peter da Silva  `-_-'  Ferranti International Controls Corporation.
"Have you hugged  U  your wolf today?"            peter@ficc.uu.net

gateley@mips.csc.ti.com (John Gateley) (10/02/88)

In article <956@l.cc.purdue.edu> cik@l.cc.purdue.edu (Herman Rubin) writes:
>What is needed is something like
>#define fc(x,y) (LOCAL tmp1,tmp2; tmp1=x; tmp2=y; fc(x,y)=tmp1*tmp1+tmp2*tmp2)
>which should be interpreted correctly even if x or y happens to be tmp1 or tmp2.
>I do not know to what extent compiler implementations do this, but I doubt
>that a #define is treated other than a macro expansion, and I do not see
>how the problem can be avoided without something like this.

The ONLY place I have heard of this being done for macros is in the
programming language Scheme: using Eugene Kohlbecker's et al. beta-expansion
macro expander, or more recently Johnathon Rees's syntactic closures. I think
you are out of luck with C. What might help is a generate symbol operation.
This should give you a brand new symbol each time it is called. You might
be able to fake it in C somehow.

John Gateley
gateley@mips.csc.ti.com

gwyn@smoke.ARPA (Doug Gwyn ) (10/03/88)

In article <8078@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
>	z = square(*p++) + square(getchar());
>I dislike the original macro, but the fact remains that there is no good way
>to write this in C.

One wonders whether this is much of a problem.
(Note that the example is silly.)

There have been many occasions when I've needed the sum of two squares,
but I've never felt the need for the ability to define a "square" macro
in order to compute that.

gwyn@smoke.ARPA (Doug Gwyn ) (10/03/88)

In article <7191@aw.sei.cmu.edu> firth@bd.sei.cmu.edu (Robert Firth) writes:
>The question is, why would anyone ever even consider writing a function
>call?  Answer: it is not only safer than the C code, it's faster.

Not necessarily, but it can be.  Also, I know of at least one compiler
that will expand a function in-line whenever it would be efficient to
do so (and when C permits it).

rob@kaa.eng.ohio-state.edu (Rob Carriere) (10/04/88)

In article <8590@smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>One wonders whether [the inability to define a square macro] is much of a
>problem.

How about the following, deep in some inner loop:

foo = square( sin( x )) + 1.7;

I *don't* want to write:

foo = sin( x )*sin( x ) + 1.7;

and

temp = sin( x );
foo = temp*temp + 1.7;

is clearly less legible.

Rob Carriere

djones@megatest.UUCP (Dave Jones) (10/04/88)

From article <701@accelerator.eng.ohio-state.edu), by rob@kaa.eng.ohio-state.edu (Rob Carriere):

) 
) How about the following, deep in some inner loop:
) 
) foo = square( sin( x )) + 1.7;
) 
) I *don't* want to write:
) 
) foo = sin( x )*sin( x ) + 1.7;
) 
) and
) 
) temp = sin( x );
) foo = temp*temp + 1.7;
) 
) is clearly less legible.
     ^^^^^^^ ^^^^ ^^^^^^^

To me it is clearly much more legible, becuase I don't have to worry
about whether "square" has side-effects!  I would write it like this:


    { register double sin_x = sin(x);
      foo = (sin_x * sin_x)  + 1.7;
    }

Always restrict the scope of variables as strictly as possible.
In the above, the reader (and the compiler!) knows that the variable 
sin_x is never used except on those two lines.

(If the compiler were smart enough to know that sin() has no side-effects,
 it could transform " foo = sin(x)*sin(x) + 1.7 " into the code I
 prefer by means of "common subexpression removal".)

english@stromboli.usc.edu (Joe English) (10/04/88)

In article <701@accelerator.eng.ohio-state.edu> rob@kaa.eng.ohio-state.edu (Rob Carriere) writes:
>In article <8590@smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>>One wonders whether [the inability to define a square macro] is much of a
>>problem.
>
>How about the following, deep in some inner loop:
>foo = square( sin( x )) + 1.7;
>I *don't* want to write:
>foo = sin( x )*sin( x ) + 1.7;
>

Why not?  Unless you're using square() for legibility, the macro
expansion is going to be sin(x) * sin(x) anyway.

As long as I'm posting, I might as well throw in my two cents' worth, too:
I never, ever write a macro whose behaviour is not identical to a
function call (or to a constant), simply because of the dangers of
side-effects being performed twice.  If I want the "right" syntax, I
write it as a function, and if I need optimization, I expand it
manually.  

Tightly optimized code is not always pretty, nor does it have to be.
The best solution I've found when writing code optimized to the point
of obfuscation is to precede it with a comment:

/* WARNING: THE FOLLOWING CODE LOOKS AWFUL! DFWI! */ 

It doesn't enhance the readability, but it at least reminds me to be
careful when modifying it.

--
      /|/| "What do you look for in a quote?"
-----< | |                                   --Jay Hinkelman
  O   \|\| english%lipari@oberon.usc.edu

edp@pyr.gatech.EDU (Warren Tucker) (10/05/88)

In article <701@accelerator.eng.ohio-state.edu> rob@kaa.eng.ohio-state.edu (Rob Carriere) writes:
>How about the following, deep in some inner loop:
>foo = square( sin( x )) + 1.7;
>I *don't* want to write:
>foo = sin( x )*sin( x ) + 1.7;
>and
>temp = sin( x );
>foo = temp*temp + 1.7;
>is clearly less legible.
>
>Rob Carriere
It may be less legible to use 'temp' but sin(x)*sin(x) calculates the
sine of x twice.

-- 
Warren Tucker
...!{akgua,allegra,amd,hplabs,ihnp4,seismo,ut-ngp}!gatech!gitpyr!edp

gwyn@smoke.ARPA (Doug Gwyn ) (10/05/88)

In article <701@accelerator.eng.ohio-state.edu> rob@kaa.eng.ohio-state.edu (Rob Carriere) writes:
>In article <8590@smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>>One wonders whether [the inability to define a square macro] is much of a
>>problem.
>How about the following, deep in some inner loop:
>foo = square( sin( x )) + 1.7;

Another silly example.  I can't imagine a real algorithm that would want
this computation in it.

>temp = sin( x );
>foo = temp*temp + 1.7;
>is clearly less legible.

Most programmers wouldn't have any trouble with
	foo = sin(x);
	foo = foo*foo + 1.7;

I agree that an exponentiation operator would be handy,
but a "square" macro doesn't seem to help readability.

rpw3@amdcad.AMD.COM (Rob Warnock) (10/05/88)

The first "implementation language" I ever used (*not* counting FORTRAN!)
was BLISS-10 (circa 1971 on a PDP-10). BLISS is an "expression language",
meaning that almost every construct has a value. If you have trouble with
this, imagine if the ";" had the same semantics as C's "," operator. So
the value of a "block" (a BEGIN-END pair, like C's {} pairs) was the value
of the last expression (C's "statement") in it. Since blocks can have
declarations of local variables, this macro problem we've seen so much
comment on was trivial (shown using C-like syntax, not real BLISS):

#define square(x) (int sqr_tmp, sqr_tmp = (x), sqr_tmp * sqr_tmp )
	...
	foo = square(x++);		/* x**2, with only one increment of x */
	foo = square(square(x++));	/* x**4, with only one increment of x */

The "sqr_tmp" is local to the body of the macro, and, as in C, inner
declarations override outer ones.

What was really nice about BLISS was that all of IF, CASE, WHILE, etc., had
values, too. (The value of a loop was "-1" if the loop terminated "normally",
and the argument of a "LEAVE" [like C's "break"] if that was used instead.)

It is irritating in C that you can't have declarations on the left-hand side
of a "," operator...

Rob Warnock
Systems Architecture Consultant

UUCP:	  {amdcad,fortune,sun}!redwood!rpw3
ATTmail:  !rpw3
DDD:	  (415)572-2607
USPS:	  627 26th Ave, San Mateo, CA  94403

steve@oakhill.UUCP (steve) (10/05/88)

<1401@devsys.oakhill.UUCP> <8629@smoke.ARPA>

In case you don't remember, I was the one who first brought up the use of temp
in this discussion.  At that point in time the discussion was the technique
on writing macros, not the technique on writing the macro square.  Most
comments on why square is not sane to write this way miss the point of the
original discussion.  Square was just a trivial example.  A similar macro I
have used have involved setting bit fields, where the variable holding the
bit also is needed later in the bit field calculation.  Using a macro to do
this covers up a messy equation, and makes it more obvious what going on.
Now lets get back to the original discussion.

As I pointed out at that time, the use of temp was not optimal.  The two
big draw backs are :
  1) It is inefficient. Thus should be used only in the case where a change in
     a variable in the equation is likely and will change the result 
     (square(x++)), or where the variable could be an expression that is
     costly to calculate (square((cos(x)/sin(x))).
  2) the use of a temp which must be supplied.  The second I have gotten
     around by suppling the temp as a parameter to the macro (as someone has
     mentioned and I was remiss in not mentioning. (I figured everyone would
     see this problem, and be able to program around it their own way)

The major point of my posting was two-fold.  First to warn people they need
to think before they macro-ed.  And also to point out that the proposed
solution had drawbacks.  I conclude with the statement I concluded my last
posting with :

> Use your best judgement - macros are dangerous thing if not used carefully
> That is why C compilers have a -P option.

                   enough from this mooncalf - Steven
----------------------------------------------------------------------------
These opinions aren't necessarily Motorola's or Remora's - but I'd like to
think we share some common views.
----------------------------------------------------------------------------
Steven R Weintraub                        cs.utexas.edu!oakhill!devsys!steve
Motorola Inc.  Austin, Texas 
(512) 440-3023 (office) (512) 453-6953 (home)
----------------------------------------------------------------------------

rob@phao.eng.ohio-state.edu (Rob Carriere) (10/05/88)

In article <855@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
>From article <701@accelerator.eng.ohio-state.edu), by rob@kaa.eng.ohio-state.edu (Rob Carriere):
>)[...]
>) temp = sin( x );
>) foo = temp*temp + 1.7;
>) 
>) is clearly less legible.
>     ^^^^^^^ ^^^^ ^^^^^^^
>
>To me it is clearly much more legible, becuase I don't have to worry
>about whether "square" has side-effects!

I should have stated the assumption that ``square'' was some
well-defined system function/macro/whatever (don't leave your math
library without one!).  Otherwise, you sure have a good point.

>Always restrict the scope of variables as strictly as possible.

Amen.

>(If the compiler were smart enough to know that sin() has no side-effects,
> it could transform " foo = sin(x)*sin(x) + 1.7 " into the code I
> prefer by means of "common subexpression removal".)

Yes, but I can't rely on the compiler being smart, unless I like nasty
surprises.  Also, while I can check ``sin(x) is the same as sin(x)''
readily enough, it rapidly gets worse with more complicated
expressions.  This is essentially the same thing the *= operator
&friends solve for assignments.  In the same vein, if the expression
gets nasty enough that I need several temporaries, I may get to the
point where I can't see the expression for the temporaries.

Rob Carriere

rob@phao.eng.ohio-state.edu (Rob Carriere) (10/06/88)

In article <12546@oberon.USC.EDU> english@stromboli.usc.edu (Joe English) 
writes:
>In article <701@accelerator.eng.ohio-state.edu> rob@kaa.eng.ohio-state.edu
>(Rob Carriere) writes:
>>I *don't* want to write [in some inner loop]
>>foo = sin( x )*sin( x ) + 1.7;
>Why not?  Unless you're using square() for legibility, the macro
>expansion is going to be sin(x) * sin(x) anyway.

That was the point of the original discussion.  Needlessly evaluating
transcendental functions twice is generally frowned upon in numerical
circles. 

Rob Carriere

djones@megatest.UUCP (Dave Jones) (10/06/88)

From article <1527@devsys.oakhill.UUCP>, by steve@oakhill.UUCP (steve):
> <1401@devsys.oakhill.UUCP> <8629@smoke.ARPA>
> 
> In case you don't remember, I was the one who first brought 
> up the use of temp in this discussion.  At that point in time the 
> discussion was the technique on writing macros, not the technique on 
> writing the macro square.  Most comments on why square is not 
> sane to write this way miss the point of the
> original discussion.  Square was just a trivial example...


Fair enough.  Things have gotten off the track a little.  I posted
a reply because I hate those silly little macros that send me poking
through .h files for no good reason, and fool my debugger.

Still, there are plenty of uses for macros, and YES!, even temps in
macros.

I have a memory allocation package that despenses memory packets of
a predetermined size.  It gets them wholesale from malloc, and then
keeps them in a linked list. It can improve the speed
of some kinds of programs very dramaticly, making some run hundreds
of times faster than they would if they called the libc's malloc()
for every heap-packet. (The malloc in Sun3 bsd 4.2, rel 3.4 libc gets 
slower and slower as heap usage increases, finally almost coming to a 
stop. I don't know why.)

The stategy helps so much that the procedure call overhead
for Heap_alloc() and Heap_free() can become significant.  So, in 
addition  to the regular proceudre versions (for debugging), the 
library provides two macros.  Problem is, they need a temp-variable 
for handling the free-list.  The solution is to define a "tmp" in 
the structure which defines the heap.

/*
** RCSHeader = $Header: heap.h,v 1.1 88/08/24 22:21:50 langmgr Exp $
** Copyright = Copyright (C) 1988 by Megatest Corporation All Rights Reserved
** TheAuthor = Dave Jones   (djones at goofy)
*/
#ifndef HEAP_H_

#include "smalloc.h"

typedef struct Heap_unit  
{
  struct Heap_unit* next;
  /* More memory allocated here... */
} Heap_Unit;

typedef struct heap  /* implements heap of packets of a fixed size */
{
  int packet_size;      /* in units of sizeof(Heap_unit) */
  int num_elements;     /* number of packets to allocate in next block */
  Heap_Unit* cache;     /* queue of all blocks of packets */
  Heap_Unit* next_free; /* free-list of packets */
  Heap_Unit* tmp;       /* temporary. used in alloc and free */
}Heap; 

extern Ptr Heap_underflow();

#define Heap_alloc(obj) \
  ((obj)->next_free == 0 ? \
      Heap_underflow(obj): \
      (Ptr) ( (obj)->tmp = (obj)->next_free, \
	      (obj)->next_free = (obj)->next_free->next, \
	      (obj)->tmp \
	    )   \
   )

#define Heap_free(obj,packet) \
 ((void)( (obj)->tmp = (obj)->next_free,  \
          (obj)->next_free = (Heap_Unit*)packet, \
          ((Heap_Unit*)packet)->next = (obj)->tmp \
        ) \
 )


/* allocate new heap */
extern Heap* Heap_new(/* size, init_size */)

/* dispose of heap allocated with Heap_new() */
void Heap_dispose(/* obj */)
/*   Heap *obj; */

/* Initialize a heap (static or automatic, for example) */
Heap* Heap_init(/* obj, size, init_size */)
/*  register Heap* obj; */

/* Clean up a heap initialized by Heap_init() */
extern Heap* Heap_clean(/* obj */);
/* register Heap* obj; */


#define HEAP_H_
#endif HEAP_H_

rob@kaa.eng.ohio-state.edu (Rob Carriere) (10/06/88)

In article <8629@smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <701@accelerator.eng.ohio-state.edu> rob@kaa.eng.ohio-state.edu 
>(Rob Carriere) writes:
>>In article <8590@smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>>>One wonders whether [the inability to define a square macro] is much of a
>>>problem.
>>How about the following, deep in some inner loop:
>>foo = square( sin( x )) + 1.7;
>
>Another silly example.  I can't imagine a real algorithm that would want
>this computation in it.

If you leave off the ``+1.7'' that was there for decoration anyway,
try an FFT.

>Most programmers wouldn't have any trouble with
>	foo = sin(x);
>	foo = foo*foo + 1.7;
>
>I agree that an exponentiation operator would be handy,
>but a "square" macro doesn't seem to help readability.

1) If you have a text in front of you that says sin(x)^2 + 1.7 (LaTeX
   notation, not C), then there *is* a problem with the temporary: you
   have to go back and check it every time you read the statement.  If
   the sin(x) gets more complicated, this just about destroys
   readability (I've had cases where it took pencil, paper and five
   minutes of algebra to verify that all the temporaries did what they
   were supposed to do -- not my definition of high readability)
2) I used the ``square'' macro, because that was the original example.
   I agree that what is needed is a way of doing small integer powers
   in general, but that just changes the problem to: why can't I write
   power( sin(x), 2 ) [1] or some such.

Rob Carriere
[1] not pow( sin(x), 2).  I want something that will become 1
    evalution of sin(x) and 1 multiply.

gwyn@smoke.ARPA (Doug Gwyn ) (10/06/88)

In article <716@accelerator.eng.ohio-state.edu>, rob@kaa.eng.ohio-state.edu (Rob Carriere) writes:
> 2) I used the ``square'' macro, because that was the original example.
>    I agree that what is needed is a way of doing small integer powers
>    in general, but that just changes the problem to: why can't I write
>    power( sin(x), 2 ) [1] or some such.
> [1] not pow( sin(x), 2).  I want something that will become 1
>     evalution of sin(x) and 1 multiply.

Ah, now we get down to the real functional need.
If you use a "static" function for this, a really good optimizing
compiler can (a) turn the "pure" function into an in-line expansion
and (b) use the constant 2 or whatever to unroll the loop.  This is
a round-about way of obtaining the effect that a Fortran compiler
could also do at compile time for an instance of its ** operator.
Many C compilers aren't that good but a couple seem to come close
enough that maybe they would produce code as good as Fortran.

wes@obie.UUCP (Barnacle Wes) (10/07/88)

In article <8078@haddock.ima.isc.com> karl@haddock.ima.isc.com (Karl Heuer) writes:
> 	z = square(*p++) + square(getchar());
> I dislike the original macro, but the fact remains that there is no good way
> to write this in C.

In article <8590@smoke.ARPA>, gwyn@smoke.ARPA (Doug Gwyn ) replies:
| One wonders whether this is much of a problem.
| (Note that the example is silly.)
| 
| There have been many occasions when I've needed the sum of two squares,
| but I've never felt the need for the ability to define a "square" macro
| in order to compute that.

This code fragment was taken from a function that calculates the true
wind speed and heading based on the apparent wind speed and angle, and
the speed and course of a sailboat.  Note the last line:

       /*
	* Now return the true wind vector to normal coordinates.
	*/
	tw.heading = atan2(tw.ycomp, tw.xcomp);
	tw.speed = sqrt((tw.ycomp * tw.ycomp) + (tw.xcomp * tw.xcomp));

This seems the obvious way to perform such a calculation to me.  If
any of you net.c.programmers really can't stand this style, please tell
me why!
-- 
                     {hpda, uwmcsd1}!sp7040!obie!wes

         "How do you make the boat go when there's no wind?"
                                 -- Me --

gwyn@smoke.ARPA (Doug Gwyn ) (10/09/88)

In article <207@obie.UUCP> wes@obie.UUCP (Barnacle Wes) writes:
>	tw.heading = atan2(tw.ycomp, tw.xcomp);
>	tw.speed = sqrt((tw.ycomp * tw.ycomp) + (tw.xcomp * tw.xcomp));

A perfect opportunity to use the hypot() function.
Unfortunately X3J11 chose not to standardize it, but if it doesn't
exist on some system you can easily provide one that works no worse
than the explicit code given above.  (A good implementation of
hypot is more accurate and robust.)

john13@garfield.MUN.EDU (John Russell) (10/11/88)

In article <1527@devsys.oakhill.UUCP> steve@oakhill.UUCP (steve) writes:
><1401@devsys.oakhill.UUCP> <8629@smoke.ARPA>
>As I pointed out at that time, the use of temp was not optimal.  The two
>big draw backs are :
>  1) It is inefficient. 
>  2) the use of a temp which must be supplied.  

How about this for an addition to C: a special keyword, similar to const,
which declares that a function which is passed identical values will always
return the same value, and that the function has no side effects.

Example:

consistent double cos();

#define square(x) ((x) * (x))

a = PI / 2.0;
b = square(cos(a));

The compiler knows "a" does not change in the expanded expression, so it uses
its own temp and only calls cos once.

Is this feasible? Doesn't sound too hard...

John
-- 
"...and intuition, in a case such as this, is of crucial importance."
			-- William Gibson, _Count_Zero_

jhh@ihlpl.ATT.COM (Haller) (10/12/88)

In article <4920@garfield.MUN.EDU>, john13@garfield.MUN.EDU (John Russell) writes:
> How about this for an addition to C: a special keyword, similar to const,
> which declares that a function which is passed identical values will always
> return the same value, and that the function has no side effects.

The information to do this is already available, and a truely optimizing
compilation system should do this.  Note that I said compilation system,
not compiler, where a makefile for a product would be the likely input.
When compiling cos(), the compiler should be able to take note of
there being no side effects, and take appropriate actions if
cos is not re-defined within an application.

In the example,
	consistent double cos();
	#define square(x) ((x) * (x))
	a = PI / 2.0;
	b = square(cos(a));

the compilation system should be able to detect that 'a' is set from
a constant, call cos(a) at compile time, perform the square are compile
time, and reduce the fragment to a=1.57..; b=0.0;, all without the need
for 'consistent'.

This optimization may be beyond the capability of current C compilers,
but we should not be introducing new keywords to solve problems that
should be solved with compilation technology in the future.

John Haller
AT&T Bell Laboratories

firth@sei.cmu.edu (Robert Firth) (10/18/88)

In article <716@accelerator.eng.ohio-state.edu>, rob@kaa.eng.ohio-state.edu (Rob Carriere) writes:

[The need for a maintainable and efficient way to write things like
   y := sin(x)^2 +1.7
]

I agree fully with Mr Carriere that most of the suggestions for hand
optimising this kind of thing are unworkable.  Scientific programs
typically contain not simple statements like the above, but statements
that run over three or four lines, full of sin(x)^n, sin(x)^(n-1),
2*sin(x-d)*cos(y+d), and so on, and on, and on.  These formulae have
been laboriously copied from a reference book or article, and they
are completely unmaintainable unless they follow as closely as possible
the original form.  In my experience, even something as simple as
multiplying two polynomials and simplifying the result can take an
hour or more to check thoroughly.

Let the compiler do the work, please.  As a corollary: use a language
sufficiently powerful that you can give the compiler enough information
to enable it to do the work.

(Aside: I stuffed the above expression into an Algol-60/Vax compiler,
 and duly got

	MOVF x,R0
	BSBW sin
	MULF2 R0,R0
	ADDF3 #1.7,R0,y
)

alexis@reed.UUCP (Alexis Dimitriadis) (10/23/88)

Whoever claimed to NEVER write macros that do not behave just like functions
(i.e., use their argument more than once, modify an argument, or reference
local variables) has good reason for doing so.  Still, features
unique to macros are sometimes the most convenient way to get from here to
there.  

Yes, there are pitfalls that should be scrupulously avoided.  
If my macro-with-arguments might not behave like a function, I make sure 
it does not _look_ like one:  I spell its name in uppercase:

#define MAX(x, y) (((x) > (y)) ? (x) : (y))

Am I naive in expecting any programmer to check the definition before using
something named MAX() with a "dangerous" argument like ++i?  Probably...

BTW, I nevertheless feel that using a separately-defined local variable is
BAD form.  If I need an extra variable, I restrict its scope:

#define SWAP(x,y) do{ long tmp = x; x = y; y = tmp; } while (0)

You cannot do that with a function! (Of course, you cannot use the above in
an expression...)

Thank you for reading through my $0.02 worth; and forgive me if I just
brought up something that got thrashed to death three months ago.

Alexis Dimitriadis
tektronix!reed!alexis