Я хотел бы понять этот макрос утверждения

В Visual Studio макрос assert определяется следующим образом:

#define assert(expression) (void)(                                                       \
            (!!(expression)) ||                                                              \
            (_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \
        )

Я понимаю, что правильная часть после || оценивается только в том случае, если левое выражение ложно, и выдает исключение.

У меня есть 3 вопроса:

  1. !! двойное отрицание — это преобразование типа «выражение» в логическое значение, верно? Что было бы, если бы они остались в стороне? Выражение 'будет неявно преобразовано в логическое значение, когда оно будет логически объединено с другим выражением, верно? В:

    поплавок() || /* Вызов функции, которая выбрасывает*/;

В любом случае, float() преобразуется в тип bool?

2.После || оператор у нас есть:

 ( /* call function that throws */  , 0 )

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

!!(expression)) || 0

Какой смысл в этом 0 после запятой? Если бы их не было, то выражение было бы таким:

!!(expression)) || / * call function that throws */

Что то же самое, верно?

  1. И, наконец, (void), мне интересно, для чего это нужно.

Что, если у меня есть тип, который (по глупости? Давайте возьмем на себя сомнения и предположим, что на то есть веские причины) переопределен operator|| ? Хм, наверное, ((bool)(expression) || ... было бы безопаснее. Они также могли переопределить operator!.

Eljay 25.12.2020 13:45

Оператор запятая необходим, потому что _wassert возвращает void. А _wassert не throw, он либо прерывается с кодом выхода 3, врывается в отладчик, либо игнорирует проблему и продолжает работу. Его поведение можно изменить с помощью _set_error_mode. Макрос также избегает проблемы висячего else, используя макрос с оператором if, и он немного умен, потому что он сохраняет его как выражение, а не оператор.

Eljay 25.12.2020 14:05

Самый верхний уровень (void) предназначен для подавления предупреждения компилятора о неиспользованном результате выражения, а также для защиты от случайного использования результата выражения. И макрос, вероятно, тот же, что используется при компиляции C, C++ или C++/CLI, поэтому он должен соответствовать пересечению этих языков.

Eljay 25.12.2020 14:14

@Элджей, круто, теперь сделай ответ :)

Dialecticus 25.12.2020 16:33
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
376
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Разберем код:

#define assert(expression) (void)(                                                       \
            (!!(expression)) ||                                                              \
            (_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \
        )

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

!! часть !!(expression) должна гарантировать, что выражение оценивается как false или true.

Но озорной код C++ мог переопределить operator! и вызвать здесь небольшой хаос.

Почему актерский состав в стиле C, а не static_cast<bool>? Я подозреваю, что один и тот же заголовочный файл используется для поддержки C и C++, а также, возможно, C++/CLI. (Если это так, может быть оправдано использование static_cast<bool> для C++ и (bool) для C в макросах для двух языков.)

Если объект expression имеет преобразование operator bool, он будет использоваться !!. (Но также был бы использован (bool)(expression).)

Несмотря на это, наблюдение о том, что !! не нужно, кажется мне правильным. Выражения в C++, которые являются истинными (ненулевыми) или ложными (нулевыми), являются неотъемлемой частью языка.

Оператор запятой (x(), 0) используется, потому что x (в данном случае _wassert) возвращает пустоту. Невозможно сделать a || b, если b недействительно, поэтому хитрость просто делает так, что пустота вызывается, а выражение приводит к int. Неважно, что это 0.

_wassert не бросает. Он может прерваться с кодом выхода 3. Он может взломать отладчик.

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

Поскольку макрос, подобный функции assert, был написан как выражение, он аккуратно избегает проблемы «висячего макроса if» (если бы использовалась конструкция if (!(expression)) ...) и является выражением, а не оператором.

Другие вопросы по теме