Logo    
Деловая газета CitCity.ru citforum.ck.ua - все об Open Source Форумы Все публикации Учебный центр Курилка
CitForum    CITForum на CD    Подписка на новости портала Море(!) аналитической информации! :: citforum.ck.ua
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Friday, 14-Mar-2008 16:21:39 EET

Google
WWW citforum.ck.ua
Техническая конференция «Корпоративные базы данных-2008»
Москва, 24-25 апреля

ГЛАВА 8. ПОТОКИ.

     Техника объектно-ориентированного  программирования  и   Turbo
Vision дают  Вам  мощный способ инкапсуляции данных и кода и мощные
способы построения взаимосвязанных структур объекта. Но что если Вы
хотите просто сохранить объекты на диске?
     До последнего времени данные хранились в записях и  запись  на
диск была  проста,  но  данные  внутри  Turbo  Vision в большинстве
случаев находятся внутри  объектов.  Конечно,  Вы  можете  отделить
данные от  объекта  и записать их на диск.  Но поскольку совместное
хранение данных и кода  дает  большие  преимущества,  разделить  их
снова было бы шагом назад.
     Не могут ли ООП и Turbo Vision  как-то  решить  эту  проблему?
Имеено для этого разработаны потоки.
     Поток Turbo Vision - это  коллекция  объектов  с  определенным
способом хранения:  обычно в файле, EMS, последовательном порту или
некотором другом устройстве.  Потоки  обрабатывают  В/В  на  уровне
объекта, а  не  на уровне данных.  Когда Вы расширяете объект Turbo
Vision, Вам необходимо  обеспечить  обработку  всех  полей  данных,
которые Вы определяете.



Вопрос: В/В объектов.


     Как программист на Паскале Вы знаете,  что  до  того,  как  Вы
сможете выполнять  В/В,  Вы  должны  сказать  компилятору какой тип
данных Вы будете  читать  или  писать  в  файл.  Файл  должен  быть
типированным и тип должен быть определен во время компиляции.
     Turbo Pascal реализует  очень  полезное  исключение  из  этого
правила: доступ   к   нетипированному   файлу  через  BlockWrite  и
BlockRead. Но  обход  проверки  типов  создает   для   программиста
определенные сложности,  хотя позволяет ему выполнять очень быстрый
двоичный В/В.
     Вторая проблема  в  том,  что  Вы не можете использовать файлы
прямо с объектами.  Turbo Pascal не позволяет Вам создать файл типа
объекта. И  поскольку  объекты  могут содержать виртуальные методы,
адрес которых  определяется   во   время   выполнения,   сохранение
информации VMT вне программы бессмысленно;  чтение такой информации
в программу еще более бессмысленно.
     Вы можете обойти эту проблему. Вы можете скопировать данные из
Ваших объектов и сохранить эту информацию в файле,  а затем  заново
построить объекты  из  этих данных.  Но это не элегантное решение и
усложняет создание объектов.



Ответ: потоки.


     Turbo Vision  позволяет  Вам  обойти  обе трудности,  а так же
предоставляет некоторые    дополнительные    возможности.    Потоки
предоставляют простой,   чрезвычайно   элегантный  способ  хранения
данных объекта вне программы.



Потоки являются полиморфными.


     Поток Turbo  Vision  дает  Вам  преимущество  и типированных и
нетипированных файлов:  проверка типов еще выполняется,  но то, что
Вы хотите  послать  в  поток,  не  обязано  определяться  во  время
компиляции. Причина этого в том,  что потоки знают как они работают
с объектами и,  если только объект порожден от TObject, поток может
обрабатывать его.  В самом деле,  различные  объекты  Turbo  Vision
можно легко записать в один поток, как группу идентичных объектов.



Управление объектами в потоке.


     Все, что Вы должны сделать - это определить для  потока, какие
объекты он должен обрабатывать, чтобы он знал как совместить данные
с VMT.  Затем Вы можете поместить объекты в  поток  и  получить  их
обратно без малейших усилий.
     Но как можно читать и писать  в  один  поток  такие  различные
объекты как  TDeskTop и TDiаlog и во время компиляции не знать даже
какие объекты  будут  обрабатываться?  Это  сильно  отличается   от
В/В традиционного   Паскаля.   В   действительности   поток   может
обрабатывать даже новые типы объектов,  которые еще не были созданы
в момент компиляции потока.
     Это решается с помощью регистрации. Каждому типу объекта Turbo
Vision (и новым типам объектов,  которые Вы порождаете из иерархии)
назначается уникальный   регистрационный    номер.    Этот    номер
записывается в поток перед данными объекта. Затем, когда Вы читаете
объект из потока,  Turbo Vision берет регистрационный  номер  и  на
основании его   знает   сколько   данных   прочитать  и  какую  VMT
подсоединить к Вашим данным.



Сущность использования потоков.


     На фундаментальном  уровне  Вы  можете  думать о потоках как о
файлах Паскаля.  В  своей  основе  файл  Паскаля   -   это   просто
последовательное устройство В/В,  Вы просто пишите в него и читаете
из него.  В свою очередь поток - это  полиморфное  последовательное
устройство В/В,  что  означает,  что  он  ведет  себя  так же,  как
последовательный файл,  но Вы можете так же читать и писать объекты
различного типа.
     Потоки можно так же рассматривать (как и  файлы  Паскаля)  как
