Теперь обратимся к секции интерфейса. Хотя слово INTERFACE уже на месте, но секция пока пуста. Каково её назначение? Здесь пора рассказать о видимости объектов, размещенных в модуле. Точнее, о видимости их за пределами этого модуля. Оказывается, что всё, что помещено нами в секцию реализации, невидимо извне, скрыто от посторонних глаз. Так устроено по причинам, которые мы обсудим позже. Но для компиляции проекта надо приоткрыть часть модуля внешнему миру. Иначе из главной программы (которая проживает в другом файле) не будут видны нужные ей идентификаторы библиотечного модуля. Для этого и нужна секция интерфейса.
На обозрение выставим только те идентификаторы, которые нужны внешним модулям. Что именно? Как это узнать? Самый верный способ – запустить компиляцию первичного модуля, и тогда компилятор покажет первый незнакомый идентификатор, – его описание и разместим в секции интерфейса. Повторная компиляция выявит следующий неизвестный идентификатор. Так постепенно мы обнаружим, что первичный модуль нуждается в переменной Stack, процедуре Push и функции Pop. Впрочем, вам это было и так ясно.
Как выставить напоказ константы, переменные и описания типов? Надо просто напросто перенести их из секции реализации в секцию интерфейса. Но с процедурами и функциями так не выйдет. В секцию интерфейса нельзя вставлять исполняемый код – только объявления! Но можно разместить заголовки процедур и функций, для чего просто скопировать их ещё раз. Сделав это, мы получим следующий библиотечный модуль.
type PRec = ^TRec;
TRec = record
mStr : string;
mNext : PRec;
end;
var Stack : PRec; { Голова стека }
procedure Push(const arg : string); { заголовок процедуры }
function Pop(var arg : string): boolean; { заголовок функции }
{- – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – - – -}
procedure Push(const arg : string);
{... }
end;
function Pop(var arg : string): boolean;
{... }
end;
end.
Описание типа и переменная Stack объявлены лишь один раз – в секции интерфейса, – отсюда они видны как внутри, так и вне библиотечного модуля. Повторное их объявление в секции реализации будет ошибкой. Заголовки процедур и функций в секциях интерфейса и реализации должны совпадать. Но в секции реализации разрешено не повторять списки параметров, и тогда компилятор возьмет их из секции интерфейса.
Всё, что размещено в секции интерфейса, называют списком экспорта библиотечного модуля. Таким образом, первичный модуль программы импортирует то, что экспортирует библиотечный модуль, – так налаживается связь между модулями. На рис. 146 показаны окна с файлами нашего проекта: вверху – первичный файл, внизу – файл библиотечного модуля.
Теперь все готово для компиляции и запуска нашего проекта. Перейдите в окно первичного модуля и нажмите сочетание Ctrl+F9, – оба файла будут откомпилированы, и программа запустится как обычно. Я не зря прошу перейти в окно именно первичного модуля. Если при нажатии Ctrl+F9 активным будет другое окно, компилятор выдаст обидное сообщение: «Cannot run a unit» – нельзя запустить модуль. В самом деле, модуль – это лишь часть программы, он не может быть исполнен. Компилятор же считает, что в активном окне содержится главная программа и пытается её запустить.
Чтобы не спотыкаться здесь, настройте в IDE имя первичного файла, то есть файла с главной программой, – здесь это «P_59_1». Для этого обратитесь к пункту меню Compile –> Primary file… и укажите там нужный файл (рис. 147). Теперь компилятор будет знать, с какого файла начинать компиляцию. Но и библиотечный файл «MyLibr» он тоже будет обрабатывать всякий раз, когда вы измените в нём что-то, – это очень удобно.
А если начнёте другой проект? Тогда не забудьте сменить имя первичного файла, либо сбросьте это имя через пункт меню Compile –> Clear Primary file.
Прежде чем завершить наш многофайловый проект, слегка улучшим его и покажем полные тексты первичного и библиотечного модулей.
Начнем с того, что переменная Stack упоминается в главной программе лишь однажды – при инициализации.
Stack:= nil; { Инициализация стека пустым значением }