Используйте #ifdefs и #define, чтобы при желании превратить вызов функции в комментарий - PullRequest
14 голосов
/ 13 февраля 2009

Можно ли сделать что-то подобное

#ifdef SOMETHING
#define foo //
#else
#define foo MyFunction
#endif

Идея состоит в том, что если SOMETHING определен, то вызовы foo (...) становятся комментариями (или чем-то, что не оценивается и не компилируется), в противном случае это становится вызовом MyFunction.

Я видел, как использовался __noop, но я не верю, что смогу это использовать.

EDIT (ы):

Я не думаю, что смогу действительно использовать здесь макрос, потому что MyFunction принимает переменное количество аргументов.

Кроме того, я бы хотел, чтобы аргументы НЕ оценивались! (Таким образом, выполнение таких действий, как комментирование тела MyFunction, на самом деле не дает мне того, что мне нужно, поскольку аргументы все равно будут оцениваться)

Ответы [ 11 ]

24 голосов
/ 13 февраля 2009

Попробуйте это:

#ifdef SOMETHING
#define foo(x)
#else
#define foo(x) MyFunction(x)
#endif

Если ваша функция имеет несколько аргументов, то:

#ifdef SOMETHING
#define foo(x,y,z)
#else
#define foo(x,y,z) MyFunction(x,y,z)
#endif

Если ваша функция имеет переменное количество аргументов, то ваш компилятор может поддерживать так называемые «переменные макросы», например:

#ifdef SOMETHING
#define foo(...)
#else
#define foo(...) MyFunction(__VA_ARGS__)
#endif

Причина, по которой я видел подобные вещи, используемые на практике, состоит в том, чтобы избавиться от функций журналирования из сборки выпуска. Однако см. Также Отдельные сборки 'debug' и 'release'? , в которых люди задаются вопросом, должны ли 1013 * даже иметь разных сборок.


В качестве альтернативы, вместо переопределения вызова функции как ничего, в комментарии Джонатана к этому ответу предлагалось сделать что-то вроде следующего:

#ifdef SOMETHING
#define foo(...) do { if (false) MyFunction(__VA_ARGS__) } while (0)
#else
#define foo(...) do { if (true) MyFunction(__VA_ARGS__) } while (0)
#endif

Причиной для этого является то, что вызов функции всегда компилируется (чтобы не было необоснованных ошибок, таких как ссылки на удаленные переменные), а вызывался только при необходимости: см. Kernighan & Pike Практика программирования , а также стандарты программирования Центра космических полетов Годдарда .

Из файла debug.h (созданного в 1990 году и поэтому не использующего __VA_ARGS__):

/*
** Usage:  TRACE((level, fmt, ...))
** "level" is the debugging level which must be operational for the output
** to appear. "fmt" is a printf format string. "..." is whatever extra
** arguments fmt requires (possibly nothing).
** The non-debug macro means that the code is validated but never called.
** -- See chapter 8 of 'The Practice of Programming', by Kernighan and Pike.
*/
#ifdef DEBUG
#define TRACE(x)    db_print x
#else
#define TRACE(x)    do { if (0) db_print x; } while (0)
#endif /* DEBUG */

С C99 больше не требуется трюк с двойными скобками. Новый код не должен использовать его, если проблема не связана с совместимостью с C89.

5 голосов
/ 13 февраля 2009

Может быть, более простой способ сделать это состоит в том, чтобы условно опустить тело функции?

void MyFunction() {
#ifndef SOMETHING
    <body of function>
#endif
}

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

3 голосов
/ 13 февраля 2009

К сожалению, текущая версия C ++ не поддерживает переменные макросы.

Однако вы можете сделать это:

#ifdef SOMETHING
#define foo
#else
#define foo(args) MyFunction args
#endif

// you call it with double parens:
foo((a, b, c));
2 голосов
/ 14 февраля 2009

Скорее всего, вы не хотите делать простое "удаление кода", как предложено, потому что ваши абоненты будут ожидать побочных эффектов от аргументы случаются. Вот некоторые неприятные фрагменты должен заставить вас задуматься:

// pre/post increment inside method call:
MyFunction(i++); 

// Function call (with side effects) used as method argument: 
MyFunction( StoreNewUsernameIntoDatabase(username) ); 

Если вы отключите MyFunction, просто сказав:

#define MyFunction(x) 