устройство В/В  с прямым доступом,  где Вы устанавливаете позицию в
файле, читаете  или  пишите  с  этой  точки,  возвращаете   позицию
указателя файла  и  т.д.  Эти операции так же доступны с потоками и
описаны в разделе "Прямой доступ к потокам".
     Существует 2  аспекта использования потока,  которые Вы должны
хорошо себе представлять и к счастью они оба очень просты. Первый -
это установка потока, второй - чтение и запись объектов в поток.



Установка потока.


     Для использования потока  Вы  должны  просто  инициализировать
его. Точный    синтаксис   констрактора   Init   будет   изменяться
в зависимости от  типа  используемого  потока.  Например,  если  Вы
открываете поток DOS,  Вам требуется передать имя файла DOS и режим
доступа (чтение, запись, чтение/запись) к файлу, содержащему поток.
     Например, чтобы  инициализировать буферизованный поток DOS для
загрузки объекта панель экрана в программу, Вам требуется только:

     var
       SaveFile: TBufStream;
     begin
       SaveFile.Init('SAMPLE.DSK', stOpen, 1024);
       .

     После того,  как Вы  инициализировали  поток,  Вы  можете  его
использовать.
     TStream - это абстрактный  механизм  потока  и  Вы  не  можете
создать экземпляр  от  него,  но  все  объекты потоков порождены от
TStream. Они  включают  TDosStream,  обеспечивающий  дисковый  В/В,
TBufStream, обеспечивающий  буферизованный  В/В  с  диска (полезен,
если Вы читаете или  пишите  большое  количество  небольших  кусков
данных)  и  TEmsStream,  который  пересылает  объекты  в EMS память
(особенно полезен для реализации быстрых ресурсов).
     Turbo Vision   так   же  реализует  индексированные  потоки  с
указателем на место  в  потоке.  Изменяя  положение  указателя,  Вы
можете осуществлять прямой доступ к потоку.



Чтение и запись потока.


     Базовый объект потоков TStream реализует  3  основных  метода:
Get, Put и Error.  Get и Put примерно соответствуют процедурам Read
и Write.  Процедура Error вызывается  при  возникновении  ошибки  в
потоке.



Вывод в поток.


     Вначале рассмотрим процедуру Put. Общий синтаксис метода Put:

     SomeStream.Put(PSomeObject);

     где SomeStream - это любой объект,  порожденный от  TStream  и
инициализированный, а  PSomeObject - это указатель на любой объект,
порожденный от TObject и зарегистрированный  с  этим  потоком.  Это
все, что  Вы должны сделать.  Поток может узнать из VMT PSomeObject
тип объекта  (предполагая,  что   тип   был   зарегистрирован)   и,
следовательно, знать,  какой  номер  ID записывать и сколько данных
записать после него.
     Однако для  Вас,  как для программиста на Turbo Vision,  будет
особенно важно то,  что когда  Вы  записываете  в  поток  группу  с
подэлементами, подэлементы   будут   автоматически  записываться  в
поток. Поэтому сохранение сложного объекта будет вовсе  не сложным,
поскольку выполняется   автоматически!   Вы  можете  сохранить  все
состояние Вашей программы просто записав  панель  экрана  в  поток.
Когда Вы  запустите Вашу программу снова и загрузите панель экрана,
программа будет в том же состоянии, что и в точке сохранения панели
экрана.



Ввод из потока.


     Считать объекты из потока  так  же  просто.  Вы  делаете  это,
используя функцию Get:

     PSomeObject := SomeStream.Get;

     где как  и  раньше SomeStream - инициализированный поток Turbo
Vision, а РSomeObject  -  указатель  на  любой  тип  объекта  Turbo
Vision. Get  просто  возвращает  указатель на то,  что он считал из
потока. Сколько данных он считал и какой тип VMT он  назначил  этим
данным определяется   не   типом   PSomeObject,  а  типом  объекта,
найденного в  потоке.  Поэтому,  если  объект  в  текущей   позиции
SomeStream не того же типа,  что PSomeObject, Вы получите случайную
информацию.
     Как и  Put,  Get  восстанавливает сложные объекты.  Так,  если
объект, считанный Вами из потока,  - это видимый элемент, владеющий
подэлементами, подэлементы будут так же загружены.



В случае ошибки.


     Наконец, процедура  Error  определяет  что  произойдет,  когда
возникнет ошибка. По умолчанию TStream.Error просто устанавливает 2
поля (Status и ErrorInfo) в потоке. Если Вы хотите выполнить другие
действия, например  сгенерировать  ошибку  времени  выполнения  или
вывести ошибку  в  диалоговое  окно,   Вам   необходимо   перекрыть
процедуру Error.



Удаление потока.


     Когда Вы заканчиваете использование потока,  Вы вызываете  его
метод Done точно так же, как Вы вызывали Close для дискового файла.
Как и для любого объекта Turbo Vision, Вы делаете это

     Dispose(SomeStream, Done);

     который удаляет объект потока.



Использование объектов с потоком.


     Все стандартные  объекты Turbo Vision готовы к использованию с
потоками и все потоки Turbo Vision знают эти  стандартные  объекты.
Когда Вы  порождает  новый  тип  объекта  от одного из стандартных,
очень просто  подготовить  его  для  использования  с   потоком   и
сообщить потоку о его существовании.



Методы Load и Store.


     Действительное чтение и запись объектов в поток обрабатывается
