Читаем Программирование полностью

Ввод из потока cin через поток Token_stream с именем ts.

*/

Здесь мы использовали блок комментариев, который начинается символами /* и заканчивается символами */. В реальной программе история пересмотра может содержать сведения о том, какие именно изменения были внесены и какие улучшения были сделаны. Обратите внимание на то, что эти комментарии помещены за пределами кода. Фактически это несколько упрощенная грамматика: сравните правило для Инструкции с тем, что на самом деле происходит в программе (например, взгляните на код в следующем разделе). Этот комментарий ничего не говорит от цикле в функции calculate(), позволяющем выполнять несколько вычислений в рамках одного сеанса работы программы. Мы вернемся к этой проблеме в разделе 7.8.1.

<p id="AutBody_Root126"><strong>7.7. Исправление ошибок</strong></p>

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

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

void calculate()

{

  while (cin)

  try {

    cout << prompt;

    Token t = ts.get();

    while (t.kind == print) t=ts.get(); // сначала

                                        // игнорируем все

                                        // инструкции "печать"

    if (t.kind == quit) return;

    ts.putback(t);

    cout << result << expression() << endl;

  }

  catch (exception& e) {

    cerr << e.what() << endl; // выводим сообщение об ошибке

    clean_up_mess();

  }

}

Мы просто поместили цикл while в блоке try, который выводит сообщения об ошибке и устраняет неисправности. После этого работу можно продолжать по-прежнему. Что означает выражение “устранить неисправность”? В принципе готовность к выполнению вычислений после исправления ошибки означает, что все данные находятся в полном порядке и вполне предсказуемы. В калькуляторе единственные данные за пределами отдельных функций находятся в потоке Token_stream. Следовательно, мы должны убедиться, что в потоке нет лексем, связанных с прекращенными вычислениями и способных помешать будущим вычислениям.

Рассмотрим пример.

1++2*3; 4+5;

Эти выражения вызывают ошибку, и лексемы 2*3; 4+5 останутся в буферах потоков Token_stream и cin после того, как второй символ + породит исключение.

У нас есть две возможности.

1. Удалить все лексемы из потока Token_stream.

2. Удалить из потока все лексемы Token_stream, связанные с текущими вычислениями.

В первом случае отбрасываем все лексемы (включая 4+5;), а во втором — отбрасываем только лексему 2*3, оставляя лексему 4+5 для последующего вычисления. Один выбор является разумным, а второй может удивить пользователя. Обе альтернативы одинаково просто реализуются. Мы предпочли второй вариант, поскольку его проще протестировать. Он выглядит проще. Чтение лексем выполняется функцией get(), поэтому можно написать функцию clean_up_mess(), имеющую примерно такой вид:

void clean_up_mess() // наивно

{

  while (true) { // пропускаем,

                 // пока не обнаружим инструкцию "печать"

    Token t = ts.get();

    if (t.kind == print) return;

  }

}

К сожалению, эта функция не всегда работает хорошо. Почему? Рассмотрим следующий вариант:

1@z; 1+3;

Символ @ приводит нас к разделу catch в цикле while. Тогда для выявления следующей точки с запятой вызываем функцию clean_up_mess(). Функция clean_up_mess() вызывает функцию get() и считывает символ z. Это порождает следующую ошибку (поскольку символ z не является лексемой), и мы снова оказываемся в блоке catch внутри функции main() и выходим из программы. Ой! У нас теперь нет шансов вычислить лексему 1+3. Вернитесь к меловой доске!

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже