C ++ исключения и логика выполнения программы - PullRequest
0 голосов
/ 06 мая 2010

Я прошел через несколько вопросов, но не нашел ответа.

Интересно, как реализовать обработку исключений в программном обеспечении C ++, чтобы оно было централизованным и отслеживало ход выполнения программного обеспечения?

Например, я хочу обработать исключения на четырех этапах программы и знаю, что исключение произошло на этом конкретном этапе:
1. Инициализация
2. Сценарий обработки
3. Расчет
4. Подведение итогов.

В этот момент я попробовал это:

int main (...)
{
...
// start logging system
try {
    ...
    }
catch (exception &e)
  {
        cerr << "Error: " << e.what() << endl;
        cerr << "Could not start the logging system. Application terminated!\n";
        return -1;
  }
catch(...)
    {
        cerr << "Unknown error occured.\n";
        cerr << "Could not start the logging system. Application terminated!\n";
        return -2;
    }

// open script file and acquire data
try {
...
    }
catch (exception &e)
  {
        cerr << "Error: " << e.what() << endl;
        cerr << "Could not acquire input parameters. Application terminated!\n";
        return -1;
  }
catch(...)
    {
        cerr << "Unknown error occured.\n";
        cerr << "Could not acquire input parameters. Application terminated!\n";
        return -2;
    }
// computation
try {
...
}
...

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

Ответы [ 6 ]

3 голосов
/ 06 мая 2010

Вы можете сохранить глобальную переменную, содержащую состояние программы, и распечатать ее в блоке catch. Тогда вам понадобятся только два блока catch, один для std :: exception и один для всего остального.

2 голосов
/ 06 мая 2010

Мне кажется, что вы пытаетесь использовать исключения как (более или менее) замену регистрации. Я думаю, что в этом случае вам было бы намного лучше использовать их вместе - в частности, у меня, вероятно, был бы небольшой обработчик исключений, который помещал бы выходные данные в журнал, поэтому ваш код будет выглядеть примерно так:

try { 
    // start logging system
}
catch (exception const &e) { std::cerr << e.what << "error starting logging"; }
catch (...) { std::cerr << "Unknown error starting logging"; }

try { 
    {   scoped_log("script processing");
        start_script();
    }
    {   scoped_log("computation");
        do_computation();
    }
    {   scoped_log("wrap up");
        wrap_up();
    }
}
catch(std::exception const &e) { log << "error: " << e.what() << "\n"; } 
catch(...) { log << "Unknown exception\n"; }  

где "scoped_log" - это простой класс, похожий на:

class scoped_log { 
    std::string caption;
public: 
    scoped_log(std::string const &c) : caption(c) {
        log << "Starting:  " << caption << "\n";
    }
    ~scoped_log() { log << "Finished: " << caption << "\n"; }
};

Это позволяет вам централизовать большинство обработки исключений. Единственное исключение (без каламбура) - запуск самой системы ведения журнала - очевидно, вы не можете использовать ведение журнала для сообщения о проблемах при запуске системы ведения журнала.

Однако, когда система ведения журнала запущена, все становится намного проще - вы используете комбинацию ведения журнала и обработки исключений, чтобы отследить, когда возникла проблема. Помните одну незначительную деталь: поскольку scoped_log - это объект, который разрушается при выходе из области действия (по любой причине), журнал будет иметь структуру, которая может (по крайней мере изначально) казаться немного вводящей в заблуждение - сообщение об ошибке будет следовать сообщению "finshed xxx" в журнале. Например, если файл сценария не может быть открыт, ваш журнал может выглядеть примерно так:

starting script processing
finished script processing
error: could not open file: 'script.input'

По крайней мере, IMO, это вряд ли вызовет проблемы, если вы знаете ситуацию.

1 голос
/ 06 мая 2010

Простой способ централизовать обработку исключений - создать простую функцию для обработки исключений, а затем использовать общий перехват, который вызывает функцию:

void processExceptions( std::string const & stage )
{
   std::cout << "Exception caught at stage " << stage << std::endl;
   try {
      throw; // rethrow the last caught exception
   }
   catch ( exception1 const & ) { 
      // do process 1
   }
   catch ( exception2 const & ) {
      // ...
   }
}
int main()
{
   try {
      initialize();
   }
   catch ( ... ) {
      processExceptions( "initialization" );
   }
   try {
      stage2();
   }
   catch ( ... ) {
      processExceptions( "stage2" );
   }
}

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

Обратите внимание, что если вы вызовете функцию, когда не было сгенерировано исключение (вне перехвата), вы получите неопределенное поведение и чаще всего ваше приложение умрет.

1 голос
/ 06 мая 2010

По моему мнению, исключения должны (вообще) быть пойманы в 5 условиях:

  1. Вы можете законным образом обработать исключение и продолжить работу в обычном режиме (например, вы получили повышение :: bad_lexical_cast из пользовательского ввода, но можете восстановить, сообщив об ошибке пользователю).
  2. Вам необходимо перевести исключение из одного источника в другой (например, ваша библиотека использует другую библиотеку как чистую деталь реализации и хочет перевести «socket_exception» в «service_exception» или что-то в этом роде). В .Net я бы сделал это, создав новый экземпляр исключения с внутренним исключением. В C ++ может быть полезно обеспечить согласованный интерфейс для ваших пользователей, особенно если вы, например, выбрасываете только исключения, полученные из std :: exception, но зависимая библиотека выдает исключения из нестандартного типа (строка, int, const char *, пользовательский тип и т. д.).
  3. Вы пишете обертку на разных языках. C ++ / C, C ++ / Fortran и т. Д. Исключения в C ++ не всегда хорошо преодолевают эти барьеры, и лучше всего перевести их в целевые средства обработки ошибок времени выполнения, если это возможно.
  4. Вы находитесь на главной странице и хотите получить небольшую информацию из исключения и журнала перед тем, как произойдет сбой. Если вы сделаете это, есть два основных лагеря, которые будут делать дальше: выход (1) или бросок; и сгенерировать ядро ​​(на * nix). У меня смешанные чувства ...
  5. очень похоже на main, вокруг записи потока. Может быть полезно зарегистрировать причину прерывания потока, а затем соответствующим образом обработать завершение потока.
0 голосов
/ 06 мая 2010

Если вы действительно хотите более централизовать это, вы можете определить свои собственные классы ошибок, такие как


class does_not_open {};  
class cannot_write {};

или около того. Вам понадобится больше работы в разных частях, например, проверьте, успешно ли открыто; если нет, то сгенерировать did_not_open (и т. д.). Но после того, как вы организуетесь таким образом, вы можете поместить всю основную часть вашего кода в следующую форму:


try{  


}  

catch(does_not_open &e){  


}

catch(cannot_write &e){

- your code here -

}
catch(...){

- your code here -

}

Не уверен, что это достигло того, на что вы надеялись ... но, гл. :)

0 голосов
/ 06 мая 2010

Другая альтернатива - иметь отдельные, определенные вами исключения, которые вы затем обрабатываете в одном центральном месте вместо разных возвращаемых значений. Поэтому, когда исключение возникает как «не удалось получить входные параметры», вы бы выдавали что-то вроде «исключение invalid_parameters» с добавлением некоторой дополнительной контекстной информации о причине исключения. Затем в центральном месте вы отображаете ошибку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...