методами Load  и  Store.  Хотя  каждый  объект должен содержать эти
методы для того,  чтобы его можно было использовать с потоками,  Вы
никогда не вызываете их прямо.  (Они вызываются из Get и Put). Все,
что Вам требуется сделать - это убедиться, что Ваш объект знает как
послать себя в поток, когда ему говорят сделать это.
     Благодаря ООП эта работа очень проста, поскольку большая часть
механизма наследуется из объекта предка. Все, что Ваш объект должен
сделать -  это  загрузить  или  сохранить  ту  часть,  которую   Вы
добавили; за остальное отвечает вызов метода предка.
     Например Вы породили новый тип видимого элемента  от  TWindow,
назвав его  именем  известного  сюрреалиста Рене Магритте,  который
нарисовал много известных картин с окнами:

     type
       TМagritte = object(TWindow)
       Painted: Boolean;
       constructor Load(var S: TStream);
       procedure Draw;
       procedure Store(var S: TStream);
     end;

     В часть данных было добавлено только одно  поле  Boolean.  Для
того, чтобы   загрузить   объект,  Вы  просто  читаете  стандартный
TWindow, а затем читаете дополнительный байт,  соответствующий полю
Boolean. То  же  самое  применимо  к сохранению объекта:  Вы просто
записываете TWindow,  а потом записываете еще один  байт.  Типичные
методы Load и Store для порожденных объектов имеют вид:

     constructor TМargitte.Load(var S: Stream);
     begin
       TWindow.Load(S);
       S.Read(Painted, SizeOf(Boolean))
     end;

     procedure TМargitte.Store(var S: Stream);
     begin
       TWindow.Store(S);
       S.Write(Painted, SizeOf(Boolean))
     end;

     Предупреждение: Вы   полностью   ответственны   за   то,   что
сохраняется столько же данных,  сколько и загружается и что  данные
загружаются в том же порядке,  в каком они сохранялись.  Компилятор
не выдает ошибок. Это может приводить к огромным проблемам, если Вы
неаккуратны. Если   Вы  изменяете  поля  объектов,  убедитесь,  что
скорректированы и Load и Store.



Регистрация потока.


     В дополнение  к  определению  методов  Load и Store для нового
объекта, Вы  должны  так  же  зарегистрировать  новый  тип  объекта
в потоке.   Регистрация  -  это  простой  двухшаговый  процесс,  Вы
определяете запись регистрации потока и передаете ее  в  глобальную
процедуру RegisterType.
     Чтобы определить запись регистрации  потока,  просто  следуйте
формату. Запись  регистрации  потока  -  это  запись  Паскаля  типа
TStreamRec, определенная:

     PStreamRec = ^TStreamRec;
     TStreamRec = record
       ObjType: Word;
       VmtLink: Word;
       Load: Pointer;
       Store: Pointer;
       Next: Word;
     end;

     Примечание: Все     стандартные     объекты    Turbo    Vision
зарегистрированы и Вам не нужно делать этого.

     По соглашениям Turbo Vision  все  записи  регистрации  потоков
имеют имена  соответствующих  объектных типов с заменой начальной Т
на R.  Так запись регистрации для  TDeskTop  -  RDeskTop  и  запись
регистрации для TMagritte - RMagritte.  Абстрактные типы, такие как
TObject и TView не имеют регистрационных записей, поскольку никогда
не создаются экземпляры этого типа.



Номера ID объектов.


     Поле ObjType -  это  единственная  часть  записи,  которую  Вы
должны знать.  Каждый новый тип, определенный Вами, будет требовать
своего собственного   уникального   номера   типа.   Turbo   Vision
резервирует регистрационные  номера  от  0  до  99  для стандартных
объектов. Вы можете использовать регистрационные номера от  100  до
65,535.

     Примечание: Вы ответственны за создание и поддержку библиотеки
номеров ID для всех новых объектов,  которые используются в потоках
В/В и должны сделать ID доступными для пользователей Ваших модулей.
Как и  константы  команд,  номера,  назначаемые  Вами  могут   быть
абсолютно произвольными, главное, чтобы они были уникальными.



Автоматические поля.


     Поле VmtLink - это связь с таблицей виртуальных  методов (VMT)
объекта. Вы просто назначаете его как смещение типа Вашего объекта:

     RSomeObject.VmtLink := Ofs(TypeOf(TSomeObject)^);

     Поля Load  и Store содержат адреса методов Load и Store Вашего
объекта.

     RSomeObject.Load := @TSomeObject.Load;
     RSomeObject.Store := @TSomeObject.Store;

     Последнее поле  Next назначается в процедуре RegisterType и не
требует Вашего вмешательства.  Оно  предназначено  для  внутреннего
использования в связанном списке регистрационных записей потока.



Регистрация.


     После того,  как Вы  создали  запись  регистрации  потока,  Вы
вызываете RegisterType,  передавая  ему  Вашу  запись.  Так,  чтобы
зарегистрировать объект TMagritte для использования с  потоками, Вы
пишите:

     const
       RMagritte: TStreamRec = (
         ObjType: 100;
         VmtLink: Ofs(TypeOf(TMagritte)^);
         Load: @TMagritte.Load;
         Store: @TMagritte.Store
       );
     RegisterType(RMagritte);

     Теперь Вы можете выводить экземпляры  нового  типа  объекта  в
любой поток Turbo Vision и читать эти экземпляры из потоков.



Механизм потоков.


     Сейчас, когда мы просмотрели  использование  потоков,  давайте
посмотрим что  делает  Turbo  Vision  с Вашими объектами,  когда Вы
вводите или выводите их.



