Not. Option C has all the disadvantages of #define -macros. Reminder:
Potential performance loss
Imagine calling #define -macro:
int fac (int x) { return x<=1 ? 1 : x*fac(x-1); } int main () { std::cout << LERP(fac(5), fac(2), 0); }
This line extends to:
std::cout << (((fac(2)) - (fac(5))) * (0) + (fac(5)))
Now you have potentially doubled the execution time for two calls to your faculty function, which was just one.
This, of course, gets worse if you nest your lerping, as is usually the case in some graphical programming situations:
int main () { std::cout << LERP( LERP(fac(5), fac(2), 0), LERP(fac(5), fac(2), 0), 0 ); }
Expansion to:
int main () { std::cout << LERP( (((fac(2)) - (fac(5))) * (0) + (fac(5))), (((fac(2)) - (fac(5))) * (0) + (fac(5))) 0 ); }
Extension to (formatting changed for readability):
int main () { std::cout << ( (((((fac(2)) - (fac(5))) * (0) + (fac(5)))) - ((((fac(2)) - (fac(5))) * (0) + (fac(5))))) * (c) + ((((fac(2)) - (fac(5))) * (0) + (fac(5))))) }
While the pure version is calculated no more than:
float a = LERP(fac(5), fac(2), 0); float b = LERP(fac(5), fac(2), 0); float c = LERP(a,b,0);
or
float fac_a = fac(5), fac_b = fac(2); float a = (fac_b-fac_a)*0 + fac_a; float fac_c = fac(5), fac_d = fac(2); float a = (fac_d-fac_c)*0 + fac_c;
So, in two-dimensional setup
- The correct version is:
- 4 calls
fac() - 4 additions
- 2 multiplications
- '# define` version:
- 9 calls
fac() - 8 additions
- 4 multiplications
It gets exponentially worse with each size added. Sometimes there is even a five-dimensional Perlin Noise (3D volume + time + continuous seed), for which some expressions are evaluated freaking 31 times, and not just once! :
LERP( LERP(LERP(LERP(LERP(probe(),1,2), LERP(3,4,5), 6), LERP(LERP(7,8,9), LERP(10,11,12), 13), 14), LERP(LERP(LERP(90,91,92), LERP(93,94,95), 96), LERP(LERP(97,98,99), LERP(910,911,912), 913), 914), 1014), LERP(LERP(LERP(LERP(0,1,2), LERP(3,4,5), 6), LERP(LERP(7,8,9), LERP(10,11,12), 13), 14), LERP(LERP(LERP(90,91,92), LERP(93,94,95), 96), LERP(LERP(97,98,99), LERP(910,911,912), 913), 914), 1014), 666)
You can also view the pre-processed code by calling cpp (pay attention to one kind of probe() ).
foo@bar:~/ cpp heavy.cc
(912) + (910))) - (((((98) - (97)) () () * (99) + (97)))) * (913) + (((98) - (97) ) * (99) + (97))))) - (((((((94) - (93)) * (95) + (93))) - (((91) - (90)) * ( 92) + (90)))) * (96) + ((((91) - (90)) * (92) + (90)))))) * (914) + ((((((94 ) - (93)) * (95) + (93))) - ((((91) - (90)) * (92) + (90)))) * (96) + ((((91) - (90)) * (92) + (90)))))))) - ((((((((((((11) - (10)) * (12) + (10))) - (( ((8) - (7)) * (9) + (7)))) * (13) + ((((8) - (7)) * (9) + (7))))) - ( (((((((4) - (3)) * (5) + (3)) (- (0)) * (2) + (0)) * (2) + (0)))))) * (14) + (((((((4) - (3)) * (5) + (3))) - (((1) - (0)) * (2) + (0)))) * (6) + (((1) - (0)) * (2) + (0))))))) * (1014) + (((((((((((11) - (10) ) * (12) + (10))) - (((((8) - (7)) * (9) + (7)))) * (13) + ((((8) - (7)) * (9) + (7))))) - (((((((4) - (3)) * (5) + (3))) - ((((1) - (0)) * ( 2) + (0)))) * (6) + (((((1) - (0)) * (2) + (0)))))) * (14) + ((((((4 ) - (3)) * (5) + (3))) - ((((1) - (0)) * (2) + (0)))) * (6) + (((1) - (0)) * (2) + (0)))))))))) - (((((((((((((((911) - (910)) * (912 ) + (910))) - (((98) - (97)) * (99) + (97)))) * (913) + (((98) - (97)) * 99) + (97 )))))) - ((((((((94) - (93)) * (95) + (93))) - (((91) - (90)) * (92) + (90))) ) * (96) + (((91) - (90)) * (92) + (90)))))) * (914) + ((((((((94 (93) - (93)) * (95) + (93))) - (((91) - (90)) * (92) + (90)))) * (96) + (((91) - (90)) * (92 ) + (90)))))))) - ((((((((((((11) - (10)) * (12) + (10))) - ((((8) - (7) ) * (9) + (7)))) * (13) + ((((8) - (7)) * (9) + (7))))) - (((((((4) - (3)) * (5) + (3))) - ((((1) - (probe ())) * (2) + (probe ())))) * (6) + (((( 1) - (probe ())) * (2) + (probe ())))))) * (14) + (((((((4) - (3)) * (5) + (3) )) - ((((1) - (probe ())) * (2) + (probe ())))) * (6) + ((((1) - (probe ())) * (2 ) + (probe ()))))))))) * (1014) + (((((((((((11) - (10)) * (12) + (10))) - ((( 8) - (7)) * (9) + (7)))) * (13) + (((((8) - (7)) * (9) + (7))))) - ((( ((((4) - (3)) * (5) + (3))) - ((((1) - (probe ())) * (2) + (probe ())))) * (6 ) + ((((1) - (probe ())) * (2) + (probe ())))))) * (14) + (((((((4) - (3)) * (5) + ( 3))) - ((((1) - (probe ())) * (2) + (probe ())))) * (6) + ((((1) - (probe ())) * (2) + (probe ()))) (912) + (910))) - (((98)) - ((910) + (910)) - (((98) (97)) * (99 ) + (97)))) * (913) + (((98) - (97)) * (99) + (97))))) - ((((((((94) - (93)) * (95) + (93))) - (((91) - (90)) * (92) + (90)))) * (96) + ((((91) - (90)) * ( 92) + (90)))))) * (914) + (((((((94) - (93)) * (95) + (93))) - (((91) - (90)) * (92) + (90)))) * (96) + ((((91) - (90)) * (92) + (90))))) () ((((8) - (7 )) * (9) + (7) - (7) - (10)) * (12) + (10)))) * (13) + ((((8) - (7)) * (9) + (7)))))) - ((((((((4) - (3)) * (5) + (3))) - ((((1) - (probe ())) * (2) + (probe ()))))) * (6) + ((((1) - (probe ())) * (2) + (probe ())))))) * (14) + ((( (((((4) - (3)) * (5) + (3))) - ((((1) - (probe ())) * (2) + (probe ()))) * ( 6) + ((((1) - (probe ())) * (2) + (probe ()) (& Plusmn;) - (10)) * (12) + (10))) - ((( (8) - (7))) * (9) + (7)))) * (13) + ((((8) - (7)) * (9) + (7))))) - ( (((((((4) - (3)) * (5) + (3))) - ((((1) - (probe ())) * (2) + ( probe ()))))) * (6) + (((((1) - (probe ())) * (2) + (probe ()) () () () () () () () ( ) (2) + (probe ())))) * (6) + ((((1) - (probe ())) * (2) + (probe ())))))))))))
Again, the full source is here .
Potential Undefined Behavior
You can put evil things into it:
LERP(++a,b,c)
which expands to
(((b) - (++a)) * (c) + (++a))
which is Undefined behavior in Cยน (and in C ++, btw). a can be doubled, or it can be doubled, or an exception can be thrown that says Debug-Runtime-Exception: Invoked Undefined Behaviour , or the compiler is smart enough to reject this code or something else.
Undefined behavior is due to the fact that C99-standard (and C ++ 2003) also does not allow changing the value several times before moving on to the next point in the sequence .
Identification and infection
(This is more relevant if you are converting C # to a macro variant.)
#define -macro-name pollutes and infects the entire translation unit from the definition point to the end of the block or its uncertainty.
foo@bar:~/ cat main.cc // Orbiter Physics Sim #include "lerp.h" int main () { const int LERP = 2; // Linear Extrasolar Resonance Potential. } foo@bar:~/ g++ main.cc main.cc:5:15: error: expected unqualified-id before '=' token
More details
- Macros are generic, but not typical. A macro writer does not have the (pure) ability to place restrictions on the types for which this macro must be valid.
- Macros do not have scope, although this is already implied in the last section.
ยน: C99 (ISO / IEC 9899: TC2), J.2, "Undefined Behavior": Between two sequence points, an object is modified more than once, or is modified and the prior value is read other than to determine the value to be stored (6.5).