Можно ли использовать "go to" из оператора catch
Все, что мне когда-либо говорили, это то, что идти к злу и держаться подальше от них, но я думаю, что они могут помочь мне здесь (?). Я хотел бы предоставить пользователю возможность перезапустить приложение, когда возникнет исключение, и у меня возникли некоторые проблемы, связанные с тем, что делать...
Мое приложение будет отслеживаться другим процессом, но есть некоторые исключения, когда я хочу, чтобы пользователь мог решать, что делать, не возвращая управление вызывающему процессу.
Является ли что-то подобное "приемлемым"? Любые другие предложения?
Спасибо!
int main(){
initialize:
try{
//do things
}
catch(...)
{
cout<<"Would you like to try initializing again?"<<endl;
//if yes
goto initialize;
//if not
abort(); //or something...
}
return 0;
}
8 ответов
Почему не так?
while(true){
//Do stuff
if(exit){
break;
}
}
или же
continue = true;
do{
//Do stuff
if(exit){
continue = false;
}
}while(continue);
Вы можете попробовать:
int main()
{
while(true)
{
try
{
program();
}
catch(std::exception& e)
{
std::cout << "Start again?" << std::endl;
//Check ...
if(!go_on)
break;
}
}
return 0;
}
goto
всегда можно избежать, давая более чистый код.
Кстати, то же самое касается break
вне switch
, continue
Ключевое слово немного менее заслуживает осуждения, потому что, по крайней мере, оно соблюдает условие замкнутого цикла.
Важно ловить исключения в нужном месте, где вы сможете наиболее эффективно справиться с этим условием.
И если условие оказывается неудобным (например, "повторить попытку?" В моем случае), попробуйте отменить его ("не выполнить?") Для более чистой структуры.
// Tries until successful, or user interaction demands failure.
bool initialize() {
for ( ;; ) {
try {
// init code
return true;
}
catch ( ... ) {
cout << "Init Failed. Fail Program?" << endl;
if ( yes ) {
return false;
}
}
}
}
int main() {
if ( ! initialize() ) {
return EXIT_FAILURE;
}
// rest of program
return EXIT_SUCCESS;
}
Примечания: это не использовать goto
или же break
, и это не повторяется (особенно не изнутри catch
блок).
Да, технически это нормально, но применимы обычные "опасные" соображения.
Первоначальная цитата была (я считаю) "неконтролируемым использованием goto, считающимся вредным". Gotos может быть полезен, но должен использоваться контролируемым образом. Существует один метод программирования, позволяющий создавать входящие подпрограммы, зависящие от состояния программы или данных, что положительно требует направленных переходов. Хотя эту технику вполне можно считать старомодной, я понимаю, что она все еще используется, но скрыта от более современного языка и возможностей компилятора. Суть контроля заключается в том, что вы должны остановиться и спросить себя, не только "существует ли более структурированный способ сделать то же самое" - @Justin - но также "при каких конкретных условиях я буду использовать goto?" Быть удобным - вероятно, не достаточное условие, чтобы использовать это, без этого более широкого ответа.
Обычный способ обработки исключений - это то место, где вы можете что-то сделать. Очевидно, что место, где вы пытаетесь с ними справиться, не правильно, так как вы должны использовать goto.
Итак, как то так:
void mainLoop() // get user settings, process, etc
{
try
{
// 1) get user settings
// 2) process data
// 3) inform of the result
}
catch( const exception_type & e)
{
// inform of the error
}
}
int main()
{
try
{
while(true)
mainLoop();
}
catch(...)
{
std::cout<<"an unknown exception caught... aborting() " << std::endl;
}
}
да. На основе проекта стандарта C++ :
Оператор goto может использоваться для передачи управления из блока try или обработчика.
Например, это законный код:
lab: try {
T1 t1;
try {
T2 t2;
if (condition)
goto lab;
} catch(...) { /* handler 2 */ }
} catch(...) { /* handler 1 */ }
Однако важно отметить, что:
Когда это происходит, каждая переменная, объявленная в блоке try, будет уничтожена в контексте, который непосредственно содержит ее объявление.
Мне очень нравится такое использование. Пока он поддерживается в очень небольшом и локальном масштабе, он действительно позволяет избежать спагетти, которых все боятся, и на самом деле делает вещи более ясными, чем альтернативы.
try_again:
try {
do_something();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
goto try_again;
}
Если я пишу логику по-другому, я добавляю больше переменных, больше логики, больше строк, больше отступов. Мне не нравится этот цикл while, потому что имя нашей переменной не имеет смысла для первого прохода, и мы загрязняем нашу бизнес-логику, добавляя условия для выхода из цикла.
bool try_again = true;
while(try_again) {
try {
do_something();
try_again = false;
} catch( const std::exception& e) {
std::cerr << e.what() << std::endl;
}
}
Цикл / делает ситуацию немного лучше: нам нужно что-то сделать с нашей специальной переменной в обработке ошибок, чтобы повторить попытку. Но у нас все еще есть дополнительные переменные, логика и вложенность. Также, /whiles
очень редки и требуют немного дополнительных усилий мозга, чтобы вспомнить их во время чтения.
bool try_again;
do {
try_again = false;
try {
do_something();
} catch( const std::exception& e) {
std::cerr << e.what() << std::endl;
try_again = true;
}
} while (try_again);
Оригинал можно упростить с помощьюbreak
, и это, вероятно, самый красивый вариант циклов, но будет больно, если нам понадобится еще один небольшой цикл for или оператор переключения.
while(true){
try {
do_something();
break;
} catch( const std::exception& e) {
std::cerr << e.what() << std::endl;
}
}
Я бы сказал, что решение более ясное, чем оба этих цикла. Прежде чем опубликовать сообщение, я едва заметил ошибку в моемdo
/while
петля. Еще одна приятная вещь оgoto
Решение: если вы ошиблись с расположением метки, должна возникнуть ошибка:
try {
try_again:
do_something();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
goto try_again;
}
ошибка: невозможно перейти от этого оператора перехода к его метке
примечание: переход обходит инициализацию блока try