Работа Put.


     Когда Вы  посылаете  объект  в поток методом Put,  поток берет
указатель VMT со смещением 0  от  объекта  и  просматривает  список
типов, зарегистрированных  с  потоками  на  соответствие.  Когда он
находит соответствие,  поток  выбирает  регистрационный  номер   ID
объекта и записывает его в поток.  Поток затем вызывает метод Store
объекта для записи объекта.  Метод Store использует процедуру Write
потока, которая записывает правильное число байт в поток.
     Ваш объект ничего не знает о потоке.  Это может быть  дисковый
файл, EMS  память или другой тип потока - Ваш объект просто говорит
"Запиши меня в поток" и поток выполняет остальное.



Работа Get.


     Когда Вы   читаете  объект  из  потока  методом  Get,  вначале
вводится номер ID и сканируется список зарегистрированных  типов на
соответствие. Когда   соответствие   найдено,   запись  регистрации
предоставляет потоку положение метода Load  и  VMT  объекта.  Затем
вызывается метод Load для чтения соответствующего количества данных
из потока.
     И снова,  Вы  просто  говорите потоку взять следующий объект и
вернуть указатель на его положение.  Ваш объект  не  заботится,  из
какого потока он был получен. Поток обеспечивает чтение правильного
количества данных, используя метод Load объекта.
     Это показывает как важно зарегистрировать тип до попытки В/В в
поток.



Обработка nil указателей на объект.


     Вы можете  записать  nil  объект  в  поток.  Однако,  когда Вы
делаете это,  в поток записывается слово со значением 0. При чтении
ID = 0,  поток возвращает nil указатель, поэтому 0 зарезервирован и
не может использоваться как ID номер объекта.



Коллекции в потоках: полный пример.


     В главе   7  "Коллекции"  Вы  видели  как  можно  сохранять  в
коллекциях различные,  но  связанные  объекты.  К  потокам  так  же
применимы полиморфные  свойства  и  они  могут  использоваться  для
сохранения целой коллекции на диске  для  восстановления  в  другое
время или  даже  другой программы.  Посмотрим еще раз TVGUID20.PAS.
Что нужно сделать, чтобы эта программа вывела коллекцию в поток?
     Ответ удивительно прост.  Во-первых, начнем с базового объекта
TGraphObject и "научим" его как сохранять его  данные  (X  и  Y)  в
потоке. Это  делает метод Store.  Затем определим новый метод Store
для каждого наследника TGraphObject,  который добавляет новые  поля
(TGraphCircle добавляет   Radius;   TGraphRect  добавляет  Width  и
Height).
     Затем создадим   регистрационную   запись   для  каждого  типа
объекта, который будет сохраняться и зарегистрируем каждый  из этих
типов. Это   все.  Все  остальное  как  при  обычном  вводе/выводе:
объявите переменную потока;  создайте новый  поток;  поместите  всю
коллекцию в поток одним оператором; закройте поток.



Добавление методов Store.


     Здесь приведены методы  Store.  Заметим,  что  PGraphPoint  не
требует его,  поскольку  не  добавляет  полей  при  наследовании от
PGraphObject.

     type
       PGraphObject = ^TGraphObject;
       TGraphObject = object(TObject);
       .
       procedure Store(var S: TStream); virtual;
     end;

     PGraphCircle = ^TGraphCircle;
     TGraphCircle = object(TGraphObject)
       Raduis: Integer;
       .
       procedure Store(var S: TStream); virtual;
     end;

     PGraphRect = ^TGraphRect;
     TGraphRect = object(TGraphObject)
       Width, Height: Integer;
       .
       procedure Store(var S: TStream); virtual;
     end;

     Реализация Store  совершенно  проста.  Каждый  объект вызывает
наследуемый метод Store,  который сохраняет все наследуемые данные.
Затем метод  Write потока записывает дополнительные данные.

     { TGraphObject не вызывает TObject.Store, поскольку TObjeсt не
имеет данных для сохранения }
     procedure TGraphObject.Store(var S: TStream);
     begin
       S.Write(X, SizeOf(X));
       S.Write(Y, SizeOf(Y));
     end;

     procedure TGraphCircle.Store(var S: TStream);
     begin
       TGraphObject.Store(S);
       S.Write(Radius, SizeOf(Radius));
     end;

     procedure TGraphRect.Store(var S: TStream);
     begin
       TGraphObject.Store(S);
       S.Write(Width, SizeOf(Width));
       S.Write(Height, SizeOf(Height));
     end;

     Заметим, что метод Write из TStream выполняет двоичную запись.
Его первый   параметр   может   быть  переменной  любого  типа,  но
TStream.Write не знает размера  этой  переменной.  Второй  параметр
предоставляет эту  информацию  и Вы должны использовать стандартную
функцию SizeOf. Таким образом, если Вы решите изменить координатную
систему, используя  числа  с  плавающей точкой,  Вам не потребуется
корректировать методы Store.



Записи регистрации.


     Определение константы   записи   регистрации  для  каждого  из
наследуемых типов,  выполняется  последним  шагом.  Хорошая   мысль
следовать соглашению  Turbo  Vision  по  именованию:  используя R в
качестве первой буквы.

     Примечание: Вспомним,  что каждая запись регистрации  получает
