Макрос в стиле функции с no-op, если условие ложно - PullRequest
1 голос
/ 17 марта 2019

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

#if HAVE_LIBFOOBAR
    foobar_func(data);
#endif

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

    foobar_func_if_available(data)

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

Таким образом, что-то вроде:

#if HAVE_LIBFOOBAR
#define foobar_func_if_available(x) foobar_func(x)
#else
#define foobar_func_if_available(x) {}
#endif

Вопросы:

  • Работает ли {} как неоператив?Безопасно ли иметь непреднамеренные эффекты (такие как использование в выражении if без скобок)?Если нет, что бы я использовал?
  • Нужно ли мне иметь два независимых #define, обернутых в условные выражения, или есть способ сделать это наоборот (один #define с условными обозначениями внутримакрос в стиле функции)?

Редактировать: предполагалось, что это дубликат другого вопроса , но, на мой взгляд, это не так: другой вопрос задает «Какая проблема решается с помощью этой конструкции? », моя -« Какая конструкция решит мою проблему ».Действительно, другой вопрос имеет возможное решение моей проблемы, он не охватывает все аспекты моего вопроса.

Ответы [ 3 ]

1 голос
/ 17 марта 2019

Создайте фиктивную функцию и укажите точку #define (условно):


#if HAVE_LIBFOOBAR
  #define foobar_func_if_available(x) foobar_func(x)
#else
  int dummy(int ignored)
  {
  return 0;
  }
  #define foobar_func_if_available(x) dummy(x)
#endif

Или просто:

#define foobar_func_if_available(x) 0
1 голос
/ 17 марта 2019

Макросы, такие как ((int)0) или ((void)0), вероятно, являются наиболее гибкими и безопасными неоперационными макросами. Они гибки, потому что вы можете использовать их в выражениях (в отличие от do{}while(0)), и они не ломаются if-else, как {} или ;.

Пример того, как макросы {} (или ;) ломаются if - else:

#define foo() {}
if(1) foo(); else bar(); //syntax error because if(1) {}; else bar(); was pasted

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

Вам не нужно иметь два макроса, как в:

#if HAVE_LIBFOOBAR
    #define foobar_func_if_available(x) foobar_func(x)
#else
    #define foobar_func_if_available(x) ((void)0) /*if foobar_func returns void*/
#endif

Вы можете поместить условие в макрос:

#define foobar_func_if_available(x) \
    (HAVE_LIBFOOBAR?foobar_func(x):((void)0))

Даже очень тупой компилятор должен уметь оптимизировать константу условного вывода.

Но если вы полагаетесь на пустое значение HAVE_LIBFOOBAR, равное 0 внутри #if, то вышеприведенное не сработает - HAVE_LIBFOOBAR должен быть целым числом.

( Вы могли бы сделать

#if !HAVE_LIBFOOBAR
    #undef HAVE_LIBFOOBAR
    #define HAVE_LIBFOOBAR 0
#endif
#define foobar_func_if_available(x) \
        (HAVE_LIBFOOBAR?foobar_func(x):((void)0))

для нормализации пустого HAVE_LIBFOOBAR в 0, но если вы не будете повторно использовать определенную теперь гарантированную HAVE_LIBFOOBAR, это кажется ненужным осложнением по сравнению с исходными двумя макросами foobar_func_if_available. )

1 голос
/ 17 марта 2019

Вы не можете достичь этого с помощью одного #define.

Вам не нужно {} как запрет, вы можете определить пустое выражение несколькими способами:

#define foobar_func_if_available(x)
#define foobar_func_if_available(x) ;
#define foobar_func_if_available(x) do{}while(0)

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

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

void foobar_func( int n )
{
    #if defined HAVE_LIBFOOBAR
       // do something
    #else
       // do nothing
    #endif
}

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

...