Share via


Один случай, когда можно использовать оператор GOTO...

??? ?? ???? ????????? ????????????? ??????? ? ????? ?????????? ?????????? ? ????????? goto. ???????? ???, ? ????, ? ????????? ???????? ??? ?????????????. ?? ????????? ????????? ? ????? ???????, ????? ????????????? ????????? goto ?? ???????? ????? ?? ????????????.

??? ?????? ??????????? ????, ??? ??? ????? ????????? ???? ???????, ? ?????? ????????? ???????-?????, ???? ??? ??? ??????? ?????????. ??? ??? ????? ????????? ??????????? ?????!

void Try1(void)
{
     if (!Cond(1)) {
printf("Error with condition 1!");
} else if(!Cond(2)) {
printf("Error with condition 2!");
} else if(!Cond(3)) {
printf("Error with condition 3!");
} else if(!Cond(4)) {
printf("Error with condition 4!");
} else if(!Cond(5)) {
printf("Error with condition 5!");
} else {
// My real code
printf("Hurra!");
}
}

??????????? ???????????????? ?????! Goto ???? ???! ?? ??????, ????-???? ???????? ????????. ?????? ??? ????? ????????? ???? ????? ???? ???????, ?????? ?????? ??????? ???????? ?????? ??????? _?_ ?????? ????????? ?????? ????? ???? ?????? ?????? ???? ??? ?????????? ??????? ?????????. ? ????? ????? ?????. ??, ??, ?? ??? ? ??????? ????? ????... ????? ???????? ????????? ?????? ? ?????, ??? ???, ?????????...

??? ??? ????????? ????? ????????? ? ???????????? ??????????? ????????????????? ??, ???-?? ?????:

void

Try2(void)
{
Condition * c1 = new Condition();
if (!c1->Cond(1)) {
printf("Error with condition 1!");
} else {
Condition * c2 = new Condition();
if (!c2->Cond(2)) {
printf("Error with condition 2!");
} else {
Condition * c3 = new Condition();
if (!c3->Cond(3)) {
printf("Error with condition 3!");
} else {
Condition * c4 = new Condition();
if (!c4->Cond(4)) {
printf("Error with condition 4!");
} else {
Condition * c5 = new Condition();
if (!c5->Cond(5)) {
printf("Error with condition 5!");
} else {
printf("Hurra!");
}
delete c5;
}
delete c4;
}
delete c3;
}
delete c2;
}
delete c1;
return;
}

??????? ?????? ???????, ??????? ? ?????? ??????????? ???? 10-20 ????? ??????? ???? ?? ??????. ? ???? ? ???????? ???????????????? ??? ????????? ?????? ? ?????, ???????? ??? ????????????? COM ???????? ??? try-catch ? C# ??? ?????... ??? ??? ????????? ??? ??? ???????? ???, ????? ??? ???????? ??? ?? ??? ?????? ?? ???????? ??????, ????? ??????? ????? ?????? ????????? ???????????

??? ?? ???????, ??? ??? ?? ?????? ????? ?????? ?????? ???????? goto. ?? ??????? ???? ? ??????, ??? ?? ????. ?????????, ??? ??? ???????? ? ???:

void Try3(void)
{
Condition1 * c1 = new Condition1();
if (!c1->Cond()) {
printf("Error with condition 1!");
goto Exit;
}

Condition2 * c2 =

new Condition2();
     if (!c2->Cond()) {
printf("Error with condition 2!");
          goto Exit;
}

Condition3 * c3 =

new Condition3();
     if (!c3->Cond()) {
printf("Error with condition 3!");
          goto Exit;
}

Condition1 * c4 =

new Condition4();
     if (!c4->Cond()) {
printf("Error with condition 1!");
          goto Exit;
}

Condition1 * c5 =

new Condition5();
     if (!c5->Cond()) {
printf("Error with condition 5!");
          goto Exit;
}

// My real code
printf(

"Hurra!");

Exit:
if (NULL != c1 ) delete c1;
if (NULL != c2 ) delete c2;
if (NULL != c3 ) delete c3;
if (NULL != c4 ) delete c4;
if (NULL != c5 ) delete c5;
     return;
}

? ??????, ????? ????????????? ??? ???????, ??????? ????? ????? ? ?????:

#define Check(cond) if (!(cond)) { printf("Error with condition "#cond); goto Exit; }

void

