Пропустить код, не используя переменную состояния, используя предусмотренное 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++ а я бы пошел с использованием исключений и trycatch блок.

Вы можете найти много полезной информации о предмете на 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;

Но опять же, я не одобряю это как изящное решение, я лично переписал бы ваш код и разбил бы его на что-то более читабельное. Но опять же я тоже не пишу функции, содержащие сотни строк.

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