- Получение общего списка свойств терминала и программы
- Номер сборки терминала
- Тип и лицензия программы
- Режимы работы терминала и программы
- Разрешения
- Проверка сетевых подключений
- Вычислительные ресурсы: память, диск, процессор
- Характеристики экрана
- Строковые свойства терминала и программы
- Настраиваемые свойства: лимит баров и язык интерфейса
- Привязка программы к свойствам среды исполнения
- Проверка состояния клавиатуры
- Проверка статуса и причины остановки MQL-программы
- Программное закрытие терминала и код возврата
- Обработка ошибок времени исполнения программы
- Пользовательские ошибки
- Управление отладкой
- Предопределенные переменные
- Предопределенные константы языка MQL5
Пользовательские ошибки
Разработчик может использовать встроенную переменную _LastError в собственных, прикладных целях. Это позволяет сделать функция SetUserError.
void SetUserError(ushort user_error)
Функция устанавливает встроенную переменную _LastError в значение ERR_USER_ERROR_FIRST + user_error, где ERR_USER_ERROR_FIRST равно 65536. Все коды ниже этой величины зарезервированы за системными ошибками.
С помощью данного механизма можно отчасти обойти ограничение MQL5, связанное с тем, что в языке не поддерживаются исключения.
Довольно часто функции используют возвращаемое значение как признак ошибки. Однако бывают алгоритмы, в которых функция должна возвращать величину прикладного типа. Допустим речь о double. Если функция имеет область определения от минус до плюс бесконечности, любое значение, выбранное нами для индикации ошибки (например, 0), будет невозможно отличить от реального результата вычисления. В случае double, конечно, есть вариант вернуть специально сконструированное значение NaN (Not a Number, см. раздел Проверка вещественных чисел на нормальность). Но что если функция возвращает структуру или объект класса? Одно из решений — возвращать результат через параметр по ссылке или указателю, но такая форма делает невозможным использование функций в качестве операндов выражений.
В контексте разговора о классах уместно вспомнить об особых функциях — конструкторах. Они обязаны вернуть новый экземпляр объекта. Однако иногда обстоятельства мешают сконструировать объект полностью, и тогда вызывающий код вроде бы и получает объект, но не должен им пользоваться. Хорошо, если в классе можно предусмотреть дополнительный метод, который позволил бы проверить полноценность объекта. Но в качестве единообразного альтернативного подхода (например, охватывающего все классы) можно задействовать SetUserError.
В разделе Перегрузка операторов мы познакомились с классом Matrix. Дополним его методами расчета определителя и обратной матрицы, а затем используем для демонстрации пользовательских ошибок (см. файл Matrix.mqh). Напомним, что для матриц были определены перегруженные операторы, позволяющие составлять их в цепочки операторов, в едином выражении, и потому внедрять в него проверку на потенциальные ошибки было бы неудобно.
Наш класс Matrix является собственной альтернативной реализацией для недавно добавленного в MQL5 встроенного объектного типа matrix.
Прежде всего, в конструкторах основного класса Matrix сделаем проверку на корректность входных параметров. Если кто-то попытается создать матрицу нулевого размера, установим пользовательскую ошибку ERR_USER_MATRIX_EMPTY (одну из нескольких предусмотренных).
enum ENUM_ERR_USER_MATRIX
|
Вышеупомянутые новые операции определены только для квадратных матриц, поэтому создадим производный класс с соответствующим ограничением на размеры.
class MatrixSquare : public Matrix
|
Второй параметр в конструкторе, по логике вещей, должен отсутствовать (подразумевается равным первому), но он нам потребовался, потому что в классе Matrix появился шаблонный метод для транспонирования, в котором все типы T должны поддерживать конструктор с двумя целочисленными параметрами.
class Matrix
|
Из-за того, что параметров в конструкторе MatrixSquare два, нам также приходится проверять их на обязательное равенство, и если оно нарушено, устанавливаем ошибку ERR_USER_MATRIX_NOT_SQUARE.
Наконец, в ходе вычисления обратной матрицы мы можем обнаружить, что матрица вырождена (определитель равен 0). На этот случай зарезервирована ошибка ERR_USER_MATRIX_SINGULAR.
class MatrixSquare : public Matrix
|
Для наглядного вывода ошибок в журнал добавлен статический метод, возвращающий перечисление ENUM_ERR_USER_MATRIX, которое легко передать в EnumToString:
static ENUM_ERR_USER_MATRIX lastError()
|
С полным кодом всех методов можно ознакомиться в прилагаемом файле.
Проверку кодов прикладных ошибок проведем в тестовом скрипте EnvError.mq5.
Сначала убедимся в работоспособности класса: инвертируем матрицу и проверим, что произведение исходной и инвертированной дает единичную матрицу.
void OnStart()
|
Этот фрагмент кода генерирует следующие записи в журнале.
Test matrix inversion (should pass)
|
Обратите внимание, что в единичной матрице из-за погрешностей вычислений с плавающей точкой некоторые нулевые элементы на самом деле представляют собой очень малые, близкие к нулю величины и потому имеют знаки.
Затем посмотрим, как алгоритм справится с вырожденной матрицей.
Print("Test matrix inversion (should fail)");
|
Результаты представлены ниже.
Test matrix inversion (should fail)
|
В данном случае мы просто выводим описание ошибки, но в реальной программе следует предусмотреть выбор варианта продолжения, в зависимости от сути проблемы.
Наконец, смоделируем ситуации для двух оставшихся прикладных ошибок.
Print("Empty matrix creation");
|
Здесь мы описываем пустую матрицу и якобы квадратную матрицу, но с отличающимися размерами.
Empty matrix creation
|
В этих случаях мы не можем не создать объект, поскольку компилятор делает это автоматически.
Конечно, в данном тесте очевидны нарушения контрактов (спецификаций того, что классы и методы "считают" корректными данными и действиями). Однако на практике аргументы часто получаются из других частей кода, в ходе обработки объемных, "чужих" данных, и обнаружить отклонения от ожиданий — не такая уж тривиальная задача.
Способность программы "переваривать" некорректные данные без фатальных последствий является важнейшим показателем её качества, наравне с выдачей правильных результатов для правильных входных данных.