Try4(void)
{
Condition1 * c1 = new Condition1();
Check(c1->Cond());

Condition2 * c2 =

new Condition2();
Check(c2->Cond());

Condition3 * c3 =

new Condition3();
Check(c3->Cond());

Condition4 * c4 =

new Condition4();
Check(c4->Cond());

Condition5 * c5 =

new Condition5();
Check(c5->Cond());

     // My real code
     printf("Hurra!");

Exit:
     if (NULL != c1 ) delete c1;
if (NULL != c2 ) delete c2;
if (NULL != c3 ) delete c3;
if (NULL != c4 ) delete c4;
if (NULL != c5 ) delete c5;
return;
}

???????, "?? ????? ? ???? ???????? ???", ?? ??? ???????, ??? ? ???? ?????? ???????? goto ???-???? ????? ????????????. ? ??? ?? ?? ???? ????????

Comments

  • Anonymous
    January 01, 2003
    Powerman: Увы, не поддерживает! Вроде бы собираются что-то с этим сделать, а пока используется отнюдь не Майкрософтовский движок Commmunity Server со всеми его ограничениями...

  • Anonymous
    January 01, 2003
    Ну, ATL я как раз не люблю. Макросы и правда надо с осторожностью использовать, а там переборщили. Кстати, XP - тоже. Есть в нем что-то такое... экстремальное. Дальше тоже спорить не буду, только поясню что вы пропустили. Когда я говорил о "зависимости" имелся в виду даже не контекст, а прямая зависимость вроде Protocol protocol = new Protocol (); if (!protocol) ...error... Destination destination = channel->CreateDetination(...); if(!destination) ...error... Connection connection = destination->CreateConnection(...); if(!connection) ...error... ... Если все эти обьекты из внешнего framework или библиотеки в функциональном стиле тут выпутаться достаточно сложно.

  • Anonymous
    January 01, 2003
    Как говорится, ой! А о читателях этого кода вы подумали?

  • Anonymous
    January 01, 2003
    А мы об этом думаем следующее. Конечно, goto использовать можно, и иногда (хотя и крайне редко) это упрощает код. Но в данном конкретном примере нужно совсем другое: во-первых для сигнализации об ошибках нужно использовать исключения, а во-вторых нужно использовать язык программирования со сборщиком мусора, чтобы не нужно было ручками делать delete. #!/usr/bin/perl package Condition; sub new { bless {}, shift } sub Cond { return 1 } package main; eval {     my $c1 = new Condition;     $c1->Cond(1) or die "Error with condition 1";     my $c2 = new Condition;     $c2->Cond(2) or die "Error with condition 2";     my $c3 = new Condition;     $c3->Cond(3) or die "Error with condition 3";     my $c4 = new Condition;     $c4->Cond(4) or die "Error with condition 4";     my $c5 = new Condition;     $c5->Cond(5) or die "Error with condition 5";     print "Hurra!n"; }; warn $@ if $@;

  • Anonymous
    January 01, 2003
    По условиям, нельзя вызывать конструктор Condition2, если обломилось создание или проверка Condition1. Пример: COM, когда конструкторы и условия - это на самом деле последовательные QueryInterface. Если обвалился первый, нечего "кьюерить" на втором.

  • Anonymous
    January 01, 2003
    Ответил на два уже опубликованных ответа, а сам залогиниться забыл... а когда залогинился, увидел, что еще двадцать в утверждения ждут. Вроде никого не пропустил... Еще раз, смарт указатели лесенки не уберут, если только не ставить return по невыполненным условиям. А многочисленные return тоже не всегда хорошая идея, есть за и против. С exceptions примерно то же самое. А die вообще применимо только в небольших скриптах. Вам понравилось бы, если бы Windows использовало бы эквивалент die при малейших проблемах? Кстати, таки использовала одно время. Это называлось blue screen of death. Пользователям почему-то не нравилось. Типичная проблема многих предложений в том, что народ не понял, что Condition2 можно инстанциировать только если Condition1 проверено и выполнено. Дело даже не в исключениях, иногда обьект Condition2 получается как результат вызова метода Condition1, например, Condition1 * c1 = new Condition1(); if (!(c1->Cond())) blah-blah-blah... goto... ; Condition2 * c2 = c1->GetCondition2(); if ... И тут ни using, ни исключения, ничто не поможет. Или пиши лесенку и читай на о-очень широком мониторе, или какой-нибудь эквивалент goto. К слову, финализация - это необязательно delete. Если еще Release в COM можно тоже запихать в смарт-указатель, то некоторые задачи финализации, особенно в real time, пакуются туда уже не так просто. Пример: когда порядок финализации важен. Кстати, интересно, что и то, что я удалял в неправильном порядке, и то, что обьявлял переменные уже после первых goto, все заметили. :-) Приятно!

  • Anonymous
    January 01, 2003
    а использовать auto_ptr::reset в catch блоке кто Вам мешает в правильном порядке? использование goto в "с", действительно в некоторых случаях оправдано, но в случае с с++ - не убедили.

  • Anonymous
    January 01, 2003
    Powerman: Да, тогда значительно лучше. Но все равно некоторая проблема присутствует. В скрипте это не заметно, но в случае с exception все равно присутствуют тонкие детали вроде деинициализации, деинициализации в правильном порядке... Остальные два автора похоже забыли прочитать не только статью, но и комментарии.

  • Anonymous
    January 01, 2003
    При компиляции Try3 будут предупреждения о том, что переменные не инициализированы. Try2 нужно модифицировать и заменить обычные указатели на смарт. Это сократит код и delete будет сделан автоматически. А goto лучше использовать для быстрого выхода из вложенных циклов (см.документацию по c#)

  • Anonymous
    January 01, 2003
    Потому что вы не прочитали условие. Сломается. Вы решали другую задачу. Прочитайте еще. Я прочитал еще раз чтобы не обижать человека случайно - сломается. Там все очень понятно сказано.

  • Anonymous
    January 01, 2003
    Сомнение - это хорошо. Насчет использования GOTO - так вся статья как раз о том, что есть случай, когда его можно использовать. А ваш код к одной строчке не сводится, поскольку COND(n) все равно надо имплементировать и сие выражение ничего не говорит о том, что же оно считает. Я уж не говорю о том, что вы упустили очень важный момент - вычисления разных условий зависят друг от друга. Ваш код в этом случае просто не выполняет условий задачи. Garb, не обижайтесь. Как упражнение ваше решение ок, но это далеко не всегда практично по очень многим причинам, а иногда просто неприменимо. Некоторые из них я вам назвал, хотите обратите внимание (еще это называется "учиться"), не хотите - от меня не убудет.

  • Anonymous
    January 01, 2003
    Чего-то содержательные комментарии истощились... Андрей, вы не прочитали условий задачи, ваше гениальное предложение грохнется в первых же строчках.

  • Anonymous
    January 01, 2003
    Ну, если ставить задачей вычистить if'ы и заменить их длинными логическими выражениями, то вы своей цели почти достигли. Хотя, по моему скромному мнению, если уж хочется преследовать такие цели, то нечего использовать C++. А у меня целью был читаемый и компактный код, который легче поддерживать. Ну, и хорошо бы эффективный. Так что мы решали разные задачи.

  • Anonymous
    January 01, 2003
    Кстати, Элдар, всё-равно у Вас премодерирование комментариев, дайте людям возможность во-первых нормально форматировать свои комментарии, и во-вторых делать Preview перед отправкой. Не может быть, чтобы движок technet не поддерживал такие базовые фичи!

  • Anonymous
    January 01, 2003
    Честно говоря, возражение насчёт исключений я не понял. У меня весь скрипт по die не умирает - все die находятся в блоке eval, который и перехватывает исключения, а после eval эти исключения превращаются в warn (т.е. print на STDERR), и выполнение скрипта продолжается.

  • Anonymous
    February 06, 2008
    Не сказал бы, что использование goto в данном примере так уж необходимо... Как минимум, можно вынести куски кода код вида Condition1 * c5 = new Condition5();     if (!c5->Cond()) {          printf("Error with condition 5!");          goto Exit;     } в отдельную функцию - перегруженную или же шаблонную, в зависимости от конкретной ситуации. А если подумать - то есть и другие варианты...

  • Anonymous
    February 06, 2008
    Ненависть к goto прививается не потому что это зло и его надо искоренять, а потому, что его бездумное использование как правило усложняет читабельность программ и потенциально провоцирует появление ошибок. Заставлять не использовать goto нужно молодых, это заставляет их напрячь немного мозги. А то чуть-что goto, а потом смотришь в программу, а там сплошное спагетти... Нас в свое время в универе учили на паскале, в котором напрочь был зарезан goto :-). А данный пример, как раз тот случай, когда использование goto вполне оправдано... Но таких случаев не так уж много. P.S. Строго говоря все if, case, циклы все равно преобразуются в разновидности goto :-)

  • Anonymous
    February 06, 2008

  1. проверка указателя на NULL перед вызовом delete необязательна.
  2. обычно new бросает std::bad_alloc, но в примере очевидно дефолтное поведение изменено.
  3. можно еще использовать пустые циклы for или while: for ( ; ; ); while (true); Выходить из них по break'у
  • Anonymous
    February 06, 2008
    Действительно, понимание принципов использования операторов типа goto на данный момент отсуствует. Это связано с тем, что появилось поколение разработчиков, которое считает, что данный оператор - зло априори. Что-то типа "он плохой, потому что все так думают (пишут в книгах / говорят умные люди и т.д.)". Это печально. А идея  ваша замечательна своей простотой (и отсуствием "суперметодов" метапрограммирования). Мое ощущение - нравится.

  • Anonymous
    February 06, 2008
    А почему бы просто не использовать AutoPtr (или CComPtr в случае с СОМ). Хотя конкретно в этом случает использование goto и не оправдывает себя, иногда он бывает действительно полезным...

  • Anonymous
    February 06, 2008
    Если бы у оператора goto были только минусы, его (или аналога) бы не вводили в новых языках программирования.

  • Anonymous
    February 06, 2008
    хех..goto..как много в этом слове... Думаю что: auto_ptr<Condition> c1 (new Condition()); if (!c1->Cond(1))    return error("Error with condition 1!"); ... ну дальше понятно. если нужны какие-то специфические действия помимо delete - думаю что (в псевдокоде) shared_ptr<void> handle1 = shared_ptr<void>(::CreateHandleFake1(....),::CloseHandle); if(!something_with_handle(handle1.get()))    return; shared_ptr<void> handle2 = shared_ptr<void>(::CreateHandleFake2(....),::CloseHandle); if(!something_with_handle(handle2.get()))    return; Ну а если и этого мало - пишем собственные врапперы. Насчет примеров приминения goto - допустимые слкчаи применения goto я свожу к расширению операторов break и continue на вложенные циклы/свитчи. В других местах как-то всегда можно без goto обойтись. Не потому что goto это плохо, а потому что без него лучше. С вложенными циклами/свитчами тоже как правило можно...но часто просто лень). Случаи "в проекте нельзя пользоваться стандартной библиотекой, бустом (shared_ptr будет в стандарте скоро) и нельзя пользоваться собственными шаблонными реализациями auto_ptr и других "умных" указателей" не рассматриваю :) Если будет такой проект - буду использовать goto, ибо читабельность важнее стереотипов:). Что касается C# - эту лапшу тоже почти всегда можно избежать используя using

  • Anonymous
    February 06, 2008
    В современной языковой среде для этого используются with или using и исключения http://msdn2.microsoft.com/en-us/library/aa664736.aspx http://www.python.org/dev/peps/pep-0343/ если приходится работать с несовременной средой из современной (типа того же COM) надо сделать переходник, превращающий среду в современную что-то типа IMyResource mycond(x) {   if (!Cond(x))      throw new ConException(x) } using (var R1=Cond(1), var R2=Cond(2), var R2=Cond(3)) {    /// do something }

  • Anonymous
    February 06, 2008
    насчет исключений - верно ведь :), насчет сборщика мусора.. не обязательно. scoped guard'ы очень даже помогли бы, и выдержано это будет во вкусном с++ стиле :)

  • Anonymous
    February 06, 2008
    Отличное место для использования exceptions

  • Anonymous
    February 06, 2008
    Прочитал, и подумал - все таки хорошо что языки программирования не стоят на месте, а развиваются. Например если взять C#, то там есть сборка мусора, а для явной финализации есть конструкция и разрешены множественные return-ы: using(Condition1 c = new Condition1())  if(!c.cond())   {     Console.WriteLine("Error with condition 1!");     return;   } using(Condition2 c = new Condition2())  if(!c.cond())   {     Console.WriteLine("Error with condition 2!");     return;   } b т.д. А вообще, многие современные языки (не только C#) позволяют решать многие задачи в разы быстрее чем на C++

  • Anonymous
    February 06, 2008
    Вы, извиняюсь, ошибочку 'goto bypasses initialization of local variable' никогда не получали? В этом коде 4 ошибки. Правильно: #define Check(cond) if (!(cond)) { printf("Error with condition "#cond); break; } void Try4 (void) {  Condition1 * c1 = NULL;  Condition2 * c2 = NULL;  Condition3 * c3 = NULL;  Condition4 * c4 = NULL;  Condition5 * c5 = NULL;  do {     c1 = new Condition1();     Check(c1->Cond());     c2 = new Condition2();     Check(c2->Cond());     c3 = new Condition3();     Check(c3->Cond());     c4 = new Condition4();     Check(c4->Cond());     c5 = new Condition5();     Check(c5->Cond());     // My real code     printf("Hurra!");  } while (0);  delete c5;  delete c4;  delete c3;  delete c2;  delete c1; } delete NULL безопасно, проверка не нужна. А разрушать на всякий случай будем в обратном порядке.

  • Anonymous
    February 06, 2008
    А что будет, если вдруг "случайно" в С++ коде выскочит exception?!?!?! Читаем http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization и пользуемся smart pointers и auto_ptr.

  • Anonymous
    February 06, 2008
    Есть десятки решений:

  1. Например добовлять кондишены в колекцию, а потом пройтись по ней и очитстить. В конекрено вашем примере можно и Cond отправить в обработку в колекции. Collection<Condition> conditionsToResolve = new Collection<Condition>(); try { conditionsToResolve.Add(new Condition()); // First condition conditionsToResolve.Add(new Condition()); // Second condition conditionsToResolve.Add(new Condition()); // Third condition foreach (Condition condition in conditionsToResolve) { if (condition.CanPass) { return true; } } return false; } finally { foreach (Condition condition in conditionsToResolve) { if (condition != null) { condition.Dispose(); } } }
  2. Можно отрефаторить в два метода. Проверка в одном методе который будет возаращать будево. Во вротом собно проверка и печать. Очистка будет проходить в финалли (незнаю подерживаеться ли это в С++). static bool CanPass() { try { // Do your porcessing here Console.WriteLine("block"); return true; } finally { // Cleanup your conditions Console.WriteLine("finally"); } }
  3. и так далее, вобщемто именно эта проблема не опарвдание для goto.
  • Anonymous
    February 06, 2008
    goto - один из способов организации автоматного программирования в императивных языках.  Есть методики совмещать структурное программирование с автоматным - одна из них указана в примере.  Страшен goto лишь в глазах слепых

  • Anonymous
    February 06, 2008
    а еще почему-то вспомнились смарт-поинтреры.. или это уже будет не структурное программирование?

  • Anonymous
    February 06, 2008
    Вариант избежать оператора goto: Condition1* c1 = NULL, ...; while (true) {   c1 = new Condition1();   if (!c1->Cond())   {      printf(...);      break;   }   ...   break; ) delete c1; ... Согласен с Powerman насчет использования исключений.

  • Anonymous
    February 06, 2008
    The comment has been removed

  • Anonymous
    February 06, 2008
    При компиляции Try3 будут предупреждения о том, что переменные не инициализированы. Try2 нужно модифицировать и заменить обычные указатели на смарт. Это сократит код и delete будет сделан автоматически. А goto лучше использовать для быстрого выхода из вложенных циклов (см.документацию по c#)

  • Anonymous
    February 06, 2008
    Если зоной видимости метки будет сама функция, то применение метки допустимо. Тогда это уже точно вопрос стиля, хотя лично я после глобальных меток в Cobol уже не хочу ими пользоваться, уж лучше исключения.

  • Anonymous
    February 06, 2008
    Странно, писал писал... а оно не запостилося. Ну да ладно код у меня остался. Collection<Condition> conditionsToResolve = new Collection<Condition>(); try { conditionsToResolve.Add(new Condition()); // First condition conditionsToResolve.Add(new Condition()); // Second condition conditionsToResolve.Add(new Condition()); // Third condition foreach (Condition condition in conditionsToResolve) { if (condition.CanPass) { return true; } } return false; } finally { foreach (Condition condition in conditionsToResolve) { if (condition != null) { condition.Dispose(); } } } думаю суть понятна, сначала создаем список кондишенов, а потом уже чето делаем. Если так не подходит не проблема. Просто когда создаем кондишены, еще их и регеним в списке для удаления. Потом чистим. Еще всю эту фигню надо отрефакторить так чтобы кондишен (всмыле все кондишены) был в одном методе, а printf("Hurra!"); в другом. Вобщемто я не считаю что в этом случае необходим goto.

  • Anonymous
    February 06, 2008
    Powerman: Исключения далеко не всегда можно использовать -- например, когда хочется продолжать работу и удалять все равно надо получится все то же самое. А если по каждому условию помирать, то, конечно, почему бы и нет. Pavel: да, предупреждения будут. По хорошему все они должны быть обьявлены вначале и инициализированы NULL. А авто-смарт-пойнтеры, но "лесенки" не уберут, поскольку смарт или не смарт, а ни один из обьектов нельзя инициировать пока не проверили все предыдущие условия.

  • Anonymous
    February 06, 2008
    goto - ацтой, нет никаких причин и оснований для его использования. try...catch для кого придумали? да и смарт поинтеры тоже не помешают, действительно...

  • Anonymous
    February 07, 2008
    IMHO в данном случае (по крайней мере в C#) разумнее goto заменить на throw MyException, объекты деинициализировать в finally

  • Anonymous
    February 07, 2008
    > goto - ацтой, нет никаких причин и оснований для его использования. Есть множество алгоритмов, которые с использованием goto записываются намного понятнее, чем без него:

  • Конечные автоматы, особенно если есть некий выделенный ("правильный") порядок событий, так что обработчики большинства состояний можно записать сверху вниз по "правильному" порядку состояний.
  • Многие варианты альфа-бета решателей.
  • Оптимистичные вычислители в задачах имитационного моделирования. При этом читабельность goto может быть улучшена простым приёмом: пишите не goto blablabla, а goto blablabla; /* see above / или goto blablabla; / see below */
  • Anonymous
    February 08, 2008
    >Есть множество алгоритмов, которые с использованием goto записываются намного понятнее, чем без него: Конечные автоматы реализованы в boost и без всякого изврата типа goto. А теперь касательно исходной задачи. template<unsigned n> struct container { &nbsp;&nbsp;&nbsp;&nbsp;boost::shared_ptr<Condition> cond_; &nbsp;&nbsp;&nbsp;&nbsp;container<n-1> cont_; &nbsp;&nbsp;&nbsp;&nbsp;unsigned init() &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cond_=new  Condition(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(cond_->Cond(n)) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return cont_.init(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return n; &nbsp;&nbsp;&nbsp;&nbsp;} }; template<> struct container<0> { &nbsp;&nbsp;&nbsp;&nbsp;unsigned init(){return 0;} }; ... container<5> conditions; if(!conditions.init()) &nbsp;&nbsp;&nbsp;&nbsp;printf("Hurra!"); вроде ничего не напутал... кстати, циклов нету ни одного.

  • Anonymous
    February 08, 2008
    >Есть множество алгоритмов, которые с использованием goto записываются намного понятнее, чем без него: Конечные автоматы реализованы в boost и без всякого изврата типа goto. А теперь касательно исходной задачи. template<unsigned n> struct container { &nbsp;&nbsp;&nbsp;&nbsp;boost::shared_ptr<Condition> cond_; &nbsp;&nbsp;&nbsp;&nbsp;container<n-1> cont_; &nbsp;&nbsp;&nbsp;&nbsp;unsigned init() &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cond_=new  Condition(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(cond_->Cond(n)) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return cont_.init(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return n; &nbsp;&nbsp;&nbsp;&nbsp;} }; template<> struct container<0> { &nbsp;&nbsp;&nbsp;&nbsp;unsigned init(){return 0;} }; ... container<5> conditions; if(!conditions.init()) &nbsp;&nbsp;&nbsp;&nbsp;printf("Hurra!"); вроде ничего не напутал... кстати, циклов нету ни одного.

  • Anonymous
    February 08, 2008
    Что касается C++ то там нет смысла так извращаться, ибо есть несколько более предпочтительных вариантов... тем более, что new бросает исключение... :) Что касается более примитивных языков типа си - то экстремально коротко можно написать так: Condition *с1 = (Condition *)malloc (...); Condition *с2 = с1 ? malloc (...) : NULL; Condition *с3 = с2 ? malloc (...) : NULL; Condition *с4 = с3 ? malloc (...) : NULL; Condition *с5 = с4 ? malloc (...) : NULL; if (c5) {     // многа буков..     printf ("Hurra!!n"); } if (c5) free (c5); if (c4) free (c4); if (c3) free (c3); if (c2) free (c2); if (c1) free (c1); Ну приблизительно. Хотя в этом коде много взаимозависимостей, сложно будет поддерживать, лучше использовать статусную переменную. int status = 1; с1 = ... if (c1 == NULL) {    // поругаться как следует..    status = 0; } if (status) {    c2 = ... } ... if (status) {    ptinrf ("Hurra!n"); } // free stuff. Стоит отметить что в последнем случае нет необходимости вкладывать условия друг в друга. PS: Вообще тема конечно довольно избитая, года с 60-го... :) ИМХО нет в goto никакой необходимости.

  • Anonymous
    February 08, 2008
    А с применением макроса - вообще получается жесть... такой код проще будет переписать, чем поддерживать.

  • Anonymous
    February 08, 2008
    >Андрей, вы не прочитали условий задачи, ваше гениальное предложение грохнется в первых же строчках. Это еще почему... Ни один последующий объект не будет создан до тех пор, пока предыдущие объекты не создадутся. Да и сам код сработает только после того, как создадутся все. Я написал код грубо, но он практически работоспособен. В условии не сказано что на каждую ошибку надо ругаться. :) В остальном я все условия выполнил... да пусть мне нужно выделить хоть 100 объектов, это ничего не изменит, хотя тогда их проще засунуть в массив :) и выделять в цикле... PS: А вообще спор о пользе goto - он вечен, не знаю точно что меня разобрало оставить каммент, начинался Макконела :)

  • Anonymous
    February 09, 2008
    The comment has been removed

  • Anonymous
    February 09, 2008
    The comment has been removed

  • Anonymous
    February 25, 2008
    Как-то даже удивительно, что еще читая начало статьи, я уже представлял, как бы отреагировали на нее некоторые известные мне "крутые" савецкие программеры, не догадываясь, что увижу эти навязанные чтением книжек рефлексы чуть ниже. "а ты что, не знаешь, что вместо #define можно использовать inline-функции? Ах ла, тогда из-за goto все перестанет компилироваться... все равно препроцессором сейчас никто не пользуется, [даже если компилятор не умеет доставать из obj-файлов значения констант для их распространения в выполняемый код других obj-файлов]..." "а ты что, не знаешь, что delete хавает NULL-значения?" - Блин, ну как тут вспомнить, откуда взялась эта привычка... через полгода после спора можно вспомнить, что у кого-то типа Кернигана и Пайка что-то про это читал, но что докажешь про это человеку сейчас, если он никогда не программировал на нестандартном и недоделанном недо-UNIX-компиляторе? А вообще, если по теме: GOTO ведь есть обычная команда процессора JUMP. Посему это просто кусок ассемблерного кода, иногда вставляемый для оптимизации, когда важна не читаемость, а скорость выполнения. И никуда от него не деться - оптимизации были, есть и будут. Попробуйте посоревноваться с самым крутейшим компилятором в оптимизации какого-нибудь сложного алгоритма (ну как, получается?), а потом простенького: не замечал я почему-то, чтобы компилятор умел в чуть более сложных случаях, чем strlen повтыкать что-то типа movsb/lodsb/scasb всюду, где возможно. Правда, сейчас процы пошли мутные, так что мутный вопрос еще сложнее стал... В структурных языках я использовал goto всего один раз: в одном языке не было прерывания цикла, и была важна производительность.

  • Anonymous
    November 24, 2008
        bool isError = false;     Condition1 * c1 = new Condition1();     if (!c1->Cond()) {          printf("Error with condition 1!");          isError = true;     }     Condition2 * c2 = new Condition2();     if (!c2->Cond()) {          printf("Error with condition 2!");          isError = true;     }     Condition3 * c3 = new Condition3();     if (!isError && !c3->Cond()) {          printf("Error with condition 3!");          isError = true;     } ...     if (!isError)     // My real code        printf("Hurra!"); с уважением. поправьте, если я не прав.

  • Anonymous
    January 15, 2009
    Не поленился, написал как бы я сделал. на форму надо поместить компонент Мемо. Вобщем, функция будет выглядеть так: void Try1(void) {   auto_ptr<Condition1>(new Condition1())->CondPrint(2) &&   auto_ptr<Condition1>(new Condition1())->CondPrint(10) &&   auto_ptr<Condition1>(new Condition1())->CondPrint(1) &&   auto_ptr<Condition1>(new Condition1())->CondPrintHurra(); } //Unit1.cpp для Borland C++Builder5 //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "Unit1.h" #include <memory> using namespace std; //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource ".dfm" TForm1 Form1; void printf(char s) {   Form1->Memo1->Text=Form1->Memo1->Text+"rn"+s; } struct Condition {   Condition()   {      printf("Condition");   }   bool Cond(int i)   {      switch(i)      {         case 1:            return true;         case 10:            return false;         case 2:            return true;         // ...      }      throw "undef i";//error   } }; //--------------------------------------------------------------------------- void Try(void) {   if(auto_ptr<Condition>(new Condition())->Cond(2))   {      printf("Error with condition 2!");      return;   }   if(auto_ptr<Condition>(new Condition())->Cond(1))   {      printf("Error with condition 1!");      return;   }   if(auto_ptr<Condition>(new Condition())->Cond(10))   {      printf("Error with condition 10!");      return;   }   printf("Hurra!"); } struct Condition1: Condition {   char Print(int i)   {      switch(i)      {         case 1:            return "Error with condition 1!";         case 10:            return "Error with condition 10!";         case 2:            return "Error with condition 10!";         // ...      }      throw "undef i";//error   }   bool CondPrintHurra()   {      printf("Hurra");      return true;   }   bool CondPrint(int i)   {      bool ret=Cond(i);      if(!ret)         printf(Print(i));      return ret;   } }; void Try1(void) {   auto_ptr<Condition1>(new Condition1())->CondPrint(2) &&   auto_ptr<Condition1>(new Condition1())->CondPrint(10) &&   auto_ptr<Condition1>(new Condition1())->CondPrint(1) &&   auto_ptr<Condition1>(new Condition1())->CondPrintHurra(); } __fastcall TForm1::TForm1(TComponent* Owner)   : TForm(Owner) {   Form1->Memo1->Text="";   Try1(); }

  • Anonymous
    January 16, 2009
    Подумал, но по своему:) Поясню свой пример. В нём представлено две версии функции Try() и Try1(). Try() решение в традиционном стиле. Try1() в стиле логического программирования. печать в конструкторе сделана только для отладки. Функция Cond() реализована для примера и выдаёт false для параметра 10. Класс Condition1 реализован только чтобы показать отличия двух стилей. После запуска на форме получим сообщение: Condition Condition Error with condition 10! ,что говорит о правильном порядке выполнения конструкторов и условий последовательно слева направо с правильным вызовом конструкторов. Если изменить в Cond() "case 10" на true, то будет напечатано следующее: Condition Condition Condition Condition Hurra Для тех кто думает, что это наворочено, хочу заметить, что важен конечный результат, то есть, то, ради чего всё делалось. Теперь можно создавать логические выражения произвольной сложности в стандартном булевском синтаксисе безо всяких там if-ов. А сейчас небольшой разочаровывающий нюанс: Порядок вызова деструкторов разный для Try1() и Try2()! Чтобы не получилось игры в одни ворота позволю себе "...подумать о читателях этого кода..." и не приведу решение.;) С уважением, Garb.

  • Anonymous
    January 19, 2009
    Закралось у меня сомнение. Вы, Эльдар в одном маленьком куске кода использовали всё что считается дурным тоном в программировании на С++. И goto и макросы. Макрос обычно предполагает многократное повторное использование, по крайней мере в рамках одного проекта. Это явно не XP стиль. То есть, Вы с одной стороны говорите о простоте, а с другой стороны явно предполагается повторное использование кода. В последнем случае лучше использовать проверенные библиотеки с auto_ptr и шаблонами. В моём случае если обернуть макросом повторяющиеся последовательности кода, всё вообще будет безумно просто. Что-то вроде: void Try() {   return COND(1) && COND(10) && COND(2) && HURRA; } С уважением, Garb.

  • Anonymous
    January 20, 2009
    Так я же показал, что порядок вычислений и вызова конструкторов и деструкторов сохраняется. Более того, порядок вычисления булевских, да и вообще выражений жёстко зафиксирован в стандарте С++. Я не вижу здесь проблем. У меня мнение такое. Как частный случай в XP- стиле Ваше решение приемлемо. Как общее решение для сложных условий и повторного использования я склоняюсь к моему варианту. По своему опыту скажу: я тоже использую goto и макросы. Но в случае с goto если надо что-то быстро сделать, а ничего другого на ум не приходит. Как правило, если есть время на обдумывание, стараюсь обойтись другими средствами. С макросами другая история: И ATL и VCL используют макросы. У меня нет оснований сомневаться в уровне программистов, создававших эти важные библиотеки. Видимо это нормально. С уважением, Garb. PS Да какие тут обиды. Ничего личного. Обменялись мнениями и отлично.