do {…} while (false)

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

 function() {  do {  } while(false);  } 

Это неплохо , более своеобразно (фактический код довольно аккуратный и неудивительный). Это не то, что я видел раньше, и я задавался вопросом, может ли кто-нибудь думать о какой-либо логике за этим – возможно, на другом языке?

Вы можете выйти из do{...}while(false) .

Многие люди отмечают, что он часто используется с перерывом как неудобный способ написания «goto». Это, вероятно, так, если оно написано непосредственно в функции.

В макросе, OTOH, do {something; } while (false) – это удобный способ FORCE с запятой после вызова макроса, абсолютно никакой другой токен не может следовать.

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

Перерыв как goto, вероятно, является ответом, но я выдвину еще одну идею.

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

Помните, что в последнее время C ++ допускает {...} любом месте, это не всегда так.

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

Это может сделать утомительный if / else – если дерево намного легче читать, просто нужно сломать всякий раз, когда точка выхода будет достигнута, а остальная часть логической строки впоследствии.

Этот шаблон также полезен на языках, на которых нет инструкции goto. Возможно, именно там оригинальный программист узнал образец.

Я видел такой код, так что вы можете использовать break как сортировку.

Это просто изrotation while чтобы получить семантику goto tidy-up без использования слова goto.

Это плохая форма, потому что, когда вы используете другие петли внутри внешнего, в while как breaks становятся двусмысленными для читателя. «Предполагается, что он должен выйти?» Или это предназначено только для выхода из внутреннего цикла? »

Я думаю, что удобнее писать break вместо goto end . Вам даже не нужно придумывать имя для ярлыка, которое делает намерение более ясным: вы не хотите переходить на метку с определенным именем. Вы хотите уйти отсюда.

