С появлением в языке DAQ PASCAL включаемых по директиве {$I FILE.INC} файлов стало понятно, что появилась хорошая возможность сократить код прикладных программ и повысить его стабильность за счет использования стандартных библиотек включаемых файлов. Поэтому была создана стандартная библиотека включаемых файлов для прикладных программ DAQ PASCAL. Есть шаблон программы, поясняющий, как правильно ее использовать.
Предполагается, что постепенно большинство прикладных программ будут создаваться на основе этой библиотеки путем включения в программу нужных библиотек и модулей. Это резко (в 2-3 раза) сократит объем прикладного кода, а также понизит вероятность ошибок в прикладных программах за счет отделения стандартного, хорошо провереного кода от прикладного. Здесь описана структура этой библиотеки.
Начнем с того, что библиотека основана на концепции модуля - условной программной единицы, со строго оговоренным порядком подключания и использования в прикладной программе. Модули unit, как в Delphi, реализуют идею раздельно компилируемых модулей на уровне компилятора. Включаемые файлы, к сожалению, не могут в полной мере реализовать такие же возможности. Однако с их помощью можно смоделировать модульную структуру программ, если дополнить включаемые файлы набором жестких соглашений. Именно так построена описываемая библиотека.
Программа DAQ PASCAL имеет жесткую структуру program: сначала идут описания констант (const), типов (type), переменных (var), процедур (procedure) и функций (function), затем тело основной программы:
program demo;
const ...
type ...
var ...
procedure ...
function ...
procedure ...
function ...
begin
...
end.
Эта жесткая структура несколько затрудняет использование включаемых файлов в качестве модулей. Ведь получается, что расположить код модуля в одном месте нельзя: он "расползается" как минимум на три части - описание констант, переменных и функций. Кроме того, используемые переменные и используемые ресурсы бывает необходимо инициализировать, а при завершении работы - освобождать.
Поэтому были введены такие правила для стандартных модулей:
Эти файлы должны присутствовать, даже если они пустые.
Это очень редко используемая возможность, поэтому она считается необязательной.
При описании переменных лучше использовать неявное описание типов,
это позволит избежать необоснованного включения файла с описанием типов.
Например: Monthes : array[1..12] of String;
Пример:
program Demos;
const
{$I _con_StdDemos1}
{$I _con_StdDemos2}
{$I _con_StdDemos3}
...
type
{$I _typ_StdDemos1}
...
var
{$I _var_StdDemos1}
{$I _var_StdDemos2}
{$I _var_StdDemos3}
...
{$I _fun_StdDemos1}
{$I _fun_StdDemos2}
{$I _fun_StdDemos3}
...
begin
if Starting then begin
InitStdLibrary;
ClearStrings;
InitStdDemos1;
InitStdDemos2;
InitStdDemos3;
...
if CheckStdErrors
then Success('Start Ok.')
else Trouble('Start Fails.');
end else
if Stopping then begin
...
FreeStdDemos3;
FreeStdDemos2;
FreeStdDemos1;
ClearStrings;
FreeStdLibrary;
end else
if Ok then begin
while StdIn_Readln(StdIn_Line) do StdIn_Process(StdIn_Line);
PollStdLibrary;
PollStdDemos1;
PollStdDemos2;
PollStdDemos3;
...
end;
end.
Смотрите также шаблон программы, в котором включены все имеющиеся модули.
Можно считать модули Errors, Strings, Console, Devices, Tags практически обязательными, так как они будут использоваться в подавляющем большинстве программ. Остальные модули используются по необходимости. Надо понимать, что в будущем возможно создание новых "супермодулей", которые будут включать внутри в себя эти стандартные модули, поэтому для них повторное включение имеющихся модулей не потребуется.
В настоящее время рекомендуется использовать супермодуль StdLibrary, который инкапсулирует (включает) в себя всю стандартную библиотеку. В большинстве случаев производительности и памяти хватает, поэтому нет смысла экономить - лучше всегда иметь всю библиотеку под рукой. К тому же это облегчит техническое сопровождение - не надо будет отслеживать длинный список отдельных модулей.
Пользовательским модулем будем называть программную единицу (допустим, Unit), построенную по определенным, здесь описанным правилам, состоящую из трех или четырех включаемых файлов - допустим, typ_Unit.inc, con_Unit.inc, var_Unit.inc, fun_Unit.inc, содержащих описание типов, констант, переменных и функций, соответственно. Файл типов может отсутствовать. Файлы описания констант, переменных и функций обязаны присутсвовать, даже если они пусты. Содержимое файлов с типами, переменными и константами явно не оговаривается - все остается на усмотрение прикладного программиста. Можно рекомендовать лишь, что описание переменных массивов и записей рекомендуется делать с неявным описанием типов при объявлении, когда это возможно по условиям задачи. Это простое правило позволит в большинстве случаев избежать введения файла описания типов.
Однако к файлу описания функций предъявляются более строгие требования. В этом файле обязаны быть четыре
процедуры (даже если они пустые) с именами ClearUnit, InitUnit, FreeUnit, PollUnit.
Процедура ClearUnit должна содержать очистку всех объявленных в модуле строк, а при необходимости и других переменных.
Она должна вызываться в процедуре ClearStrings, между вызовом ClearStdLibrary и CheckStringLeaks,
см. шаблон template.pas.
При наличии нескольких модулей методы Clear вызываются в порядке их включения.
Процедура InitUnit должна содержать начальную инициализацию всех используемых в модуле переменных, тегов,
кривых, устройств и другие действия, выполняемые в момент старта измерительной системы.
Она должна вызываться в блоке Starting, после вызова процедуры ClearStrings,
см. шаблон template.pas.
При наличии нескольких модулей методы Init вызываются в порядке их включения.
Процедура FreeUnit должна содержать код завершения и очистки всех используемых в модуле переменных, тегов,
кривых, устройств и другие действия, выполняемые в момент остановки измерительной системы.
Она должна вызываться в блоке Stopping, перед вызовом процедуры ClearStrings,
см. шаблон template.pas.
При наличии нескольких модулей методы Free вызываются в порядке, обратном порядку их включения.
Это нужно для того, чтобы методы Init\Free вызывались в стековом порядке.
Процедура PollUnit должна содержать код обработки данных в цикле опроса измерительной системы.
Она должна вызываться в блоке Polling, после вызова процедуры PollStdLibrary,
см. шаблон template.pas.
При наличии нескольких модулей методы Poll вызываются в порядке их включения.
Супермодулем мы будем называть модуль, который включает в себя другие модули или супермодули. Так, например, стандартная библиотека StdLibrary является супермодулем, включающим в себе всю стандартную библиотеку. Основное назначение супермодуля - инкапсуляция модулей в сложной системе. Инкапсуляция предполагает "изолирующее или замещающее включение", то есть такое включение, при котором супермодуль заменяет собой все включенные модули, а прикладной программист изолируется от сложного состава включаемых модулей. При этом, хотя супермодуль может содержать много модулей, для программиста он представляется одним модулем, заменяющим собой все включенные в его состав модули.
Допустим, что супермодуль Super содержит два модуля Unit1 и Unit2. Тогда инкапсуляция выглядит так:
con_Super.inc содержит:
{$ con_Unit1.inc}
{$ con_Unit2.inc}
var_Super.inc содержит:
{$ var_Unit1.inc}
{$ var_Unit2.inc}
fun_Super.inc содержит:
{$ fun_Unit1.inc}
{$ fun_Unit2.inc}
procedure ClearSuper;
begin
ClearUnit1;
ClearUnit2;
end;
procedure InitSuper; // Вызов в прямом порядке!
begin
InitUnit1;
InitUnit2;
end;
procedure FreeSuper; // Вызов в обратном порядке!
begin
FreeUnit2;
FreeUnit1;
end;
procedure PollSuper;
begin
PollUnit1;
PollUnit2;
end;
При соблюдении показанных выше правил инкапсуляции супермодуль Super будет, с точки зрения его включения в тело прикладной программы, как "заместитель" модулей Unit1, Unit2. Еще более важно, что при обновлении модулей Unit1, Unit2 ничего снаружи менять не придется, все изменения будут локализованы в теле самих модулей. Это должно очень сильно облегчить техническую поддержку и сопровождение сложного программного кода.
Соблюдение намеченных здесь правил инкапсуляции модулей представляется очень важным с точки зрения создания больших и сложных проектов, потому что при использовании библиотек включаемых файлов программный код будет сильно "разбросан" по множеству файлов. Правила инкапсуляции призваны локализовать все изменения в рамках одного модуля, чтобы не "растаскивать" правку на множество файлов - это источник трудно находимых ошибок.
Недостатком предложенного подхода является то, что в модулях будет много "пустых" процедур. Однако это неизбежная цена за универсализм и надежность. Эта цена представляется приемлемой. Ведь даже если какая-то процедура (Clear,Init,Free,Poll) в данной версии модуля пуста, она может оказаться заполненной в следующей версии программы. При этом, соблюдая правила инкапсуляции, можно не беспокоиться о том, как модуль будет встраиваться в сложную систему, возможно, с многими уровнями включения.
Использование виртуального модуля является Addons наиболее простым путем инкапсуляции пользовательских функций в системную библиотеку в рамках данной конфигурации. Для этого в каталог DaqPas этой конфигурации копируются файлы _con_StdAddons.inc, _var_StdAddons.inc, _fun_StdAddons.inc и заполняются нужным пользователю кодом. Эти файлы "перекрывают" одноименные заглушки в системом каталоге, и таким образом пользовательские функции автоматически встраиваются в системную библиотеку и становятся доступны всем программам в данном каталоге, построенным на основе шаблона. Сами программы при этом не меняются.