8
Хороший программный стиль
До сих пор я сосредотачивался на объяснении начальных основ программирования. Я думаю, настало время поговорить о стиле или говоря проще о том, как программа должна быть написана. Обучение программированию может быть несложным, но вскоре, когда начнете сами писать свою программу, вы можете столкнутся с плохим стилем написания собственного кода. Аккуратно отформатированный код, не только делает ему более профессиональный вид, но и помогает вам легко читать и понимать его. Далее я опишу способы удобного форматирования своего кода для максимального удобства чтения. Это не является обязательным, скорее предпочтительным. Надеюсь, что после прочтения моих советов, вы организуете свой собственный стиль и будете придерживаться его в дальнейшем. Кроме того я дам вам советы и приемы, которые помогут вам избежать ошибок в коде, и даже если они произойдут, с легкостью с ними справляться. Так же будут некоторые рекомендации по использованию отладчика PureBasic для отслеживания проблем, происходящие в вашем коде.
Почему следует беспокоиться об аккуратности форматирования кода?
Этот вопрос иногда задают начинающие. Один из возможных ответов например, если бы я писал эту книгу без заголовков, подзаголовков, пунктов и слил бы всю книгу в один большой блок, легко ли было бы читать, как сейчас? Аккуратно форматированный код не влияет на производительность программы и делается только для удобной читаемости. Вроде как зачем беспокоиться? Ну, я могу гарантировать, что в будущем вы будете работать над различными исходными кодами, которые не сможете закончить за один день. Вы вернетесь к нему через несколько дней, неделей или месяцев и уже не будете помнить полностью его. И вот теперь представьте себе если код будет уродливый, плохо читаемый. Вам придет в голову мысль создать лучше заново, чем читать подобную галиматью. И даже если вы создадите за один день свой код, где гарантия, что вы не захотите обновить его через некоторое время?
В вашей программе могут случаться ошибки и при плохо отформатированном программном коде появятся трудности с их обнаружением. Бывают случаи, когда вы сами не можете обнаружить ошибки и попросите помощи у других. Как другие смогут читать ваш сумбурный код? Работа в коллективе является еще одной веской причиной для написания хорошо отформатированного кода. Если вы работаете над проектом в команде, важно чтобы вся команда использовала один стандарт форматирования. Это сделает работу в коллективе максимально эффективной.
Ценность Комментариев
Первое, о чем стоит упомянуть для понимания хорошего стиля программирования, это использование комментариев. До сих пор, комментария не были использованы в моих примерах. Я решил подождать с ними до этой главы.Вы можете использовать их сколько хотите и они никогда не будут вмешиваться в выполнение программы. Комментарии могут дать подробное объяснение вашего кода. При том можно делать пояснения хоть каждой команды или действия . Вот короткий пример:
;The procedure Pi() is called to return the value of
Pi.
;This calculation is only accurate to six decimal places.
Procedure.f Pi()
ProcedureReturn 4 * (4 * ATan(1/5) - ATan(1/239))
EndProcedure
CircleDiameter.l = 150
Debug "A circle which has a diameter of " + Str(CircleDiameter) + "mm"
Debug "has a circumference of " + StrF(CircleDiameter * Pi()) + "mm."
Здесь я использовал свои комментарии, чтобы описать действия процедуры, а так же упомянуть об ограничении в шесть знаков после запятой для данного расчета. Комментарии всегда начинаются со знака ; Вот пример:
;This is a comment
Комментарии могут появиться абсолютно в любом месте вашего исходного кода, даже на одной строке с другой командой, вот так:
Procedure.l AddTogether(a.l,
b.l)
;Add two numbers
and return the result
ProcedureReturn a + b
EndProcedure
Вы должны помнить, что точка с запятой в начале определяет где начинается комментарий. Комментарий лучше делать кратким, но достаточно понятным. Если нужно сделать один комментарий на нескольких строчках, надо на каждой строчке перед записью ставить точку с запятой.
Комментарии могут быть использованы для:
1). Добавление лицензий или информации об авторских правах.
2). Объяснять, почему был использован конкретный подход.
3). Уведомления о том, где код может быть улучшен.
4). Уточнение функций сложных процедур.
5). Уточнение внутренности процедур , используя формулы.
Мой формат кодирования
Мой план - определить структуру плохого кода и подвести под стандартный формат кода других языков программирования. Вся эта книга была написана в таком стиле, который я лично считаю легко читаемым и понимаемым.
Переменные, Массивы, Связанные списки и Процедуры
Имена переменных, массивов, связанных списков и процедур я стараюсь писать в формате, который считаю более читабельным. Имя у меня состоит из строчных и прописных букв. Такое имя быстрее находится в тексте кода, да и более примечательно глазу. Пример:
NumberOfDays.l = 365
Dim MonthsOfTheYear.s(11)
NewList Days.s()
Константы
Для формата констант я использую стандартный C подобный и Windows API стиль, в которых все буквы прописные. Если мне нужно различать отдельные слова, заключенные в имени констант, я использую подчеркивание. Пример:
#SPEED_OF_LIGHT = 299792458 ; Meters per second
Структуры
Для имен структур я использую тот же формат, что и для констант. То есть буквы все прописные, и если надо отделять слова использую подчеркивание. Пример:
Structure USER_SERVICE
ServiceType.s
ServiceCode.l
EndStructure
Отступы
Отступы дают возможность увидеть структуру кода для процедур, циклов и условных операторов. Отступы очень часто используются для хорошего визуального восприятия кода. Ниже приведены примеры отступов:
; Returns a String containing a floating
point number that’s been rounded up.
; 'Number' = Number to round up and return as a String.
; 'DecimalPlaces' = Number of decimal places to round up to.
Procedure.s StrFRound(Number.f, DecimalPlaces.l)
Protected R.f
Protected T.f
If DecimalPlaces < 0
DecimalPlaces = 0
EndIf
R.f = 0.5 * Pow(10, -1 * DecimalPlaces)
T.f = Pow(10, DecimalPlaces)
ProcedureReturn StrF(Int((Number + R) * T) / T,
DecimalPlaces)
EndProcedure
Debug StrFRound(3.1415927, 4)
В этом примере вы могли видеть, как я с отступом написал код. Это четко определяет код между началом и окончанием команд процедур. А так же вы наверно заметили отступы с условным оператором IF_ENDIF. Также с отступами я применяю написание кода для циклов. Отступы особенно полезны с вложенными циклами:
For x = 0 To 2
For y = 0 To 2
z.l = 0
While z =< 10
Debug x * y + z
z + 1
Wend
Next y
Next x
В данном примере ясно видно границы каждого из циклов. Идет четкое разграничение каждого из циклов путем сдвига внутренностей каждого из них. Команда NEXT стоит четко под командой FOR. А команда WEND четко под командой WHILE.
Несколько команд в одной строке
Иногда я пишу несколько команд в одной строке, чтобы сделать некоторые исходные коды визуально меньше размерами . Для отделения команд используется двоеточие:
Dim Birds.s(3)
Birds(0)="Sparrow" : Birds(1)="Wren" : Birds(2)="Cuckoo" :
Birds(3)="Owl"
Некоторые программисты начинают хмуриться на такой род форматирования, поскольку считают что это может помешать читабельности. В чем то они правы, но все же бывают моменты, когда такое написание выглядит довольно читабельным.
Разбитие исходного кода на несколько файлов
Когда я пишу программу, как правило разбиваю его на несколько отдельных файлов исходного кода. Эти файлы являются стандартными PureBasic файлами с расширением .PB или .PBI файл IDE. Эти файлы всегда нетрудно включить в основной код программы с помощью команды XIncludeFile или IncludeFile. Например, если я определяю много процедур, я создаю отдельный исходный файл с именем Procedures.pbi , где будут все мои процедуры. Затем в верхней части моего основного исходного кода я записываю:
IncludeFile "Procedures.pbi"
Это влечет за собой принятие всех строк кода из файла Procedures.pbi и вставляет их в мой основной исходный код, начиная с этой линии. При использовании нескольких файлов, может случится так, что в них могут встречаться много раз оператор IncludeFile с одним и тем же именем. Будут лишние дубликаты, и чтобы этого не было, надо использовать команду XIncludeFile. Все как и раньше:
XIncludeFile "Procedures.pbi"
Команда XIncludeFile будет включать код из файла Procedures.pbi только если он не был включен ранее. Я редко использую команду IncludeFile. Я предпочитаю использовать XIncludeFile, так как это сократит количество возникающих ошибок.
Правильное окончание программы
Чтобы завершить программу правильно, вы должны использовать команду END. Это закрывает программу и освобождает всю память, используемую ею. Опциональный выход с использованием кода выхода так же возможен.
Без использования кода выхода:
End ; Immediately ends the program and frees all memory used by it.
С использованием кода:
End 1 ; Immediately ends the program, frees all memory and returns 1.
Золотые правила написания легко читаемого кода
Вот список золотых правил, которым следуют при написании программы. Я придерживаюсь этих правил, даже при написании очень маленьких программ. Если будете следовать этому списку, вы тоже сможете написать хороший и понятный код. Используя стандартный способ форматирования для ваших программ, дает четкую структуру для вашего кода и людям читающим его, легко будет дать вам совет или даже помочь с поиском ошибок.
1). Дайте всем переменным, процедурам, массивам, и т.д. точные,
удобопроизносимые, подробные имена.
2). Для группы переменных подключайте массивы или структуры.
3). Процедуры должны выполнить только одну функцию и выполнять ее
хорошо.
4). Используйте отступ, чтобы показать структуру кода.
5). Используйте круглые скобки в выражениях, чтобы избежать любого
беспорядка в значениях.
6). Используйте пустые строки, чтобы отделить различные процедуры и
другие блоки программы.
7). Попытайтесь не использовать команды ‘Goto’ или
‘Gosub’.
8). Используйте комментарии, чтобы помочь людям и вам лучше понимать
код.
9). Пытайтесь плохой код с комментариями, переписать должным образом.
10). Работая в группе, согласуйте стиль форматирования перед стартом,
затем придерживайтесь его.
Как свести к минимуму появление ошибок
В этой главе я хочу поговорить о методах, которые помогут вам найти ошибки в коде. Даже самые опытные и преданные делу программисты делают ошибки. Здесь я покажу хорошую практику работы наряду с некоторыми ценными советами и рекомендациями. Это позволит вам быть более бдительными в программировании, сводя к минимуму вероятность каких-либо проблем.
Используйте процедуру-обработчик ошибок
Когда будете писать большие проекты, вы поймете, что будет слишком много тестов для подтверждения значений. Это связано с тем, что много команд возвращает значение. Эта величина почти всегда целое число и всегда выше 0 . Некоторые программисты используют логический оператор IF для этих целей. Я нахожу этот подход правильным только для маленьких программ. Поскольку в большом коде использование множества IF-ENDIF может привести к путанице. Гораздо лучше использовать процедуру обработки ошибок. Это не только делает ваш исходный код более легким для чтения, но так же сообщает пользователю о всех неисправностях в программе. А теперь я представляю вашему вниманию два исходных кода без использования процедуры-обработчика и с использованием:
#TEXTFILE = 1
If ReadFile(#TEXTFILE, "TextFile.txt")
Debug ReadString(#TEXTFILE)
CloseFile(#TEXTFILE)
Else
MessageRequester("Error", "Could not open the file:
'TextFile.txt'.")
EndIf
Здесь тестируется чтение файла TextFile.txt. Хотя этот метод хорош для небольших программ, я предпочитаю другой:
#TEXTFILE = 1
Procedure HandleError(Result.l, Text.s)
If Result = 0
MessageRequester("Error", Text,
#PB_MessageRequester_Ok)
End
EndIf
EndProcedure
HandleError(ReadFile(#TEXTFILE,"TextFile.txt"),"Couldn’t open:
'TextFile.txt'.")
Debug ReadString(#TEXTFILE)
CloseFile(#TEXTFILE)
Здесь я использовал процедуру под названием HandleError() для тестирования возвращенного значения любых команд, переданных ей. Первый параметр называется Result, куда посылается возвращенное значение из команд. Второй параметр Text.s это строка,которую вы желаете для отображения ошибки при параметре равном нулю. Процедура получит в качестве параметров два значения, которые передаст ей этот код:
ReadFile(#TEXTFILE, "TextFile.txt")
Если файл TextFile.txt не существует на вашем жестком диске или что то идет не так, команда ReadFile() вернет 0. Это значение передается первым параметром процедуре обработки ошибок. Внутри процедуры этот параметр проверяется, если его значение равно 0, будет вызов сообщения и программа завершится. Это очень удобно, выдавать четкие и краткие сообщения об ошибках для каждой команды, которая вызывает ошибку. Если же команда возвращает значение выше 0, то процедура не предпримет никаких действий. Использование процедуры обработки ошибок становится действительно полезным в крупных программах, где много команд должны быть проверены одна за другой. После того как процедура определена, вы можете использовать ее для тестирования многих команд и каждая из них будет испытана в одной части кода, в отличие от использования способа с оператором IF. Взгляните на этот пример тестирования нескольких команд:
HandleError(InitEngine3D(), "InitEngine3D()
failed to initialized!")
HandleError(InitSprite(), "InitSprite() failed to initialized!")
HandleError(InitKeyboard(), "InitKeyboard() failed to initialized!")
HandleError(OpenScreen(1024, 768, 32, "Game"), "A screen could not be
opened!")
А теперь представьте себе сколько кода бы понадобилось для проверки этих четырех команд с использованием оператора IF. Данный пример позволяет легче обнаружить ошибки и неожиданные проблемы. Единственная проблема такого подхода является то, что HandleError() появляется в начале каждой строки, и это некоторые люди считают навязчивым.
Использование команды EnableExplicit
Эта команда является находкой для некоторых людей и помехой для других, и поэтому она не является обязательной для использования. Данная команда позволяет явное определение переменных в течение всей программы. Так что же это значит? В главе 2, я объяснил, что если вы не указали тип суффикса для переменной, то это тип переменной по умолчанию имеет тип LONG. Команда EnableExplicit меняет это правило и требует, чтобы все новые переменные были определены. Позвольте мне показать вам, как эта команда может оказаться полезной. Скажем, например, у меня есть процедура, в которую необходимо передать значение переменной и получить результат вычислений:
WeeklyAmount.l = 1024
Procedure CalculateYearlyAmount(Value.l)
YearlyAmount.l = Value * 52
ProcedureReturn YearlyAmount
EndProcedure
Debug CalculateYearlyAmount(WeaklyAmount)
Вроде как выглядит нормально, и если вы быстро просмотрите этот пример, вы не увидите никаких проблем. Но запустив пример увидите, что процедура CalculateYearlyAmount() возвращает 0. Такого не должно быть, скажете вы! Ведь процедуре передается переменная WeeklyAmount, которая имеет значение 1024. Если присмотреться внимательно на вызов процедуры можно заметить, что на самом деле в качестве параметра передается переменная WeaklyAmount. Обратите внимание, что имя переменной другое, и поэтому она рассматривается в качестве новой переменной. В PureBasic если переменная не была объявлена, создается новая переменная автоматически и в нашем примере такая переменная передается в качестве параметра. Значение такой переменной равно нулю. Отсюда вычисление равно нулю и возвращает процедура тоже нуль.
Автоматическое определение переменных, по мнению некоторых программистов очень подвержено ошибкам, поскольку сказывается человеческий фактор. Это можно изменить с помощью команды EnableExplicit. Если бы мы использовали эту команду в приведенном выше примере, мы получили бы сообщение компилятора о том, что переменная должна быть объявлена.Это означает, что все переменные, используемые после команды EnableExplicit должны быть определены с использованием любой из команд для определения переменных: ‘Define’, ‘Global’, ‘Protected’, ‘Static’ и ‘Shared’ С помощью этой команды выявилась ошибка в написании имени переменной. Пример с использованием команды EnableExplicit. В нем все переменные определены:
EnableExplicit
Define WeeklyAmount.l = 1024
Procedure CalculateYearlyAmount(Value.l)
Protected YearlyAmount.l = Value * 52
ProcedureReturn YearlyAmount
EndProcedure
Debug CalculateYearlyAmount(WeeklyAmount)
С помощью этой команды можно писать код не боясь делать орфографические ошибки, потому что каждый раз, когда появится орфографическая ошибка, компилятор будет рассматривать имя как отдельную переменную. И вам будет предложено определить эту переменную, давая вам шанс исправить переменную, тем самым восстанавливая любые значения, которые могут быть утрачены за счет этих ошибок. В любое время, если вы хотите вернуть правила объявления переменных по умолчанию, вы можете выключить команду EnableExplicit. Это делается командой DisableExplicit.
Определение переменных, используя команду DEFINE
Команда DEFINE может быть использована двумя способами. Первый это определение типа по умолчанию:
Define.s
MyString = "Hello"
Второй с использованием команды EnableExplicit. Если команда EnableExplicit была использована, все переменные с тех пор в вашей программе, должны быть строго определены. В этом случае, команда DEFINE помогает вам в этом:
EnableExplicit
Define MyVar.b = 10
Обратите внимание, что при использовании таким образом, вы не должны использовать тип суффиксов в конце команды DEFINE, потому что мы определяем тип каждой переменной с использованием собственного суффикса.
Представление Отладчика PureBasic
PureBasic предоставляет отладчик, который помогает найти ошибки в коде. Это отладчик неоценим, так как он дает вам возможность программного управления потоком, а так же оценивать значения переменных, массивов и связанных списков в любое время в течение выполнения своей программы. Он также предоставляет расширенные функции для программистов, например чтобы изучить и изменить регистры процессора или просмотр значений, хранящихся в смежных адресах памяти. Также можно просмотреть загрузку процессора вашей программы, используя встроенный монитор процессора. Если вы запустите программу и отладчик сталкивается с ошибкой, программа будет остановлена и строка, где произошла ошибка будет отмечаться в IDE (красной линией), и ошибка будет отображаться в журнале и в строке состояния IDE. Когда обнаружена ошибка, вы можете использовать функции программного управления или завершения запущенной программы. Для завершения запущенной программы используют команду Kill Program, которая находится в меню : Debugger/ Kill Program, или связанную с ней кнопку на панели инструментов. Если отладчик отключен, то ошибки не будут пойманы и могут привести к краху программы.
Во время написания программы в IDE, отладчик включен по умолчанию. Это вы можете увидеть, взглянув на кнопочку переключения Debugger Enable/Disable Toggle на панели инструментов IDE, см. Рис.ниже. Если эта кнопка отображается нажатой значит отладчик включен. Так же это можно увидеть(Menu:Debugger->Use Debugger). Отладчик также может быть включен в опции компилятора для вашей программы (Menu:Compiler->Compiler Options...->Compiler Options->Enable Debugger). Все эти различные способы переключения состояния отладчика связаны. Если используется одно, другие зеркала его статус. Пакет PureBasic для Windows поставляется с тремя различными видами отладчика. Они могут быть использованы для отладки программы, но некоторые имеют не одинаковую функциональность. Во-первых, встроенный отладчик является наиболее богатым в плане функций и используется по умолчанию, он встраивается непосредственно в IDE. PureBasic IDE работая на некоторых операционных системах, не поддерживает эту встроенную версию, но поддерживает другую автономного отладчика, он также поставляется с установкой. Эта автономная версия имеет почти такой же набор возможностей. Третий отладчик запускается в консольной версии. Первичное использование этой версии является для не-графических сред. Оно основано для операционных систем Linux или удаленно разработанных приложений, работающие с клиентами с помощью SSH протокола. Доступные отладчики могут быть выбраны по умолчанию в настройках IDE, (Menu:File->Preferences->Debugger).
Несмотря на то, отладчик это отличный инструмент для отслеживания проблем, отладка понижает функциональность программы. Вы увидите, что программы, запущенные под отладчиком работают медленнее, чем при запуске их без него. Некоторым программам необходима максимальная скорость, которую можно развить только в окончательных приложениях (EXE). Вы всегда можете отключить отладчик, используя кнопку DisableDebugger, а потом включить при необходимости кнопкой EnableDebugger. Все таки вы должны помнить, что если вы отключите отладчик, будут отключены и все команды DEBUG, это потому, что команда DEBUG является частью отладчика.
Использование отладчика
Функции отладчика могут быть использованы в любое время, пока ваша программа выполняется. Они могут быть доступны из меню отладчика, а также с помощью соответствующих кнопок. Журнал ошибок (Menu:Debugger->Error Log) или монитор процессора (Menu:Debugger->CPU Monitor) всегда доступны. Пока вы используете отладчик, все файлы исходного кода, которые подключены к запущенной программе немедленно включены только для чтения, пока программа не завершилась. Это гарантирует, что используемый в настоящее время код не будет изменен. Обзор отладчика я собираюсь начать с объяснения программы управления функциями, которые он обеспечивает. Эти элементы управления позволяют остановить программу в любой момент времени и изучить значения любой переменной, массива или связанного списка .Состояние любой программы при использовании отладчика будет показан в строке состояния IDE и в журнале ошибок. Обзор отладчика с кнопками на панели инструментов приведен ниже на рис.
Чтобы остановить программу в любое время с использованием отладчика, вы можете использовать команду CallDebugger в вашем коде. Или используйте Stop в меню отладчика (Menu:Debugger->Stop) или нажмите кнопку Stop на панели тулбара во время выполнения программы. Вы можете также использовать точки останова(Брекпоинты), чтобы остановить исполнение программы. Для использования брекпоинтов в IDE, поместите курсор на строку, в которой вы хотели бы сделать остановку программы для дальнейшего управления с отладчиком. Затем выберите в меню команду Breakpoint (Menu:Debugger->Breakpoint). Вы заметите, что строка поменяла цвет в IDE. Это визуальная индикация для того, чтобы показать, где определены точки останова. Когда программа дойдет до этого места, она остановится и будут доступны все функции управления отладчика. Далее вы пошагово можете изучать выполнение программы. Вот краткое описание программы контрольных функций:
STOP Эта команда останавливает программу и отображает текущую строку.
CONTINUE Продолжает выполнение программы, пока не встретит другую точку остановки программы
STEP Выполняется одна строка исходного кода, и останавливает выполнение.
Kill Program Полностью завершает работу программы
Вот короткий пример остановки выполнения программы командой CallDebugger
CallDebugger
For x.l = 1 To 10
Debug x
Next x
End
После запуска программа была приостановлена командой CallDebugger. Вы можете перемещаться по ней, используя кнопку STEP на панели инструментов. Нажмите на нее десять раз, и вы увидите различные значения X в окне вывода отладки, они будут увеличиваться на единицу при каждом прохождении цикла. Программа будет остановлена и вы можете просматривать значения любой переменной, массива или связанного списка , открыв Variable Viewer (Menu:Debugger->Variable Viewer). Этот небольшой обзор должен был научить вас основам работы с отладчиком. Для более продвинутой информации о том, на что он способен, смотрите справку PureBasic.
OnError Library
Внимание! От версии к версии, меняется набор команд в PureBasic. Так и с библиотекой OnError. Все нижеописанные коды, будут безошибочно работать в версии компилятора 4.00 Но в более поздних версиях некоторые команды, потеряли свой функционал, а некоторые попросту исключили. Но это не значит, что не стоит изучать этот раздел.
Встроенная OnError Library позволяет выполнять проверку ошибок в окончательном исполняемом файле при отключенном отладчике. Обычно вы будете использовать отладчик при разработке для поиска ошибок, но когда вы запланируете скомпилировать исполняемый файл, отключения отладчика принесет вам выигрыш по скорости примерно в шесть раз. Есть много очень сложных команд, которые могут быть использованы с этой библиотекой, но это выходит за рамки этой книги, так что я просто расскажу про наиболее часто используемые и легкие для понимания. Во-первых, я покажу вам, как эффективно поймать ошибку, не используя отладчик. Взгляните на следующий пример:
;Set the error handler
OnErrorGoto(?ErrorHandler)
;Trigger a classic 'divide by zero' error.
Null.l = 0
TestVariable.l = 100 / Null
;Handle any system error that occurs
ErrorHandler:
Text.s + "Error ID number:" + #TAB$ + #TAB$ + Str(GetErrorNumber()) +
#CRLF$
Text.s + "Error description:" + #TAB$ + #TAB$ + GetErrorDescription() +
#CRLF$
Text.s + "Error occurred on line:" + #TAB$ + Str(GetErrorLineNR()) +
#CRLF$
Text.s + "Error occurred in module:" + #TAB$ + GetErrorModuleName() +
#CRLF$
MessageRequester("ERROR", Text)
End
Здесь я использую команду OnErrorGoto с указанием места, куда нужно будет перейти, если произойдет ошибка. Параметр этой команды МЕТКА , куда нужно прыгать. Если вы внимательно посмотрите на параметр, вы заметите, что я приложил вопросительный знак перед именем. Это необходимо команде OnErrorGoto(). Использование знака вопроса, возвращает адрес памяти метки, а не саму метку. В главе 13 даются более подробные объяснения указателей адресов памяти. Подобно использованию команд Gosub или GOTO, мы создаем метку в нашем коде на которую ссылается команда OnErrorGoto и в конец имени ставим двоеточие чтобы дать понять компилятору, что это метка ErrorHandler: . В приведенном выше примере я использовал эти команды:
GetErrorNumber() Эта команда возвращает уникальный номер последней ошибки.
GetErrorDescription() Эта команда возвращает строку с полным описанием произошедшей ошибки.
GetErrorLineNR() Эта команда возвращает номер строки, где произошла ошибка в исходном коде, либо в исходном файле или во включенных файлах. Для правильной работы этой команды, необходимо включить опцию компилятора (Enable OnError Lines Support) перед компиляцией программы.
GetErrorModuleName() Эта команда возвращает строку, говорящую вам адрес исходного файла, в котором находится код с произошедшей ошибкой. Это очень полезно, если вы используете много включенных файлов для исходного кода. Для правильной работы этой команды необходимо включить опцию компилятора (Enable OnError Lines Support) перед компиляцией программы. Это находится в настройках компилятора IDE, (Menu:Compiler->Compiler Options...->Compiler Options->Enable OnError Lines Support).
Чтобы показать работу OnError я специально создал ошибки в коде. В данном случае я написал код с ошибкой (деление на ноль):
;Trigger a classic 'divide by zero'
error.
Null.l = 0
TestVariable.l = 100 / Null
Если отладчик включен, то он показал бы конечно ошибку, но можно и без него поймать ошибку. Мы можем использовать библиотеку OnError для этого. Чтобы запустить пример и увидеть ее нормальную работу, вам необходимо сначала отключить отладчик и в настройках компилятора указать поддержку OnError . Теперь при компиляции программы, она должна поймать ошибку (деление на ноль) и в сообщении вывести подробный отчет. На рисунке ниже примерный вариант того, что должен отобразить компилятор, после компиляции нашего примера.
В последнем примере я использовал команду OnErrorGoto() для перехода на заданную метку в случае ошибки. Эта команда, однако, не позволяет сделать возврат и продолжить программу, даже если вы используете команду Return. Более удобно для таких целей использовать команду OnErrorGosub(). С помощью этой команды вы можете указать метку или процедуру, как обработчик ошибок и сделать возврат в программу для дальнейшего продолжения. Вот пример использования:
;Handle any system error that occurs
Procedure ErrorHandler()
Text.s + "Error ID number:" + #TAB$ + Str(GetErrorNumber()) +
#CRLF$
Text.s + "Error description:" + #TAB$ + GetErrorDescription() +
#CRLF$
Text.s + "Occurred on line:" + #TAB$ + Str(GetErrorLineNR()) +
#CRLF$
Text.s + "Occurred in module:" + #TAB$ + GetErrorModuleName() +
#CRLF$+#CRLF$
Text.s + "Would you like to continue execution of the program?"
ReturnValue.l = MessageRequester("ERROR", Text,
#PB_MessageRequester_YesNo)
If ReturnValue = #PB_MessageRequester_No
End
EndIf
EndProcedure
;Set the error handler
OnErrorGosub(@ErrorHandler())
;Trigger a classic 'divide by zero' error.
Null.l = 0
TestVariable.l = 100 / Null
;If the program reaches here then the program was resumed
MessageRequester("STATUS", "The program was allowed to continue
running.")
End
Здесь я продемонстрировал, как возобновить выполнение программы после сообщения об ошибке. То есть то, что вы не можете делать, используя команду OnErrorGoto() . Существует одна проблема с этим подходом. Если вы решите продолжить выполнение программы после произошедшей ошибки, есть риск сделать эту программу нестабильной, что может привести к зависанию и пр. Поэтому, если вы даете вашей программе возможность возобновиться после сообщения об ошибке, вне зависимости от серьезности ошибки, вы должны сообщить пользователю, что лучше перезапустить программу. Возобновление выполнения программы в идеале может быть использовано, для сохранений данных перед перезапуском программы. Скажем, например, вы написали текстовый редактор и Боб использует его для написания письма. Через час после написания, необъяснимая ошибка, и программа выводит сообщение об ошибке. Но Боб не хочет потерять свое письмо из-за ошибки. В этом случае, Боб может быть проинформирован о случившемся и будет возможность возобновить выполнение программы, чтобы сохранить его письмо в файл до перезагрузки программы.
Эти обе команды очень похожи. Есть только одна разница: OnErrorGoto() завершает программу после ошибки, а OnErrorGosub() позволяет продолжить.
Ловим ошибки, создаваемые пользователем
Пока что я показал вам способ ловли и документирования системных ошибок, которые могут возникнуть в вашей программе. Этот способ будет ловить практически любую серьезную ошибку. Однако вы можете использовать в вашем коде обработчик пользовательских ошибок с помощью OnError library . Существует команда SetErrorNumber(), которой требуется всего один параметр для правильной работы. Я обычно использую константы, определяющие пользовательские ошибки:
#ERROR_READFILE = 1
#ERROR_WRITEFILE = 2
#FILE_TEXT = 1
;Set the error handler
OnErrorGoto(?ErrorHandler)
If ReadFile(#FILE_TEXT, "Report.txt") = #False
;If the file read fails then fire an error
SetErrorNumber(#ERROR_READFILE)
EndIf
End
;Handle any custom error that occurs
ErrorHandler:
Text.s + "Error ID number:" + #TAB$ + #TAB$ + Str(GetErrorNumber()) +
#CRLF$
Select GetErrorNumber()
Case #ERROR_READFILE
Description.s = "The file could not be read."
Case #ERROR_WRITEFILE
Description.s = "The file could not be written."
EndSelect
Text.s + "Error description:" + #TAB$ + #TAB$ + Description + #CRLF$
Text.s + "Error occurred on line:" + #TAB$ + Str(GetErrorLineNR()) +
#CRLF$
Text.s + "Error occurred in module:" + #TAB$ + GetErrorModuleName() +
#CRLF$
MessageRequester("ERROR", Text)
End
В приведенном выше примере, если файл Report.txt нельзя прочитать с помощью команды ReadFile() я подключаю команду SetErrorNumber(), которая вызывает код обработки пользовательской ошибки. Эта ошибка определяется константой #ERROR_READFILE которая имеет значение 1. Когда ошибка срабатывает, вызывается метка обработчика ошибок или процедура. В обработчике ошибок с помощью команды GetErrorNumber() отлавливается параметр ошибки, переданный командой SetErrorNumber(). Текстовые результаты, вы можете выводить те, которые будут уместны в каждом конкретном случае. Как видно из этих примеров, используя OnError library можно сделать неплохой обработчик ошибок, с использованием очень простого синтаксиса.
GUI
В следующем разделе я собираюсь поговорить о графическом пользовательском интерфейсе(GUI) и как он создается в PureBasic. Почти все современные операционные системы имеют GUI, что позволяет пользователю гибко взаимодействовать с программами, которые предпочитают использовать его. Операционная система предоставляет пользователю интерфейс для программы через интерфейс прикладного программирования (или API), благодаря которому программа может сообщить операционной системе, как оформить свой пользовательский интерфейс. Это звучит очень сложно, но это просто и изящно делается в PureBasic с помощью различных библиотек (Window, Menu, а так же Gadgets). Данный раздел начинается с объяснения и демонстрации программ, которые используют консоль, так как их пользовательский интерфейс более понятен. Позднее я перейду к объяснению, как создавать программы с меню и графикой. В последнем разделе я дам вам обзор визуального конструктора PureBasic. С помощью этого инструмента можно разрабатывать интерфейс визуально. После прочтения этого раздела вы поймете как создать графический пользовательский интерфейс для ваших программ а так же как он взаимодействует с пользователем.