2009-02-13 2 views
13

Можно ли сделать что-то вроде этогоИспользуйте #ifdefs и #define, чтобы дополнительно включить вызов функции в комментарий

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

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

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

EDIT (ы):

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

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

+0

Узнайте, поддерживает ли ваш компилятор «переменные макросы». – ChrisW

+0

_Why_ вы хотите это сделать? Возможно, есть и другой способ получить то, что вам нужно. Потому что без контекста то, что вы хотите сделать, кажется довольно опасным ... – gimpf

+0

Для компиляторов, которые не поддерживают переменные макросы, приятно удалять строки отладки и вызовы функций. Это экономит много исполняемого пространства. – MSN

ответ

24

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

#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 

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

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

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


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

#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 The Practice of Programming, а также Goddard Space Flight Center programming standards. не

Из 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.

+0

Будет ли это работать с переменным числом аргументов? например foo (a, b, c) –

+0

Он будет работать с большим числом аргументов, отличным от одного, но фиксированным. Для переменного количества аргументов Google и/или проверьте свою компиляторную документацию на так называемые «переменные макросы». – ChrisW

+0

@ChrisW Если вы измените свой ответ, чтобы использовать переменный макрос, то я _think_ это именно то, что мне нужно :) –

0

Если я правильно помню, вы должны быть в состоянии #define макрос на «ничего» и что заставит компилятор игнорировать этот призыв

#define foo() 

foo(); // this will be ignored 
5

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

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

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

+1

Но аргументы будут по-прежнему оценены ... –

+0

Правда. Я думаю, что если вы не хотите, чтобы аргументы были оценены, вам, возможно, придется дойти до #ifdefing всех вызовов. Решение #define foo (x) также будет оценивать ваши аргументы для большинства компиляторов. – MattK

+0

Не только аргументы оцениваются, но и данные для них включены, что не очень приятно, если функция является функцией регистрации, а аргументы - это огромные строки, раскрывающие работу вашего приложения. – sharptooth

2

Если в случае, если вы не хотите, Foo называется, можно определить как:

void foo() {} 

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

+0

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

+0

Это зависит от компилятора. Я видел, как VC++ оптимизирует функции с кодом в них, когда он может определить, что у них не было побочных эффектов. Очень неприятно, когда вы пытаетесь делать тесты ;-) – Ferruccio

2

А что-то вдоль этих линий:

#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"); 
3

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

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

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

// you call it with double parens: 
foo((a, b, c)); 
+0

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

1

Нет, C и C++ Стандарты говорят, что вы не можете #define что-то, чтобы быть комментарий, так

#define foo // 

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

1
#ifdef SOMETHING 
#define foo sizeof 
#else 
#define foo MyFunction 
#endif 

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

0

насчет окружающих каждый вызов MYFUNCTION с

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

?

+1

Это становится невыносимо неуклюжим после второго призыва. Не спускайтесь по этому маршруту. –

2

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

// 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() как .

+0

'sizeof' имеет ту же проблему, что и побочные эффекты. –

1

Я немного неохотно публикую этот ответ, потому что использование макроса хакера может стать источником проблем. Однако - , если вызовы функции, которые вы хотите удалить, всегда используются отдельно в инструкции (т. Е. Они никогда не являются частью более крупного выражения), тогда может работать что-то вроде следующего (и обрабатывает 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 

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

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

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

Смежные вопросы