Что такое портативный способ реализации оператора no-op в C ++? - PullRequest
25 голосов
/ 02 ноября 2011

Время от времени в C ++ требуется оператор no-op.Например, при реализации assert(), который отключен в конфигурации без отладки (см. Также этот вопрос ):

#ifdef _DEBUG
#define assert(x) if( !x ) { \
                     ThrowExcepion(__FILE__, __LINE__);\
                  } else {\
                     //noop here \
                  }
#else
#define assert(x) //noop here
#endif

Пока что у меня сложилось впечатление, что правильный путь заключается виспользуйте (void)0; для no-op:

(void)0;

, однако я подозреваю, что это может вызвать предупреждения на некоторых компиляторах - что-то вроде C4555: expression has no effect; expected expression with side-effect Предупреждение Visual C ++, которое не отправляется для данного конкретного случая, но отправляетсякогда нет приведения к void.

Это универсально переносимо?Есть ли лучший способ?

Ответы [ 10 ]

17 голосов
/ 02 ноября 2011

Самым простым неработающим является отсутствие кода вообще:

#define noop

Тогда код пользователя будет иметь:

if (condition) noop; else do_something();

Альтернатива, которую вы упомянули, также неop: (void)0;, но если вы собираетесь использовать это внутри макроса, вы должны оставить ; в стороне, чтобы вызывающая сторона добавила:

#define noop (void)0
if (condition) noop; else do_something();

(Если ; был частьюмакрос, тогда там будет дополнительный ;)

15 голосов
/ 02 ноября 2011

Я подозреваю, что это может вызывать предупреждения на некоторых компиляторах

Маловероятно, поскольку ((void)0) - это то, к чему расширяется стандартный макрос assert, когда определяется NDEBUG.Таким образом, любой компилятор, который выдает предупреждения для него, будет выдавать предупреждения всякий раз, когда код, содержащий утверждения, компилируется для выпуска.Я ожидаю, что это будет сочтено ошибкой пользователей.

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

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

Обратите внимание, что в C ++ стандартные заголовки разрешают включать друг друга.Поэтому, если вы используете любой стандартный заголовок, assert может быть определено этим.Таким образом, ваш код является непереносимым для этой учетной записи.Если вы говорите «универсально переносимый», вы обычно должны рассматривать любой макрос, определенный в любом стандартном заголовке, как зарезервированный идентификатор.Вы могли бы отменить его, но использование другого имени для ваших собственных утверждений было бы более разумным.Я знаю, что это только пример, но я не понимаю, почему вы когда-нибудь захотите определить assert «универсально переносимым» способом, поскольку во всех реализациях C ++ он уже есть, и он не делает то, что выопределяя это здесь.

8 голосов
/ 02 ноября 2011

Как насчет do { } while(0)? Да, он добавляет код, но я уверен, что большинство современных компиляторов способны его оптимизировать.

5 голосов
/ 02 ноября 2011

;считается стандартным без оп.Обратите внимание, что возможно, что компилятор не сгенерирует из него никакого кода.

1 голос
/ 04 сентября 2018

Я думаю, что цель здесь, и причина не определять макрос ни к чему, состоит в том, чтобы потребовал пользователя, чтобы добавить ;. Для этой цели в любом случае утверждение является законным, (void)0 (или ((void)0), или другие варианты в этом отношении) - хорошо.

Я нашел этот вопрос, потому что мне нужно было сделать то же самое в global scope, где обычное старое утверждение недопустимо. К счастью, C ++ 11 дает нам альтернативу: static_assert(true, "NO OP"). Это может использоваться в любом месте , и выполняет мою задачу, требуя ; после макроса. (В моем случае макрос - это тег для инструмента генерации кода, который анализирует исходный файл, поэтому при компиляции кода как C ++ он будет всегда НЕТ-ОП.)

1 голос
/ 13 апреля 2018

А как же:

#define NOP() ({(void)0;})

или просто

#define NOP() ({;})
0 голосов
/ 13 ноября 2015

этот код не будет опущен при оптимизации

static void nop_func()  {   }
typedef void (*nop_func_t)();
static nop_func_t nop = &nop_func;

for (...)
{
    nop();
}
0 голосов
/ 22 марта 2015
    inline void noop( ) {}

Самодокументируемый

0 голосов
/ 02 ноября 2014

Я рекомендую использовать:

static_cast<void> (0)   
0 голосов
/ 02 ноября 2011

AFAIK, это универсально портативный.

#define MYDEFINE()

тоже подойдет.

Другой вариант может быть примерно таким:

void noop(...) {}
#define MYDEFINE() noop()

Однако я бы придерживался (void)0 или использовал бы такие свойства, как __noop

...