В чем недостаток использования препроцессора для определения вызова функции? - PullRequest
6 голосов
/ 18 января 2010

Хотелось бы узнать, в чем минусы использования препроцессора таким образом:

#define SOME_FUNCTION someFunction(someArgument)

В основном я чувствую, что это неправильно (или определенно не лучшая практика) - но я не уверен, почему ... мои навыки препроцессора в лучшем случае ржавые.

Ответы [ 9 ]

5 голосов
/ 18 января 2010

Проблема в том, что аргументы пересматриваются каждый раз, когда они используются:

#define MIN(A,B)   ((A) < (B))?(A):(B);

Обратите внимание, что мне нужно обернуть все аргументы в '(' ')', чтобы убедиться, что выражение вычисляется корректно. Но что произойдет, если мы сделаем это?

int  s = MIN(++current,Max);

Кодируя это, я ожидаю, что ток будет увеличен один раз перед вызовом функции. Но поскольку это макрос, он увеличивается один раз в тесте и второй раз, если он все еще меньше, чем Max

5 голосов
/ 18 января 2010

Недостаток?Обычно определение макроса не попадает в таблицу символов исполняемого файла.Чуть сложнее отлаживать.

2 голосов
/ 18 января 2010

Есть несколько проблем, о которых вы можете подумать:

  • В C ++ макрос не имеет пространства имен и области видимости, поэтому он везде одинаков. Примером этого являются неудачные min и max, которые определены где-то в windows.h. Если вы программируете для windows и включаете windows.h и хотите написать std :: numeric_limits :: max (), тогда max будет заменен некоторым кодом ... Это оставляет некомпилируемый код после запуска препроцессора. (Хорошо, есть способы отключить макросы мин / макс в windows.h, но это все еще плохой дизайн!)
  • Макрос не может быть хорошо отлажен. Отладчик остановится на строке, в которой макрос используется, а не на коде внутри макроса ...
  • Возможна переоценка параметров макроса (вы могли бы предотвратить это, поместив блок с локальными переменными внутри макроса, но это сделало бы отладку еще хуже!)
1 голос
/ 18 января 2010

Что ж, если вы должны это сделать (и в некоторых случаях это возможно), вы должны по крайней мере определить макрос как «подобный функции», таким образом:

#define SOME_FUNCTION() someFunction(defaultArgument)

в противном случае вы бы написали код, который выглядел как присвоение константе, когда это был фактически вызов функции; то есть;

x = SOME_FUNCTION ;  // hidden function call

но с "подобным функции" макросом вы должны написать:

x = SOME_FUNCTION() ;  // shorthand function-call with default argument

Что лучше соответствует синтаксису препроцессора с синтаксисом языка.

Как правило, макросов, подобных функциональным, лучше избегать, но некоторые более коварны, чем другие, это далеко не худший случай. Тем не менее, вы могли бы так же легко написать оболочку функции в C или в C ++, используя аргумент по умолчанию, и это было бы предпочтительнее в большинстве случаев.

0 голосов
/ 19 января 2010

Недостатком является то, что вы скрываете код.Плюс в том, что вы скрываете код.

Минус обычно превышает перевес.

Обычно этот конкретный подход в значительной степени бесполезен, и если вызов не выглядит как

someModule-> someStorage-> functionList [storage.getFunctionName] .pointer-> SomeFunction (... столь же неясный аргумент ...);

бессмысленно делать это.Если только аргумент является скрытым вызовом, сокращенно только аргумент.Если это только функция, сокращенная только функция.Если и то и другое, вам может быть лучше использовать

 SOME_FUNCTION(SOME_ARGUMENT);

Если функция никогда не вызывается с чем-либо еще, вы можете рассмотреть возможность ее удаления из списка аргументов и получения внутри тела функции.И если пара повторяется очень часто, в небольших вариациях вы можете рассмотреть функции-оболочки.

После того, как вы сделаете несколько ошибок в коде макроса, вы поймете, что их отладка - это боль, и вы не будете использоватьих легкомысленно.

0 голосов
/ 18 января 2010

Зачем вам искать недостатки в использовании конкретного кода препроцессора?Использование макросов препроцессора для чего-либо кроме условной компиляции (включая include guard) автоматически считается плохим.Правильный вопрос - есть ли преимущества для конкретного использования.

Макросы препроцессора никоим образом не учитывают объем или использование.Они могут испортить очень хороший код неожиданными и труднодоступными способами.Всегда избегайте их, если у вас нет веских причин для их использования.

0 голосов
/ 18 января 2010

Все проблемы в информатике могут быть решен другим уровнем Косвенная

Это один из следующих дополнительных уровней косвенности: -)

Скажите, что в какой-то момент этой функции требуется другой аргумент или вам вообще не нужно ее вызывать, вы можете изменить #define один раз вместо того, чтобы менять все места, где вызывается функция.

Полезно для разработки, но опасно хранить в рабочем коде ... Я бы запустил препроцессор, чтобы заменить это правило, когда у меня есть зрелый код и я знаю, что мне не нужно его менять.

0 голосов
/ 18 января 2010

Это будет зависеть от языка, компилятора и т. Д. И т. Д. И т. П. *

Тем не менее, в этом нет ничего плохого, как только название подразумевает, что эти директивы происходят ПРЕДЫДУЩИМ ПРОЦЕССОМ компиляции.

Препроцессор удаляет все константные ссылки по их действительному значению, все псевдофункции с правильным кодом и т. Д.

0 голосов
/ 18 января 2010

Это иногда полезно в очень закрытой области для определения шагов процесса в более читаемой форме:

void myfunc() {
  DO_STEP_ONE;
  THEN_ANOTHER_STEP;
  KEEP_GOING;
  LAST_STEP;
}

Но обычно это только затрудняет чтение и понимание кода.

Если только читатели вашего кода не узнают, что эти # определения означают подробно, и это своего рода краткий путь, вы просто заставляете людей искать в двух местах понять строку кода файл и в функции), а не один.

Я очень редко использую такой подход.

...