Пропустить код, не используя переменную состояния, используя предусмотренное goto
У меня есть код, в котором есть части, которые не должны выполняться, если раньше в коде была ошибка. Я на самом деле использую переменную bool под названием EndProg
что, если установлено true
, будет указывать программе избегать выполнения некоторых частей кода.
Моя проблема в том, что я не хочу использовать этот метод, и я бы предпочел использовать goto
вместо этого, потому что это заставит программу перейти к части очистки и избежать проверки EndProg
значение несколько раз.
Другая проблема заключается в том, что я читал на многих страницах Stackru и других веб-сайтов, которые используют goto
считается плохой практикой и может сделать код более трудным для чтения или создания ошибок.
Мой код достаточно прост, и мне нужно будет использовать только одну метку, поэтому я сомневаюсь, что это создаст проблемы; но я хотел бы знать, есть ли другие способы сделать то, что я хочу, без создания функций для выполнения задач очистки или использования return
(потому что, например, мне нужно будет написать код очистки несколько раз), и я также не хочу писать один и тот же большой код очистки в нескольких местах, а затем использовать return
или сделать что-то еще.
Я не хочу ни увеличивать количество строк кода, ни использовать return
не используйте много if
и не проверяйте значение переменной состояния. Чтобы вы посоветовали?
Вот кусок кода:
bool EndProg=false;
/*
Lot of code that can set EndProg to true
*/
ClassType ClassName;
if(!EndProg && LoadConf(&ConfFilePath,&ClassName)==0)
{
int fildes=-1;
if(ClassName.abc) // bool
{
if(ClassName.FilePath==0) // char *
{
ClassName.FilePath=new(std::nothrow) char[9]();
if(ClassName.FilePath!=0)strcpy(ClassName.FilePath,"file.ext");
else EndProg=true;
}
if(!EndProg && mkfifo(ClassName.FilePath,S_IRUSR | S_IWUSR)==-1)
{
if(errno==EEXIST)
{
/* EEXIST is returned if the file already exists
We continue, later we will try to open this file */
}
else EndProg=true;
}
if(!EndProg && (fildes=open(ClassName.FilePath,O_RDWR))==-1)EndProg=true;
}
/*
Lot of code that will check if EndProg == true
*/
}
delete[] ClassName.FilePath;
delete[] ConfFilePath;
Что я хотел бы сделать, это:
bool EndProg=false;
/*
Lot of code that can set EndProg to true
*/
ClassType ClassName;
if(LoadConf(&ConfFilePath,&ClassName)==0)
{
int fildes=-1;
if(ClassName.abc) // bool
{
if(ClassName.FilePath==0) // char *
{
ClassName.FilePath=new(std::nothrow) char[9]();
if(ClassName.FilePath==0)goto cleanup;
strcpy(ClassName.FilePath,"file.ext");
}
if(mkfifo(ClassName.FilePath,S_IRUSR | S_IWUSR)==-1)
{
if(errno==EEXIST)
{
/* EEXIST is returned if the file already exists
We continue, later we will try to open this file */
}
else goto cleanup;
}
if((fildes=open(ClassName.FilePath,O_RDWR))==-1)goto cleanup;
}
/*
Lot of code that will check if EndProg == true
*/
}
cleanup:
delete[] ClassName.FilePath;
delete[] ConfFilePath;
Как видите, это не сложно понять, и даже если поиск по ярлыку может быть проблемой для кого-то, это не для меня; и я не планирую делать код общедоступным.
Обновить:
Я решил использовать исключения, и это работает для некоторых частей моего исходного кода. Но я сомневаюсь, что это будет легко реализовать в более сложных частях. Спасибо за ваши ответы.
4 ответа
Поскольку вы отметили этот вопрос c++
а я бы пошел с использованием исключений и try
catch
блок.
Вы можете найти много полезной информации о предмете на SO и на других сайтах:
Вот очень простой урок.
А вот хороший и простой FAQ, который может вам помочь.
В принципе, нечего бояться, исключения не являются загадочными и, на самом деле, имеют больше смысла, когда вы освоите их. Потому что в основном эта концепция позволяет вам достичь именно того, что вы хотите:
Несколько ловушек, которые могут быть обработаны одним и тем же кодом обработки ошибок.
Изменить: например, если бы я переместить mkfifo
и т. д. в функцию (в общем случае создание функции для каждого четко определенного логического блока является более понятным и более читабельным) и имеет нечто вроде
Это просто набросок, чтобы дать вам общее представление:
#include <exception>
functionThatDoesMakeFifo(...){
// check which ever conditions you want to check after mkfifo
// if one of them goes wrong just do:
throw std::exception();
}
// this is inside your function:
ClassType ClassName;
try{
ClassName.FilePath = new char[9](); // even though I'd use a string...
.
.
. // rest of the code
} catch(std::exception &e){
delete [] ClassName.FilePath;
delete [] ConfFilePath;
ClassName.FilePath = NULL; // not mandatory just my habit
ConfFilePath = NULL;
}
Я хотел бы попробовать что-то вроде Scope Guards или BOOST_SCOPE_EXIT (C++) или его аналог C++11:
template<class F>
struct ScopeExit
{
ScopeExit(F f) : f(f) {}
~ScopeExit() { f(); }
F f;
};
template<class F>
ScopeExit<F> MakeScopeExit(F f) { return ScopeExit<F>(f); }
#define STRING_JOIN2(arg1, arg2) DO_STRING_JOIN2(arg1, arg2)
#define DO_STRING_JOIN2(arg1, arg2) arg1 ## arg2
#define SCOPE_EXIT(code) \
auto STRING_JOIN2(scope_exit_, __LINE__) = MakeScopeExit([=](){code;})
bool myfunct()
{
ClassType ClassName;
ClassName.FilePath = 0;
ConfFilePath = 0;
SCOPE_EXIT(delete [] ClassName.FilePath; delete [] ConfFilePath; );
if (LoadConf(&ConfFilePath,&ClassName) == 0)
{
int fildes=-1;
if(ClassName.abc) // bool
{
if(ClassName.FilePath==0) // char *
{
ClassName.FilePath=new(std::nothrow) char[9]();
if(ClassName.FilePath==0) return false;
strcpy(ClassName.FilePath,"file.ext");
}
if(mkfifo(ClassName.FilePath,S_IRUSR | S_IWUSR)==-1)
{
if (errno==EEXIST)
{
/* EEXIST is returned if the file already exists
We continue, later we will try to open this file */
}
else return false;
}
if((fildes=open(ClassName.FilePath,O_RDWR))==-1) return false;
}
/*
Lot of code that will check if EndProg == true
*/
}
return true;
}
я использую return
но код очистки только в одном месте.
Тем не мение ClassName
следует позаботиться о том, чтобы убрать собственные ресурсы в деструкторе.
Возможно, за это проголосуют, но я думаю, что ограниченное использование goto в C не является злом. В частности, то, о чем вы говорите, вполне приемлемо: переход вперед для очистки кода от ошибок. Я бы посоветовал вам ограничить это одним целевым ярлыком для каждой процедуры.
То, что люди ненавидят (оправданно), это старый код для спагетти, когда Гото прыгает повсюду.
Я видел небольшой трюк, который поможет вам решить эту проблему, хотя я лично не фанат трюков, он может подойти для того, что вам нужно.
while (true)
{
if(ClassName.FilePath==0) // char *
{
ClassName.FilePath=new(std::nothrow) char[9]();
if(ClassName.FilePath==0) break;
strcpy(ClassName.FilePath,"file.ext");
}
if(mkfifo(ClassName.FilePath,S_IRUSR | S_IWUSR)==-1)
{
if(errno==EEXIST)
{
/* EEXIST is returned if the file already exists
We continue, later we will try to open this file */
}
else break;
}
if((fildes=open(ClassName.FilePath,O_RDWR))==-1) break;
/*
Lot of code that will check if EndProg == true
*/
break;
}
delete[] ClassName.FilePath;
delete[] ConfFilePath;
Но опять же, я не одобряю это как изящное решение, я лично переписал бы ваш код и разбил бы его на что-то более читабельное. Но опять же я тоже не пишу функции, содержащие сотни строк.