уникальный номер  идентификатора  объекта  (Objtype).  Turbo Vision
резервирует от  0  до  99  для  стандартных  объектов.  Рекомендуем
сохранить ID  номера  всех  объектов  в  одном  месте для избежания
дублирования.

     const
       RGraphPoint: TStreamRec = (
         ObjType: 150;
         VmtLink: Ofs(TypeOf(TGraphPoint)^);
         Load: nil;
         Store: @TGraphPoint.Store;

       RGraphCircle: TStreamRec = (
         ObjType: 151;
         VmtLink: Ofs(TypeOf(TGraphCircle)^);
         Load: nil;
         Store: @TGraphCircle.Store;

       RGraphRect: TStreamRec = (
         ObjType: 152;
         VmtLink: Ofs(TypeOf(TGraphRect)^);
         Load: nil;
         Store: @TGraphRect.Store;

     Вам не  требуется  регистрационная  запись  для  TGraphObject,
поскольку это абстрактный тип и никогда не имеет экземпляров  и  не
помещается в  коллекцию  или  поток.  Каждый  указатель Load записи
регистрации устанавливается в nil, поскольку этот пример был создан
только для сохранения данных в поток. Методы Load будут определены,
а регистрационные  записи  скорректированы  в   следующем   примере
(TVGUID22.PAS).



Регистрация.


     Вы не должны забывать регистрировать каждую из этих записей до
выполнения ввода/вывода  в  поток.  Простейший способ сделать это -
поместить их в одну процедуру и вызвать ее в начале Вашей программы
(или в методе Init Вашей программы).

     procedure StreamRegistration;
     begin
       RegisterType(RCollection);
       RegisterType(RGraphPoint);
       RegisterType(RGraphCircle);
       RegisterType(RGraphRect);
     end;

     Заметим, что  Вы  зарегистрировали  TCollection (используя его
запись  RCollection  -  теперь  Вы  видите  почему  соглашения   об
именовании упрощает   программирование),   хотя  Вы  не  определяли
TCollection. Это правило просто:  Вы отвечаете за регистрацию  всех
типов объектов, которые выводятся в поток.



Запись в поток.


     Все, что осталось сделать - это обычная последовательность В/В
в файл: создать поток; поместить данные (коллекцию) в него; закрыть
поток. Вы  не  используете  итератор  ForEach  для  записи  каждого
элемента коллекции  в  поток.  Вы  просто говорите потоку поместить
коллекцию в поток:

     { TVGUID21.PAS }
     var
       GraphicsList: PCollection;
       GraphicsStream: TBufStream;
     begin
       StreamRegistration;
       .
       GraphicsStream.Init('GRAPHICS.STM', stCreate, 1024);
       GraphicsStream.Put(GraphicsList);
       GraphicsStream.Done;
       .
     end.

     Будет создаваться  дисковый   файл,   который   содержит   всю
информацию, необходимую для чтения коллекции в память.  Когда поток
открывается и коллекция считывается (см. TVGUID22.PAS), все скрытые
связи между  коллекцией  и ее элементами и объектами и их таблицами
виртуальных методов будут восстановлены. Эта техника используется в
IDE Turbo  Pascal для сохранения его файла панели экрана. Следующий
пример показывает Вам как сделать это. Но вначале Вы должны изучить
объекты, выводимые  в  поток,  которые  содержат  связи  с  другими
объектами.



Кто сохраняет?


     Важное предупреждение о потоках:  пользователь объекта  -  это
тот  объект,  который  должен  записывать этот объект в поток.  Это
предупреждение подобно тому с которым Вы вероятно  сталкивались при
использовании традиционного Паскаля:  владелец указателя - это тот,
кто должен освободить этот указатель.
     В сложной  реальной  программе  многочисленные  объекты  часто
будут содержать указатель на какую-то структуру. Когда придет время
для В/В  в поток,  Вы должны решить кто владелец структуры;  только
этот владелец должен посылать  эту  структуру  в  поток.  Иначе  Вы
получите в  потоке несколько копий одной структуры.  Затем когда Вы
будете читать поток будет создано несколько экземпляров структуры и
каждый отдельный объект теперь будет указывать на собственную копию
структуры вместо единственной первоначальной структуры.



Экземпляры видимых подэлементов.


     Вам будет  удобно  сохранять указатели на подэлементы группы в
локальных экземплярах переменных.  Например,  диалоговое окно часто
хранит указатели  на  объекты  элементов  управления в мнемонически
названных полях для упрощения доступа (такие поля как  OKButton или
FileInputLine). Когда  этот  видимый  элемент  вставляется в дерево
видимых элементов,  владелец   содержит   2   указателя   на   этот
подэлемент, один в поле, а другой в списке подэлементов. Если Вы не
разрешаете это,  чтение объекта из потока приведет  к  дублированию
экземпляров.
     Решение -   в   предоставлении   методов    GetSubViewPtr    и
PutSubViewPtr  в TGroup.  Когда сохраняется поле,  которое является
подэлементом,  вместо  того  чтобы  записать  указатель,  как   это
делается с другими переменными, Вы вызываете PutSubViewPtr, который
сохраняет ссылку  на  позицию  подэлемента  в  списке  подэлементов
группы.  Таким образом когда Вы загружаете группу с помощью Load из
потока,  Вы можете вызвать GetSubViewPtr,  который гарантирует, что
это поле и список подэлементов указывают на один объект.
     Приведем короткий   пример   использования   GetSubViewPtr   и
PutSubViewPtr в простом окне:

     type
       TButtonWindow = object(TWindow)
         Button: PButton;
         constructor Load(var S: TStream);
         procedure Store(var S: TStream);
       end;

     constructor Load(var S: TStream);
     begin
       TWindow.Load(S);
       GetSubViewPtr(S, Button);
     end;

     procedure Store(var S: TStream);
     begin
       TWindow.Store(S);
       PutSubViewPtr(S, Button);
     end;

     Давайте посмотрим  чем  этолт  метод   Store   отличается   от
нормального Store.  После  сохранения  окна  Вы  просто  сохраняете
ссылку на поле Button вместо сохранения  самого  поля.  Сам  объект
кнопки сохраняется   как   подэлемент   окна,   когда   Вы  вызвали
TWindow.Store. Все  что  Вы  делаете  дополнительно   к   помещению
информации в  поток  это  говорите,  что  Button  указывает на этот
подэлемент. Метод Load делает то же  в  обратном  порядке,  вначале
загружая окно и подэлемент кнопки, а затем восстанавливая указатель
на этот подэлемент в Button.



Равные экземпляры видимого элемента.


     Аналогичная ситуация  возникает,  когда  видимый элемент имеет
поле, указывающее на равный ему видимый  элемент.  Видимый  элемент
называется равным  другому  видимому  элементу,  если  оба  видимых
элемента принадлежат  одной  группе.  Хороший  пример  -  скроллер.
Поскольку скроллер  знает  о  двух  полосах скроллинга,  являющихся
элементами окна,  которому принадлежит скроллер, он имеет два поля,
которые указывают на эти видимые элементы.
     Как и с видимыми подэлементами,  у Вас могут быть проблемы при
чтении и  записи ссылок на равные видимые элементы в поток. Решение
также просто.  Методы PutPeerViewPtr и GetPeerViewPtr предназначены
для доступа   к   позиции   другого   видимого  элемента  в  списке
подобъектов владельца.
     Нужно заботится  только  о  загрузке  ссылок на равные видимые
элементы, который еще не загружены (т.е.  они стоят позже в  списке
подэлементов и   следовательно   позже   в  потоке).  Turbo  Vision
обрабатывает это автоматически,  сохраняя трассу всех таких  ссылок
вперед и разрешая их, когда все подэлементы группы будут загружены.
Вам необходимо помнить,  что ссылки на равные видимые  элементы  не
действительны до  тех  пор,  пока  не  будет  завершен  весь  Load.
Вследствие этого Вы не должны помещать в метод  Load  код,  который
использует подэлементы,  зависящие  от  равных подэлементов,  иначе
результаты будут непредсказуемы.



Сохранение и загрузка панели экрана.


     Если Вы сохраняете панель экрана в потоке, панель экрана будет
сохранять все свое содержимое: всю среду панели экрана, включая все
текущие видимые элементы.
     Если Вы хотите разрешить пользователю сохранять панель экрана,
Вам необходимо убедиться,  что все возможные видимые элементы имеют
соответствующие видимые элементы Store  и  Load,  что  все  видимые
элементы зарегистрированы,  поскольку  пользователь может сохранить
панель экрана в любой момент.
     Чтобы сделать это Вы можете использовать подобный код:

     procedure TMyApp.RestoreDeskTop;
     var
       SaveFile: TBufStream;
       Temp: PDeskTop;
     begin
       SaveFile.Init('T.DSK', stOpen, 1024);
       Temp := PDeskTop(SaveFile.Get);
       SaveFile.Done;
       if Temp <> nil then
       begin
         Dispose(DeskTop, Done);
         DeskTop := Temp;
         Append(DeskTop);
         DeskTop^.DrawView;
       end;
       if SaveFile.Status <> 0 then ErrorReadingFile;
     end;

     Вы можете  сделать следующий шаг и сохранять и восстанавливать
всю программу.  Объект   TApplication   может   сам   сохранять   и
восстанавливать себя.



Копирование потоков.


     TStream имеет метод CopyFrom(S, Count), который копирует Count
байт из  потока  S.  CopyFrom  может использоваться для копирования
всего содержимого  потока  в  другой  поток.  Если   Вы   постоянно
обращаетесь к  дисковым  потокам,  Вы  можете  скопировать их в EMS
поток для более быстрого доступа:

     NewStream := New(TEmsStream, Init(OldStream^.GetSize));
     OldStream^.Seek(0);
     NewStream^.CopyFrom(OldStream, OldStream^.GetSize);



Прямой доступ к потокам.


     До сих   пор   мы  использовали  потоки  как  последовательные
устройства: Вы выводили объекты  в  конец  потока  и  считывали  их
обратно в   том   же   порядке.   Turbo  Vision  предоставляет  Вам
дополнительные возможности. Он позволяет Вам интерпретировать поток
как виртуальное устройство с прямым доступом.  В дополнение к Get и
Put, которые  соответствуют  Read  и  Write   для   файла,   потоки
предоставляют возможности   аналогичные   файловым  Seek,  FilePos,
FileSize и Truncate.
     - Процедура  Seek  передвигает  указатель  текущего  потока на
заданную позицию (в  байтах  лот  начала  потока)  как  стандартная
процедура Seek Turbo Pascal.
     - Функция  GetPos  обратна  процедуре  Seek.  Она   возвращает
LongInt с текущей позицией в потоке.
     - Функция GetSize возвращает размер потока в байтах.
     - Процедура  Truncate удаляет все данные после текущей позиции
потока, делая текущую позицию последней в потоке.
     Чтобы можно  было использовать эти программы,  прямой доступ к
потоку требует создания вне потока индекса,  содержащего  начальные
позиции каждого объекта в потоке.  Коллекция идеальна для этой цели
и в действительности используется в Turbo Vision с файлами ресурсов
(ресурсы обсуждаются в главе 9). Если Вы хотите использовать прямой
доступ к потоку, Вы можете использовать файл ресурса.



Не-объекты в потоке.


     Вы можете   писать   в   поток  данные,  которые  не  являются
объектами, но  Вы  должны  использовать  при  этом  другой  подход.
Стандартные методы  Get  и  Put  требуют,  чтобы  Вы  сохраняли или
загружали объект,  порожденный от TObject.  Если Вы хотите  создать
поток не-объектов, перейдите прямо на низкоуровневые процедуры Read
и Write,  каждая из которых читает или пишет заданное число байт  в
поток. Этот  же  механизм  используется  в  Get  и Put для чтения и
записи данных  объектов;   Вы   просто   обходите   механизм   VMT,
используемый в Get и Put.



Проектирование Ваших потоков.


     Этот раздел суммирует методы и  возможности  обработки  ошибок
потоков Turbo Vision так, чтобы Вы знали что Вы можете использовать
при создании новых типов потоков.
     TStream -   это   абстрактный   объект,  который  должен  быть
расширен, чтобы  создать  используемый  тип   потока.   Большинство
методов TStream  абстрактные  и  должны  быть  реализованы  в Ваших
наследниках, а другие зависят от  методов  TStream.  Только  методы
Error, Get и Put полностью реализованы в TStream.  GetPos, GetSize,
Read, Seek,  SetPos,  Truncate и Write должны быть перекрыты.  Если
тип порожденного  объекта имеет буфер,  так же должен быть перекрыт
метод Flush.



Обработка ошибок потока.


     TStream имеет  метод  Error(Code,  Info),  который вызывается,
когда поток обнаруживает ошибку.  Error просто  устанавливает  поле
Status в   одну  из  констант,  приведенную  в  разделе  "Константы
stXXXX" главы 14.
     Поле ErrorInfo  за  исключением  ситуации,  когда Status равен
stGetError или stPutError,  неопределено. Если Status - stGetError,
поле ErrorInfo  содержит  номер  ID  незарегистрированного  типа  в
потоке. Если Status - stPutError,  поле ErrorInfo содержит смещение
VMT для  типа,  который  Вы пытаетесь поместить в поток.  Вы можете
перекрыть TStream.Error  для  создания  любой   обработки   ошибок,
включая ошибки времени выполнения.
                              Назад | Содержание | Вперед

 

 

\

Подписка на новости IT-портала citforum.ck.ua
(библиотека, citforum.ck.ua, CitCity)

Новые публикации:

12 марта

  • Восход и закат High Performance Fortran: наглядный урок истории (пересказ: С.Кузнецов)
  • citforum.ck.ua:

    Новые Блогометки:

  • Ccze: хорошее модульное средство подсветки логов
  • PWSafe - кроссплатформенное средство для работы с паролями
  • colordiff - подсветка для diff
  • psmisc: рассмотрим ближе стандартный пакет
  • Работа с сетью
  • xkb, узелок на память
  • ffmpeg-php
  • debiannotes:desktop:prettyfonts
  • 5 марта

    citforum.ck.ua:

  • Ричард Столлман в Москве
  • О мудром доценте замолвите слово... (Интенсификация Малаховна)
  • Новые Блогометки:

  • "Десктопизация" OpenBSD
  • weather: проверяйте сводку и прогноз погоды из командной строки
  • hpodder: клиент подкастов, который просто работает
  • bc: язык численных расчетов с произвольной точностью
  • Decibel: аудиоплеер для людей
  • GNU Wget: загрузите весь понравившийся сетевой контент на локальный компьютер
  • Deborphan: найдите ненужные пакеты
  • Kivio: мощный и простой в использовании редактор блок-схем
  • Cowsay: настраиваемая говорящая и думающая корова
  • Thoggen: основанная на GTK+ программа для извлечения видео с DVD
  • 28 февраля

  • Подбор и развитие команд
    Глава из книги «Руководство командой разработчиков программного обеспечения. Прикладные мысли» (С.Архипенков)
  • citforum.ck.ua:

    Дискуссия об анонимусах:

  • К комментаторам
  • Windows против Linux - психологический портрет участников форумов
  • Новые Блогометки:

  • Nokia N810 - Linux Inside
  • LiMo - стандарты Linux для сотовых телефонов
  • timer-applet: таймер для панели GNOME
  • Debfoster: удалите пакет и все его зависимости
  • GPW: генератор произносимых паролей
  • AMOR: общество для рабочего стола
  • 20 февраля

    citforum.ck.ua:

    Новые Блогометки:

  • Кое-что о приложениях KDE 4
  • Инструкция по установке KDE 4 в Ubuntu
  • Настоящие мужчины ставят KDE из SVN!
  • Начат переход Amarok на Qt 4.4
  • Marble
  • Dillo - сверхбыстрый браузер
  • Создаем резервные копии настроек программ и важных файлов в Ubuntu LInux
  • NTP: всегда вовремя
  • VYM - простое средство зарисовки мыслей и планирования
  • KBibTeX: простой и гибкий редактор библиографий для KDE
  • Дискуссия Windows vs Linux:

  • Жил-был Мальчик, или Сказочка о Том, Откуда Берутся "КУЛХАЦКЕРЫ", ненавидящие Линукс и Юникс
  • 13 февраля

  • Терминологический словарь Wi-Fi
  • Задача проектирования базы данных методом нормализации
  • CitCity:

  • Лучшие смартфоны начала 2008 года
  • citforum.ck.ua:

  • Первый взгляд на Firefox 3.0
  • Open Source на Белгородщине: семинар в Старом Осколе
  • Что такое KDE?
  • Цикл о Slackware:

  • Русский в консоли
  • Быстрая настройка Иксов
  • xorgconfig - консольный подход
  • 6 февраля

    citforum.ck.ua:

  • Мобильный Linux – вчера, сегодня, завтра
  • Чем записать диски в Linux? Попробуй Brasero!
  • Консольные команды
  • Рецепты. Кое-что о программе mplayer
  • Slackware:
    • Что такое Slackware?
    • Установка Slackware - Загрузка
    • Категории программного обеспечения
    • Структура файловой системы
    • Система инициализации Slackware Linux
    • Скрипты инициализации уровня запуска

    30 января

  • Обзор алгоритмов MOLAP
  • CitCity:

  • BI-технологии 2007. Итоги года
  • Рынок СУБД для Хранилищ данных 2007. Итоги года, тенденции
  • Обзор рынка BI (по результатам исследований IDC, OLAP Report, Gartner)
  • Модель зрелости BI
  • citforum.ck.ua:

  • Владимир Попов: За что я люблю Linux
  • Священные войны
  • 23 января

  • Data Mining от Oracle: настоящее и будущее
  • Комментарии к статье Ч.Бергера «Data Mining от Oracle: настоящее и будущее»
  • Байесовский классификатор и регрессионная модель в ORTD: практический пример
  • citforum.ck.ua:

    Дискуссия Windows vs Linux:

  • Программисты и фирмы: кто кого
  • О "чистых пользователях"
  • Новые Блогометки:

    • Почему Jabber, а не ICQ?
    • Archlinux install quick
    • Arch на IBM Z60m
    • Arch + IBM R50e
    • OpenBSD - сборка E17-cvs (или ещe одна маленькая победа разума)
    • OpenBSD - всe для Человека и ради Человека...
    • PekWM
    • E17 и "прозрачность"
    • E17 - приятные мелочи (multimedia)
    • SuSE + Enlightenment = угробил целый день

    16 января

  • Вьетнам компьютерной науки (пересказ - С.Кузнецов)
  • Пример построения автоматизированного управления дисками (ASM) (В. Пржиялковский)
  • CitCity:

  • 2008 год: антипрогноз
  • citforum.ck.ua:

    Новые Блогометки:

    Сети и Интернет:

    • Mozilla firefox. Шрифты в меню
    • Screen tips
    • Liferea: программа чтения RSS для GNOME
    • HTTrack: скачивание и зеркалирование сайтов
    • Clusterssh: работа с несколькими сеансами SSH через общий интерфейс

    Десктопы:

    • Fluxbox & xinitrc. Some new tips
    • Как я конфигурировал xdm

    Системы:

    • SuSE 10.2: zypper - еще один способ установки пакетов
    • cpipe: определите пропускную способность конвейера команд
    • gddrescue: средство восстановления данных с поврежденных носителей
    • VirtualBox: ваш виртуальный ПК

    Приложения:

    • MyTop: top для MySQL

    10 января

    citforum.ck.ua:

    Дискуссионный клуб:

  • Краткое руководство по общению с никсофилами (Интенсификация Малаховна Сергина-Гейтс)
  • О троллях
  • Пещера горного короля: заметки о троллинге
  • Новые Блогометки:

    Сети и Интернет:

    • Делаем блог на Drupal
    • Использование lftp
    • Устанавливаем FTP сервер ProFTPd с TLS шифрованием
    • Управляем файлами на FTP сервере с помощью FileZilla

    Десктопы:

    • fluxbox.autorun
    • 15 человек на сундук мертвеца! (или песнь о зарытых сокровищах)

    Системы:

    • Живой Debian или рабочее место в кармане
    • Разбивка hdd

    Приложения:

    • Cat Excel files
    • Vim: меню выбора кодировок

    26 декабря

    citforum.ck.ua:

  • В Блогометках открыты разделы:
    • Софт для Windows
    • Сети и Интернет
  • dwm. От статики к динамике
  • Установил Solaris
  • Новая Дискуссия:
    • Нужен ли русский Linux?

    19 декабря

  • SQL Anywhere: встраиваемая СУБД
  • citforum.ck.ua:

  • В разделе Блогометки появились рубрики:
    • Десктопы
    • Приложения
    • Системы
  • Подробно о разделе: Блоги и блогометки: открываем сезон промывки
  • 13 декабря

    CitCity:

  • Microsoft и Барселона: сюрреализм?
  • citforum.ck.ua:

  • Открыт новый раздел Блогометки
  • ZFS в подробностях. 1. Былое и ныне
  • 5 декабря

  • Архитектура предприятия: основные определения
  • Архитектуры для государственных ведомств. Примеры
  • Обзор журнала Computer:

  • Высокопроизводительные встроенные системы
  • citforum.ck.ua:

  • Продолжение цикла Linux для начинающих:
    • Пользовательские интерфейсы
    • Файлы
    • Системы настройки

    Все публикации >>>




IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей Пресс-релизы -
Послать комментарий
Информация для авторов
Rambler's Top100 хостинг от .masterhost This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2007 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...