bernsten@phoenix.Princeton.EDU (Dan Bernstein) (05/15/89)
In article <2337@mit-caf.MIT.EDU> vlcek@mit-caf.UUCP (Jim Vlcek) writes: > In article <181@larry.sal.wisc.edu> jwp@larry.sal.wisc.edu.UUCP > (Jeffrey W Percival) writes: [if you rely upon .h files for function declarations, it is easy to later make a function into a macro] > >Is that interpretation unflawed? > If what was once a function is turned into a macro, programs which > passed arguments with side effects to the function may not work > correctly with the macro. You know the tune: > #define square(x) ((x)*(x)) /* Used to be a function ... needed speed */ > y = square(++i); > Any macro->function sleight of hand had best not take place behind the > scenes, and in fact should probably not take place at all. Going the > other direction is probably considered safe, although some programs > may have depended upon the side effects of the macro which is now a > function. Probably anyone who does so deserves to suffer, though... Oh dear. I should be burned at the stake for some of the macros I write. I even take functions like sqaure(in,out) int in; int *out; { *out = in * in; } and convert them into macros like #define square(in,out) { int Tsquare1 = in; int *Tsquare2 = out; *Tsquare2 = Tsquare1 * Tsquare1; } Gasp! Where's the error when I do square(i++,&result)? Where have the side effects gone? How come this macro does exactly the same thing as the function except that it's faster? And I would have thought from the above article that this was impossible! I have a tiny shell script that does the above conversion automatically; I suppose it's being inefficient when a variable is only used once (as in the above use of Tsquare2), but it basically does the job right. There are no side effects, it's all perfectly safe, and I don't think I deserve to suffer for it. Since C is not fully expression-based, it is impossible to do the above conversion for functions that you're using as functions rather than as procedures (i.e., functions that return a type and may not take up an entire statement). One can to some extent use the comma operator, but since one can't really declare local variables without { }, the results can't be nested. However, any procedural function can be quite safely converted into a macro. What we really want here is a feature often observed to be missing from C: inline functions. Inline functions need not be syntactically different from normal functions; perhaps we could add an ``inline'' keyword to a function declaration, with the restriction that the function must be defined there (in the header file). A different approach would be to make C fully expression-based, so that the comma operator would disappear in favor of semicolons; the idea is to allow all possible statement constructs as expression constructs. Of course, this must include local variables. ---Dan Bernstein, bernsten@phoenix.princeton.edu
vlcek@mit-caf.MIT.EDU (Jim Vlcek) (05/15/89)
A short while ago, Jeffrey Percival (in <181@larry.sal.wisc.edu>, noted that if one relies upon header files for function declarations, then one might later seamlessly change the function(s) into macros. He asked if this interpretation was unflawed. I pointed out, in <2337@mit-caf>, that the macros might have problems with arguments with side effects - the old ``square(++i)'' thing. I made a mistake in there, by the way. I said: ``Any macro->function sleight of hand had best not take place behind the scenes, and in fact should probably not take place at all. Going the other direction is probably considered safe, although some programs may have depended upon the side effects of the macro which is now a function. Probably anyone who does so deserves to suffer, though...'' That first line should read ``Any function->macro sleight of hand...'' Hopefully, this was clear already from the context. Which brings us to Dan Bernstein, in <8420@phoenix.princeton.edu>: ``Oh dear. I should be burned at the stake for some of the macros I write. I even take functions like sqaure(in,out) int in; int *out; { *out = in * in; } and convert them into macros like #define square(in,out) { int Tsquare1 = in; int *Tsquare2 = out; *Tsquare2 = Tsquare1 * Tsquare1; } ``Gasp! Where's the error when I do square(i++,&result)? Where have the side effects gone? How come this macro does exactly the same thing as the function except that it's faster? And I would have thought from the above article that this was impossible!'' OK, try this: if (p>0) square(p,&q); else q=0; It'll work if square() is a function, but fail if square() is a macro as you've defined. Can you find the syntax error? Further, as Dan points out later, this is limited to functions or macros which do not return a value, and hence is only a special case. It's for these reasons that I never define any macros as compound statements. Back to Dan: ``I have a tiny shell script that does the above conversion automatically; I suppose it's being inefficient when a variable is only used once (as in the above use of Tsquare2), but it basically does the job right. There are no side effects, it's all perfectly safe, and I don't think I deserve to suffer for it.'' My point about suffering was perhaps somewhat obscure. I said that it was probably OK to change a macro into a function, although the behavior could not be guaranteed to be identical in that the function would evaluate its arguments only once, whereas the macro would evaluate each argument as many times as it appeared. My point was that anyone who depended upon the side effects ensuing from multiple evaluations of a macro argument probably deserves to suffer. Perhaps someone can show a reasonable counterexample, however. Jim Vlcek (vlcek@caf.mit.edu uunet!mit-caf!vlcek)
laba-3hd@tarantula.berkeley.edu (Sam Shen) (05/16/89)
In article <8420@phoenix.Princeton.EDU> bernsten@phoenix.Princeton.EDU (Dan Bernstein) writes: Since C is not fully expression-based, it is impossible to do the above conversion for functions that you're using as functions rather than as procedures (i.e., functions that return a type and may not take up an entire statement). One can to some extent use the comma operator, but since one can't really declare local variables without { }, the results can't be nested. However, any procedural function can be quite safely converted into a macro. GNU C allows this. From the gcc manual: A compound statement in parentheses may appear inside an expression in GNU C. This allows you to declare variables within an expression. For example: ({ int y = foo (); int z; if (y > 0) z = y; else z = - y; z; }) is a valid (though slightly more complex than necessary) expression for the absolute value of foo(). The manual goes on to note that with the typedef extension, we can do really neat things like: #define max(a,b) \ ({typedef _ta = (a), _tb = (b); \ _ta _a = (a); _tb _b = (b); \ _a > _b ? _a : _b; }) Anyhow Dan Bernstein dreams on... What we really want here is a feature often observed to be missing from C: inline functions. Inline functions need not be syntactically different from normal functions; perhaps we could add an ``inline'' keyword to a function declaration, with the restriction that the function must be defined there (in the header file). A different approach would be to make C fully expression-based, so that the comma operator would disappear in favor of semicolons; the idea is to allow all possible statement constructs as expression constructs. Of course, this must include local variables. ---Dan Bernstein, bernsten@phoenix.princeton.edu GNU C has this too. We could say inline int max(int a, int b) { return a > b ? a : b; } But then we would have the automatic typing of the macro above. -Sam Shen laba-3hd@web.berkeley.edu -- ---------- Sam Shen resident of 260 Evans Hall, UCB, 94720. ...!ucbvax!web!laba-3hd | "Say yur prayers, yuh flea-bitten varmit!" laba-3hd@web.berkeley.edu | --Yosemite Sam
tps@chem.ucsd.edu (Tom Stockfisch) (05/16/89)
In article <8420@phoenix.Princeton.EDU> bernsten@phoenix.Princeton.EDU (Dan Bernstein) writes: #In article <2337@mit-caf.MIT.EDU> vlcek@mit-caf.UUCP (Jim Vlcek) writes: ## In article <181@larry.sal.wisc.edu> jwp@larry.sal.wisc.edu.UUCP ## You know the tune: ## #define square(x) ((x)*(x)) /* Used to be a function ... needed speed */ ## y = square(++i); ## Any macro->function sleight of hand had best not take place behind the ## scenes, and in fact should probably not take place at all.... #I even take functions like # # sqaure(in,out) int in; int *out; { *out = in * in; } # #and convert them into macros like # # #define square(in,out) { int Tsquare1 = in; int *Tsquare2 = out; # *Tsquare2 = Tsquare1 * Tsquare1; } # #Gasp! Where's the error when I do square(i++,&result)? Where have the #side effects gone? How come this macro does exactly the same thing as #the function except that it's faster? And I would have thought from the #above article that this was impossible! #There are no side effects, it's all perfectly safe, and I don't think #I deserve to suffer for it. Well, it's not *perfectly* safe. What if the poor user who includes square.h does # include "square.h" ... # define Tsquare1 foo; bar ... square(i++,&result); But I agree, it's safe enough to make it worth the remaining risk. And it would be nice to have C++'s inline facility. -- || Tom Stockfisch, UCSD Chemistry tps@chem.ucsd.edu
ned@pebbles.cad.mcc.com (CME Ned Nowotny) (06/20/89)
In article <8420@phoenix.Princeton.EDU> bernsten@phoenix.Princeton.EDU (Dan Bernstein) writes: =>In article <2337@mit-caf.MIT.EDU> vlcek@mit-caf.UUCP (Jim Vlcek) writes: =>> In article <181@larry.sal.wisc.edu> jwp@larry.sal.wisc.edu.UUCP =>> (Jeffrey W Percival) writes: => [if you rely upon .h files for function declarations, => it is easy to later make a function into a macro] =>> >Is that interpretation unflawed? =>> If what was once a function is turned into a macro, programs which =>> passed arguments with side effects to the function may not work =>> correctly with the macro. You know the tune: =>> #define square(x) ((x)*(x)) /* Used to be a function ... needed speed */ =>> y = square(++i); =>> Any macro->function sleight of hand had best not take place behind the =>> scenes, and in fact should probably not take place at all. Going the =>> other direction is probably considered safe, although some programs =>> may have depended upon the side effects of the macro which is now a =>> function. Probably anyone who does so deserves to suffer, though... => =>Oh dear. I should be burned at the stake for some of the macros I write. =>I even take functions like => => sqaure(in,out) int in; int *out; { *out = in * in; } => =>and convert them into macros like => => #define square(in,out) { int Tsquare1 = in; int *Tsquare2 = out; => *Tsquare2 = Tsquare1 * Tsquare1; } => =>Gasp! Where's the error when I do square(i++,&result)? Where have the =>side effects gone? How come this macro does exactly the same thing as =>the function except that it's faster? And I would have thought from the =>above article that this was impossible! => However, it is not much fun to debug code with something like the following call to square(): square( Tsquare1, &Tsquare2 ); Of course, the author would never use Tsquare[12] as variable names in code that calls square. However, other users of his header file might. There are variable name constructions which would be extremely unlikely to cause problems, but none guaranteed safe. Ned Nowotny, MCC CAD Program, Box 200195, Austin, TX 78720 Ph: (512) 338-3715 ARPA: ned@mcc.com UUCP: ...!cs.utexas.edu!milano!cadillac!ned ------------------------------------------------------------------------------- "We have ways to make you scream." - Intel advertisement in the June 1989 DDJ.