Описание пользователя
Глава 3. Использование стандартных модулей автокомпиляции
§3.8. Настройки стандартного модуля автокомпиляции
§3.8.10. Настройка обработки исключений и ошибок
Описывается настройка процедур и способов обработки ошибок, возникающих при работе модели. Эти процедуры могут включаться в формируемый текст программы по желанию пользователя.
В настройках модуля автокомпиляции можно указать, какими операторами можно перехватывать возникшие в модели исключения и как модель может перехватывать ошибки в математических функциях. Такой перехват ошибок включается индивидуально в настройках каждой модели, но, чтобы его вообще можно было включить, в настройках модуля должны быть заданы способы обработки исключений и ошибок. Чтобы настроить эти способы, следует вызвать окно настройки модуля и на его вкладке «» нажать кнопку «». В открывшемся дополнительном окне настроек следует выбрать вкладку «» (рис. 504).
Рис. 504. Настройка обработки исключений и ошибок (на примере Borland C++ 5.5)
В верхней части вкладки настраивается перехват исключений. Чтобы описать его, необходимо установить одноименный флажок и ввести в поля ввода «» и «» операторы начала блока обработки исключений (try) и начала блока реакции на исключение (catch) соответственно. Желательно ввести операторы для перехвата любого исключения, то есть самые универсальные из них.
Исключения (exceptions, исключительные ситуации) – это события, возникающие при каких-либо серьезных ошибках и прерывающие нормальный ход выполнения программы. Эти события часто создаются различными библиотечными функциями, разработчики которых, по каким-то причинам, решили сообщать об ошибках именно таким образом, а не возвратом какого-либо признака ошибки. Могут исключения возникать и при математических операциях, например, при делении на ноль.
Обработка исключений обычно устроена следующим образом: часть программы, в которой могут возникнуть исключения, помещают в блок обработки исключений, чаще всего описываемый оператором try. За ним размещают один или несколько блоков реакции на исключение, чаще всего описываемых оператором catch. При возникновении исключения внутри блока try управление немедленно передается ближайшему к нему блоку catch, предназначенному для обработки исключения соответствующего типа. Если такого блока не найдется в данной функции, управление будет передано выше. Если же его не будет во всей программе, ее выполнение будет прервано. Для моделей блоков в RDS это будет означать, что расчет остановится и RDS выдаст стандартное сообщение об ошибке. Более подробно обработка исключений описывается в учебниках по языку C++.
При включении флажка «» модуль автокомпиляции добавляет в текст формируемой программы описания трех констант:
// Обработка исключений включена #define RDSBCPP_EXCEPTIONS // Оператор try #define RDSBCPP_TRY текст_первого_поля // Оператор catch #define RDSBCPP_CATCHALL текст_второго_поля
Например, для настроек на рис. 504 будут добавлены такие описания:
// Обработка исключений включена #define RDSBCPP_EXCEPTIONS // Оператор try #define RDSBCPP_TRY __try // Оператор catch #define RDSBCPP_CATCHALL __except(EXCEPTION_EXECUTE_HANDLER)
В операторах перехвата исключений (catch) может указываться тип перехватываемого исключения. В данной настройке следует вводить наиболее универсальный обработчик – например, «catch(...)», или, как в примере, оператор «__except(EXCEPTION_EXECUTE_HANDLER)». Компиляторы поддерживают различные операторы обработки исключений, но введенный оператор перехвата должен соответствовать оператору начала блока обработки: для «try» нужно указывать «catch», для «__try» – «__except», и т.п. Способы обработки исключений и использующиеся для этого операторы приводятся в описании конкретного компилятора.
Добавленные константы используются следующим образом: наличие определения константы RDSBCPP_EXCEPTIONS говорит о том, что в параметрах модуля автокомпиляции указаны операторы обработки исключений (то есть флажок «» установлен), константа RDSBCPP_TRY может быть использована как оператор начала блока исключений, а константа RDSBCPP_CATCHALL – как оператор начала блока перехвата. Использование этих констант вместо конкретных операторов позволяет создавать модели, которые можно использовать с любым компилятором, если параметры модуля, работающего с этим компилятором, настроены правильно.
Рассмотрим простой пример. Допустим, в модели блока есть вещественные переменные x1, x2 и y, и в реакции на нажатие кнопки мыши мы хотим присвоить y результат деления x1 на x2. Простейший текст реакции будет выглядеть так:
y=x1/x2;
Однако, если x2 будет иметь нулевое значение, при делении возникнет исключение, расчет будет остановлен, и пользователь увидит сообщение, подобное изображенному на рис. 357. Можно перехватить это исключение следующим образом:
RDSBCPP_TRY
{ y=x1/x2; }
RDSBCPP_CATCHALL
{ rdsMessageBox("Деление на ноль","Ошибка",MB_OK|MB_ICONERROR);
y=rdsbcppHugeDouble;
}
Здесь мы заключили деление в блок обработки исключений, причем вместо конкретного оператора try использовали константу RDSBCPP_TRY – вместо нее компилятором будет подставлен оператор, указанный в настройках модуля. В оператор перехвата, записанного после константы RDSBCPP_CATCHALL, мы включили вывод сообщения об ошибке и присвоение переменной y значения-маркера ошибки. Расчет при этом остановлен не будет.
Приведенный пример, хотя и позволяет использовать для модели разные компиляторы, имеет один недостаток: если в настройках модуля не указан способ обработки исключений (флажок «» не установлен), константы RDSBCPP_TRY и RDSBCPP_CATCHALL будут определены таким образом, чтобы обработка исключений игнорировалась и блок перехвата никогда не выполнялся. Для этого RDSBCPP_TRY будет иметь пустое значение, а RDSBCPP_CATCHALL будет иметь значение «if(0)», то есть будет заменяться на никогда не выполняющийся оператор if. Модель при этом будет работать так, как будто никакой обработки исключений нет, однако, при компиляции «if(0)» будет все время выдаваться предупреждение «условие всегда ложно». Отключать это предупреждение в параметрах компилятора опасно, поскольку оно позволяет обнаружить потенциальные алгоритмические ошибки в программе. Лучше всего переписать приведенный пример с использованием константы RDSBCPP_EXCEPTIONS:
RDSBCPP_TRY
{ y=x1/x2; }
#ifdef RDSBCPP_EXCEPTIONS
RDSBCPP_CATCHALL
{ rdsMessageBox("Деление на ноль","Ошибка",MB_OK|MB_ICONERROR);
y=rdsbcppHugeDouble;
}
#endif
Здесь весь блок перехвата исключений заключен внутрь конструкции «#ifdef … #endif». Таким образом, если константа RDSBCPP_EXCEPTIONS будет определена, то есть если в настройках модуля описана обработка исключений, блок перехвата будет скомпилирован и перехватит возникшее исключение при работе модели. Если же константа будет отсутствовать, весь блок перехвата скомпилирован не будет и не выдаст никаких предупреждений.
Вторая часть вкладки «» служит для настройки перехвата ошибок математики. Эти ошибки возникают только в функциях математической библиотеки и могут обрабатываться отдельно. Их обработка позволяет не прерывать выполнение программы в случае ошибки, а подставлять вместо результата операции, которая не может быть выполнена, какое-либо значение – например, маркер ошибки rdsbcppHugeDouble. Следует помнить, что деление на ноль не может быть перехвачено таким образом, оно всегда создает исключение, обработка которого рассматривается выше. Обработка ошибок математики включается индивидуально в параметрах каждой модели флажком «» (см. рис. 356).
Флажок «» разрешает модулю автокомпиляции добавлять в текст формируемой программы функции для такого перехвата. В поле ввода «» вводится оператор, который может потребоваться для инициализации перехвата математических ошибок – он будет добавлен к процедуре инициализации глобальных переменных, место вставки которой указывается в настройках модуля на вкладке «». Следует отметить, что ни один из поддерживаемых модулями компиляторов не требует никаких специальных действий для инициализации обработки ошибок математики, поэтому это поле ввода в настройках модулей всегда пустое. Тем не менее, оно может оказаться полезным для какого-либо нестандартного компилятора.
В многострочное поле «» вводится функция перехвата математических ошибок в том виде, который требуется компилятору. Функция вводится полностью, вместе с заголовком и телом. Имя этой функции и ее параметры приводятся в описании конкретного компилятора. Для Borland C++, например, подходит такая функция:
int _matherr(struct _exception *a)
{ a->retval=(a->type==UNDERFLOW)?0.0:rdsbcppHugeDouble;
return 1;
}
Здесь анализируется возникшая ошибка и, в случае потери точности, результат математической операции считается нулем, а для вех остальных ошибок результатом будет маркер ошибки rdsbcppHugeDouble. Возврат функцией единицы указывает на то, что функция успешно обработала ошибку.
В поля ввода «» и «» вводятся команды, позволяющие, если в настройках модели разрешен перехват ошибок математики, временно блокировать специфические математические исключения (команды в поле «») на время работы модели, а затем разрешать их снова (команды в поле «»). Функции для управления математическими исключениями и их параметры описываются в руководстве по Windows API и в описаниях компиляторов. Команды, записанные в этих полях по умолчанию, в начале функции модели блокируют все исключения, а в конце – восстанавливают их прежнее состояние.