тогда побочные эффекты, которые ожидали звонящие, исчезли, и их код будет сломан, и его будет довольно сложно отлаживать. мне нравится предложение "sizeof" выше, и мне также нравится предложение просто отключить тело MyFunction () через # ifdef, хотя это означает что все абоненты получают одинаковую версию MyFunction (). От твоего Постановка проблемы, я полагаю, это не то, что вы хотите.

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

#ifdef SOMETHING 
#define MyFunction(x) NoOp_MyFunction(x) 

int NoOp_MyFunction(x) { } 
#endif 

Вы можете даже включить реализацию NoOp_MyFunction () внутри источник и заголовки для MyFunction (). У вас также есть гибкость добавить дополнительную информацию о регистрации или отладке в NoOp_MyFunction () как хорошо.

2 голосов
/ 13 февраля 2009

Как насчет чего-то такого:

#ifdef NDEBUG
#define DEBUG(STATEMENT) ((void)0)
#else
#define DEBUG(STATEMENT) (STATEMENT)
#endif

Вы могли бы использовать это для регистрации отладочных сообщений:

DEBUG(puts("compile with -DNDEBUG and I'm gone"));

Неуниверсальная версия для форматированного вывода с дополнительной информацией отладки с использованием переменных макросов C99 и идентификатора __func__ может выглядеть следующим образом:

#ifdef NDEBUG
#define Dprintf(FORMAT, ...) ((void)0)
#define Dputs(MSG) ((void)0)
#else
#define Dprintf(FORMAT, ...) \
    fprintf(stderr, "%s() in %s, line %i: " FORMAT "\n", \
        __func__, __FILE__, __LINE__, __VA_ARGS__)
#define Dputs(MSG) Dprintf("%s", MSG)
#endif

Вот как бы вы использовали эти макросы:

Dprintf("count = %i", count);
Dputs("checkpoint passed");
2 голосов
/ 13 февраля 2009

Если в случае, если вы не хотите, чтобы foo вызывался, вы определяете его как:

void foo() {}

любые вызовы foo () должны быть оптимизированы.

1 голос
/ 14 февраля 2009

Я немного неохотно публикую этот ответ, потому что использование макро-хакерства может стать источником проблем. Однако - , если вызовы функции, которую вы хотите удалить, всегда используются отдельно в выражении (т. Е. Они никогда не являются частью большего выражения), тогда может сработать что-то вроде следующего (и это ручки varargs):

#ifdef SOMETHING
#define foo (1) ? ((void) 0) : (void)
#else
#define foo MyFunction
#endif

Итак, если у вас есть строка кода:

foo( "this is a %s - a++ is %d\n", "test", a++);

это закончится после шага предварительной обработки как:

MyFunction( "this is a %s - a++ is %d\n", "test", a++);

или

(1) ? ((void) 0) : (void)( "this is a %s - a++ is %d\n", "test", a++);

, который превращает список параметров псевдофункции в набор выражений, разделенных оператором запятой, который никогда не будет вычисляться, поскольку условное выражение всегда возвращает результат ((void) 0).

Вариант этого близок к тому, что предложили ChriSW и Джонатан Леффлер:

#ifdef SOMETHING
#define foo if (0) MyFunction
#else
#define foo if (1) MyFunction
#endif

Это немного отличается тем, что компилятору не требуется поддерживать переменные макросы (__VA_ARGS__).

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

Обратите внимание на потенциальные проблемы - особенно если параметры в вызове вызывают побочные эффекты (это общая проблема с макросами, а не только этот хак). В этом примере a++ будет оцениваться, только если в сборке определено SOMETHING, в противном случае это не так. Таким образом, если код после вызова зависит от значения a, которое будет увеличено, в одной из сборок будет ошибка.

1 голос
/ 13 февраля 2009
#ifdef SOMETHING
#define foo sizeof
#else
#define foo MyFunction
#endif

Я предполагаю, что foo - это функция стиля printf? В любом случае, это не будет работать с функцией с нулевым параметром, но если бы это было так, вы бы уже знали, что делать. Если вы действительно хотите быть анальным, вы можете использовать (void)sizeof, но это, вероятно, не нужно.

1 голос
/ 13 февраля 2009

Нет, стандарты C и C ++ говорят, что вы не можете # определить что-то, что будет комментарием, поэтому

#define foo //

не будет работать.

0 голосов
/ 13 февраля 2009

Как насчет окружения каждого вызова myFunction с помощью

#ifdef SOMETHING
myFunction(...);
#endif

...