Нет.Вариант C поставляется со всеми недостатками #define
-macros.Напоминание:
#define LERP(a,b,c) (((b) - (a)) * (c) + (a))
Потенциальная потеря производительности
Представьте себе чистый вызов функции при вызове #define
-macro:
int fac (int x) {
return x<=1 ? 1 : x*fac(x-1);
}
int main () {
std::cout << LERP(fac(5), fac(2), 0);
}
Эта строка расширяетсяto:
std::cout << (((fac(2)) - (fac(5))) * (0) + (fac(5)))
Теперь вы потенциально удвоили время выполнения для двух вызовов вашей факультативной функции, которая, по-видимому, была всего одна.
Это, безусловно, ухудшается, если вы вкладываете свой лепрпинг, как дляЭкземпляр часто встречается в некоторых ситуациях графического программирования:
int main () {
std::cout << LERP(
LERP(fac(5), fac(2), 0),
LERP(fac(5), fac(2), 0),
0
);
}
Расширение до:
int main () {
std::cout << LERP(
(((fac(2)) - (fac(5))) * (0) + (fac(5))),
(((fac(2)) - (fac(5))) * (0) + (fac(5)))
0
);
}
Расширение до (форматирование настроено для удобства чтения):
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)))))
}
Принимая во внимание чистотувычислительная версия не более:
float a = LERP(fac(5), fac(2), 0);
float b = LERP(fac(5), fac(2), 0);
float c = LERP(a,b,0);
или
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;
Итак, в двумерной установке
- Правильная версия:
- 4 вызова на
fac()
- 4 сложения
- 2 умножения
- ´ # define` версия:
- 9 вызовов на
fac()
- 8 сложений
- 4 умножения
Получается экспоненциальноХуже с каждым измерением, которое вы бы добавили.Иногда видны даже пятимерные Perlin Noise (3d том + время + непрерывное начальное число), , для которых некоторые выражения оцениваются как ошеломляющие 31 раз, а не только один раз! :
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)
Вы также можете увидеть предварительно обработанный код, вызвав cpp
(обратите внимание на одиночное появление probe()
перед).
foo@bar:~/ cpp heavy.cc
[snip] (((((((((((((((((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)) * (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) - (0)) * (2) + (0)))) * (6) + ((((1) - (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)) * (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) - (пробник ())) * (2) + (пробник ())))) * (6) + (((((1) - (зонд ())) * (2) + (зонд ())))))) * (14) + (((((((4)- (3)) * (5) + (3))) - ((((1) - (пробник ())) * (2) + (пробник ())))) * (6) + ((((1) - (пробник ())) * (2) + (пробник ())))))))) * (1014) + ((((((((((11) - (10)) *(12) + (10))) - ((((8) - (7)) * (9) + (7)))) * (13) + ((((8) - (7)) * (9) + (7))))) - (((((((4) - (3)) * (5) + (3))) - ((((1) - (пробник ())) *(2) + (датчик ())))) * (6) + ((((1) - (датчик ())) * (2) + (зонд ())))))) * (14) + ((((((((4) - (3)) * (5) + (3))) - ((((1) - (датчик ())) * (2) + (датчик ())))) * (6) + ((((1) - (датчик ())) * (2) + (датчик ())))))))))) * (666) + (((((((((((((((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)) * (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) - (датчик ())) * (2) + (датчик ())))) * (6) + ((((1) - (датчик())) * (2) + (зонд ())))))) * (14) + (((((((4) - (3)) * (5) + (3))) - ((((1) - (зонд ())) * (2) + (зонд ())))) * (6) + ((((1) - (зонд ())) * (2) + (зонд())))))))) * (1014) + (((((((((((11) - (10)) * (12) + (10))) - ((((8) -(7)) * (9) + (7)))) * (13) +((((8) - (7)) * (9) + (7))))) - (((((((4) - (3)) * (5) + (3))) - ((((1) - (зонд ())) * (2) + (зонд ())))) * (6) + ((((1) - (зонд ())) * (2) + (зонд())))))) * (14) + (((((((4) - (3)) * (5) + (3))) - ((((1) - (пробник ())) * (2) + (пробник ())))) * (6) + ((((1) - (пробник ())) * (2) + (пробник ())))))))))
Опять же, полный источник здесь .
Потенциальное неопределенное поведение
Вы можете поместить в него злые вещи:
LERP(++a,b,c)
, который расширяется до
(((b) - (++a)) * (c) + (++a))
, что является неопределенным поведением в C¹(и в C ++, кстати).a
может быть увеличено в два раза, или может быть увеличено один раз, или может быть сгенерировано исключение, которое говорит Debug-Runtime-Exception: Invoked Undefined Behaviour
, или компилятор достаточно умен, чтобы отклонить этот код, или что-то еще.
Неопределенное поведениеисходит из того факта, что стандарт C99 (и C ++ 2003 тоже) не допускает многократного изменения значения до достижения следующей точки последовательности .
ID загрязнения и инфекции
(Это более уместно, если вы преобразуете C # в вариант макроса.)
* Макро-имя #define
загрязняет и заражает всю единицу перевода от точки определения до конца единицы или ее неопределенности.
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
Подробнее ...
- Макросы являются общими, но не безопасными. Макро-писатель не имеет (чистой) возможности накладывать ограничения на типы, для которых этот макрос должен быть допустимым.
- Макросы не имеют области видимости, хотя это уже подразумевается в последнем разделе
¹: C99 (ISO / IEC 9899: TC2), J.2, «Неопределенное поведение»: 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).