Вероятно, вам также нужны брекеты. Итак, это do{...}while(false); версия:

 do { // code if (condition) break; // or continue // more code } while(false); 

И так вы должны были бы выразить это, если хотите использовать goto :

 { // code if (condition) goto end; // more code } end: 

Я думаю, что смысл первой версии гораздо легче понять. Также проще писать, проще расширять, проще переводить на язык, который не поддерживает goto и т. Д.


Наиболее часто упоминаемая проблема использования break заключается в том, что это плохо замаскированный goto . Но на самом деле break имеет больше сходства с return : обе команды выпрыгивают из блока кода, который в значительной степени структурирован по сравнению с goto . Тем не менее обе команды позволяют нескольким точкам выхода в блоке кода, который иногда может сбивать с толку. В конце концов, я попытался бы найти наиболее четкое решение, независимо от того, что находится в конкретной ситуации.

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

Это очень распространенная практика. В C. Я пытаюсь думать об этом так, как будто вы хотите лгать себе: «Я не использую goto ». Думая об этом, не было бы ничего плохого в том, что goto использовал аналогично. Фактически это также уменьшит уровень отступов.

Тем не менее, я заметил, что очень часто это происходит … в то время как циклы, как правило, растут. И тогда они получат, if s и else s внутри, что делает код на самом деле не очень читаемым, не говоря уже о тестируемых.

Обычно они делают очистку . По возможности, я предпочел бы использовать RAII и вернуться раньше от короткой функции. С другой стороны, C не предоставляет вам столько удобств, как C ++ , что делает … тем do..while один из лучших подходов к очистке.

Он похож на программиста С. В C ++ у автоматических переменных есть деструкторы, которые вы используете для очистки, поэтому перед возвратом не должно быть ничего необходимого. В C у вас не было этой идиомы RAII , поэтому, если у вас есть общий очищающий код, вы либо goto , либо используете один цикл, как описано выше.

Его главный недостаток по сравнению с идиомой C ++ заключается в том, что он не будет убирать, если исключение будет выброшено в тело. У C не было исключений, поэтому это не было проблемой, но это делает его плохой привычкой на C ++.

Возможно, он используется для того, чтобы break мог использоваться внутри, чтобы прервать выполнение дополнительного кода в любой момент:

 do { if (!condition1) break; some_code; if (!condition2) break; some_further_code; // … } while(false); 

Я думаю, что это делается для использования break или continue . Какая-то логика кода «goto».

Это просто: Очевидно, вы можете выпрыгнуть из фальшивого цикла в любое время, используя оператор break . Кроме того, блок do является отдельной областью (что также может быть достигнуто только с помощью { ... } ).

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

  function() {  
error: }

(В стороне: конструктор do-while-false используется в Lua, чтобы найти отсутствующий оператор continue .)

Многие ответчики объяснили причину do{(...)break;}while(false) . Я хотел бы дополнить эту картину еще одним примером реальной жизни.

В следующем коде мне пришлось установить operation enums на основе адреса, на который указывает указатель data . Поскольку ящик коммутатора можно использовать только для скалярных типов, я сначала сделал это неэффективно таким образом

 if (data == &array[o1]) operation = O1; else if (data == &array[o2]) operation = O2; else if (data == &array[on]) operation = ON; Log("operation:",operation); 

Но поскольку Log () и остальная часть кода повторяются для любого выбранного значения операции, я блуждал, как пропустить остальные сравнения, когда адрес уже обнаружен. И вот где do{(...)break;}while(false) пригодится.

 do { if (data == &array[o1]) { operation = O1; break; } if (data == &array[o2]) { operation = O2; break; } if (data == &array[on]) { operation = ON; break; } } while (false); Log("operation:",operation); 

Можно задаться вопросом, почему он не мог сделать то же самое с break в выражении if , например:

 if (data == &array[o1]) { operation = O1; break; } else if (...) 

break взаимодействует исключительно с ближайшим замкнутым контуром или переключателем, будь то for , while или do .. while type, поэтому, к сожалению, это не сработает.

Несколько объяснений. Первый – общий, второй – для макросов препроцессора C с параметрами:

Управление streamом

Я видел, что это используется в простом коде C. В принципе, это более безопасная версия goto, поскольку вы можете вырваться из нее, и вся память очищается должным образом.

Почему что-то goto like должно быть хорошим? Ну, если у вас есть код, в котором почти каждая строка может возвращать ошибку, но вам нужно реагировать на все одинаково (например, путем передачи ошибки вашему вызывающему абоненту после очистки), обычно более читаемо избегать if( error ) { /* cleanup and error string generation and return here */ } поскольку это позволяет избежать дублирования кода очистки.

Однако в C ++ у вас есть исключения + RAII именно для этой цели, поэтому я бы счел это плохой стиль кодирования.

Проверка точки с запятой

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

 #define PRINT_IF_DEBUGMODE_ON(msg) if( gDebugModeOn ) printf("foo"); 

Это случайно называется

 if( foo ) PRINT_IF_DEBUGMODE_ON("Hullo\n") else doSomethingElse(); 

« gDebugModeOn » будет считаться связанным с gDebugModeOn , поэтому, когда foo является false , произойдет точное обратное тому, что было предназначено.

Предоставление возможности для временных переменных.

Поскольку do / while имеет фигурные скобки, временные переменные имеют четко определенный масштаб, от которых они не могут убежать.

Избегание предупреждений “возможно нежелательных точек с запятой”

Некоторые macros активируются только в отладочных assemblyх. Вы определяете их как:

 #if DEBUG #define DBG_PRINT_NUM(n) printf("%d\n",n); #else #define DBG_PRINT_NUM(n) #endif 

Теперь, если вы используете это в сборке релизов внутри условного выражения, он компилируется в

 if( foo ) ; 

Многие компиляторы рассматривают это как то же, что и

 if( foo ); 

Который часто пишется случайно. Поэтому вы получаете предупреждение. Do {} while (false) скрывает это от компилятора и принимается им как указание на то, что вы действительно ничего не хотите здесь делать.

Избегание захвата строк условностями

Макрос из предыдущего примера:

 if( foo ) DBG_PRINT_NUM(42) doSomething(); 

Теперь, в сборке отладки, поскольку мы также обычно включаем точку с запятой, это компилируется просто отлично. Однако в выпуске сборки это внезапно превращается в:

 if( foo ) doSomething(); 

Или более четко отформатирован

 if( foo ) doSomething(); 

Это совсем не то, что было предназначено. Добавление do {…} while (false) вокруг макроса превращает недостающую точку с запятой в ошибку компиляции.

Что это значит для ОП?

В общем, вы хотите использовать исключения в C ++ для обработки ошибок и шаблоны вместо макросов. Однако в очень редком случае, когда вам все еще нужны macros (например, при создании имен classов с использованием вставки меток) или ограничены равным C, это полезный шаблон.

Сколько лет был автор?

Я спрашиваю, потому что однажды я наткнулся на некоторый код Fortran в реальном времени, который сделал это, еще в конце 80-х. Оказывается, это действительно хороший способ имитировать streamи на ОС, которые их не имеют. Вы просто помещаете всю программу (ваш планировщик) в цикл и называете свои «streamовые» подпрограммы «один за другим». Процедуры streamов сами по себе являются циклами, которые повторяются до тех пор, пока не произойдет один из нескольких условий (часто один из них является определенным количеством прошло время). Это «совместная многозадачность», так как отдельные streamи позволяют отказываться от процессора каждый раз, а затем, чтобы другие не истощались. Вы можете вставлять вызовы подпрограмм цикла для имитации приоритета streamа группы.

В дополнение к уже упомянутым «примерам goto», do … while (0) idiom иногда используется в определении макроса, чтобы обеспечить скобки в определении и все еще иметь работу компилятора с добавлением полуплоскости в конец вызов макроса.

http://groups.google.com/group/comp.soft-sys.ace/browse_thread/thread/52f670f1292f30a4?tvc=2&q=while+(0)

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

Я также видел эту конструкцию, используемую в смешанных средах C / C ++ как исключение бедного человека. «Do {} while (false)» с «break» можно использовать для перехода в конец блока кода, если в цикле будет встречено что-то, что обычно требует исключения.

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

Я работаю с Adobe InDesign SDK, а примеры InDesign SDK имеют почти каждую функцию, написанную так. Это связано с тем, что функция обычно очень длинная. Где вам нужно выполнить QueryInterface (…), чтобы получить что-либо из объектной модели приложения. Таким образом, обычно каждый QueryInterface следует, if не пойдет хорошо, перерыв.

Многие уже заявили о сходстве между этой конструкцией и goto и выразили предпочтение goto. Возможно, фон этого человека включал среду, в которой goto’s строго запрещалось правилами кодирования?

Другая причина, по которой я могу думать, заключается в том, что она украшает фигурные скобки, тогда как я верю в более новые стандартные фигурные скобки C ++, которые не подходят (ISO C им не нравится). В противном случае, чтобы успокоить статический анализатор, например, линт.

Не знаете, почему вы хотите их, возможно, переменную область или преимущество с отладчиком.

См. Trivial Do While loop и Braces Good from C2.

Чтобы уточнить мою терминологию (которая, как я полагаю, следует за стандартным использованием):

Голые брекеты :

 init(); ... { c = NULL; mkwidget(&c); finishwidget(&c); } shutdown(); 

Пустые фигурные скобки (NOP):

 {} 

например

 while (1) {} /* Do nothing, endless loop */ 

Блок :

 if (finished) { closewindows(&windows); freememory(&cache); } 

который стал бы

 if (finished) closewindows(&windows); freememory(&cache); 

если скобки удалены, что изменяет stream выполнения, а не только область локальных переменных. Таким образом, не «автономный» или «голый».

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

Это изобретательный способ эмулировать GOTO поскольку эти два практически идентичны:

 // NOTE: This is discouraged! do { if (someCondition) break; // some code be here } while (false); // more code be here 

а также:

 // NOTE: This is discouraged, too! if (someCondition) goto marker; // some code be here marker: // more code be here 

С другой стороны, оба из них действительно должны быть выполнены, if s:

 if (!someCondition) { // some code be here } // more code be here 

Хотя вложенность может быть немного уродливой, если вы просто превратите длинную строку forward- GOTO в вложенную, if s. Реальный ответ – правильный рефакторинг, хотя и не имитирующий архаичные языковые конструкции.

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

Я не знаю ни одного C-подобного языка, где do / while является идиоматическим решением для чего угодно, на самом деле.

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

Некоторые кодеры предпочитают иметь только один выход / возврат из своих функций. Использование манекена do {….} while (false); позволяет вам «вырваться» из фиктивной петли, как только вы закончите, и все еще имеете один возврат.

Я java-кодер, поэтому мой пример будет чем-то вроде

 import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; public class p45 { static List cakeNames = Arrays.asList("schwarzwald torte", "princess", "icecream"); static Set forbidden = Stream.of(0, 2).collect(Collectors.toSet()); public static void main(String[] argv) { for (int i = 0; i < 4; i++) { System.out.println(String.format("cake(%d)=\"%s\"", i, describeCake(i))); } } static String describeCake(int typeOfCake) { String result = "unknown"; do { // ensure type of cake is valid if (typeOfCake < 0 || typeOfCake >= cakeNames.size()) break; if (forbidden.contains(typeOfCake)) { result = "not for you!!"; break; } result = cakeNames.get(typeOfCake); } while (false); return result; } } 

Это забавно. Вероятно, в петле есть перерывы, как говорили другие. Я бы сделал это так:

 while(true) { 
break; // at the end. }
в while(true) {
break; // at the end. }
  • Microsoft.Office.Interop.Excel действительно медленный
  • Как я называю имя classа в C #?
  • Как определить работу интернет-соединения в C #?
  • Нужен многостраничный процесс ASP.NET MVC с обратной связью с пользователем
  • объект объекта не может ссылаться на несколько экземпляров IEntityChangeTracker. при добавлении связанных объектов к объекту в Entity Framework 4.1
  • Не удалось выполнить сериализацию ответа в Web API с помощью Json
  • Что такое функторы C ++ и их использование?
  • Бинарные литералы C #
  • C # => оператор?
  • printf добавляет дополнительный `FFFFFF` к шестнадцатеричной печати из массива char
  • Clipboard.GetText возвращает null (пустая строка)
  • Interesting Posts

    Как отформатировать дату в angularjs

    Как домашние сети будут работать в мире IPv6?

    Запуск операционной системы из памяти

    Bash: чтение ввода внутри, в то время как цикл чтения не работает

    React «не может читать свойство неопределенного» при использовании карты

    Как обновить PostgreSQL с версии 9.6 до версии 10.1 без потери данных?

    Являются ли IP-адреса с и без ведущих нулей одинаковыми?

    Как разобрать XML с помощью jsoup

    Сервер OpenVPN не работает так, как должен: не может получать большинство пакетов от устройства LAN до туннелированного клиента

    Что я могу сделать, чтобы разрешить исключение «Row not found or changed» в LINQ to SQL в базе данных SQL Server Compact Edition?

    Использование HttpClient и HttpPost в Android с параметрами post

    Не удалось найти допустимый путь сертификации для запрошенной целевой ошибки даже после импорта сертификата

    Как найти утечки памяти из собственного кода в android

    Аутентификация имени пользователя вместо электронной почты

    Http получить из оболочки в Mac OS X

    Давайте будем гением компьютера.