Большой архив статей, книг, документации по программированию, вебдизайну, компьютерной графике, сетям, операционным системам и многому другому
 
<Добавить в Избранное>    <Сделать стартовой>    <Реклама на сайте>    <Контакты>
  Главная Документация Программы Обои   Экспорт RSS E-Books
 
 

  Раздел: Компьютерная документация -> Программирование -> Delphi / Pascal

 

Упрощаем работу с потоками (TStream)

Работа программиста невозможна без работы с данными, которые хранятся в файлах или в памяти. В Delphi введен механизм потокового ввода-вывода, значительно упрощающий наш нелегкий труд. Однако структура данных может быть достаточно сложна. К тому же, в разных проектах она наверняка будет различна. Все это заставляет нас снова и снова писать сотни строчек однообразного кода записи/чтения данных. Утомляет. В этой я покажу, как я решил эту проблему для себя.

Совсем немного теории

Для тех, кто знает, что скрывается за страшной аббревиатурой RTTI, рекомендую пропустить этот раздел, ничего нового Вы здесь не найдете. А остальных попытаюсь немного ввести в курс дела.

RTTI (Run-time type information) - как видно из названия, это механизм, позволяющий определить тип данных во время выполнения. Суть его в том, что компилятор генерирует расширенную информацию для почти всех классов, используемых в вашей программе. Я сказал почти? Да, только для классов, объявленных с директивой {$M+} и их потомков, а таким классом, в частности является TPersistent. Потомками этого класса являются все компоненты, графические классы (TFont, TBitmap, TIcon и т.д.) и многие другие. Так вот, я отвлекся, эта информация активно используется самой средой разработки (инспектор объектов, редакторы свойств) и может быть использована программистом. Необходимые средства для работы с RTTI находятся в модуле TypInfo.pas. Проблема лишь в том, что по неизвестным мне причинам, Borland решила не документировать эти возможности (в справке по Delphi7, не нашел ничего связанного с RTTI, кроме упомянутой ранее директивы {$M+/-}, метода TObject.ClassInfo и операторов is и as).

И еще: RTTI позволяет получить информацию о свойствах и методах, объявленных ТОЛЬКО в разделе published. Зачем нам это нужно и как это нам поможет - увидите далее.

Ставим задачу

Поставим себе такую задачу: создать класс, который будет искать все свои published-свойства и сохранять их в поток (в файл, в частности). Программисту нужно только ОДИН раз написать код, который реализует сказанное выше, создать потомка этого класса, объявить в нем все необходимые свойства, и вызвать метод SaveToStream (его не надо будет перекрывать для каждого потомка) для сохранения самого себя в поток. Аналогично, метод LoadFromStream прочитает все свойства из потока. Ну-с, приступим-с.

Реализация

Класс TRttiObject
// Сохраняет и читает из потока все Published свойства
TRttiObject = class(TInterfacedPersistent, IStreamPersist)
public
  procedure SaveToStream(Stream: TStream);
  procedure LoadFromStream(Stream: TStream);
  constructor Create; virtual;

end;

Тут нужно немного пояснить. Класс будет записывать/читать все свойства, тип которых целый (в том числе логический), перечислимый, вещественный, символьный, строковый, а также некоторые классы, которые поддерживают работу с потоками. Отличить эти классы от других, можно запросив у них интерфейс IStreamPersist, который объявлен в Classes.pas так:

IStreamPersist = interface
  ['{B8CD12A3-267A-11D4-83DA-00C04F60B2DD}']
  procedure LoadFromStream(Stream: TStream);
  procedure SaveToStream(Stream: TStream);

end;

Этот класс реализуют, например, все потомки TGraphic, такие как TBitmap, TIcon, TMetafile, а также наш класс TRttiObject.

Почему в качестве предка выбран TInterfacedPersistent, а не TObject или TInterfacedObject. Тут несколько причин: во-первых, он является потомком TPersistent, который объявлен с директивой {$M+} (правда ничего не мешало бы сделать это самим), а во-вторых, в нем наиболее удачно для нас реализованы методы интерфейса IInterface (подсчет ссылок, реализованный в TInterfacedObject, нам ни к чему, а если взять TObject, то эти методы нужно будет реализовать самому).

procedure TRttiObject.SaveToStream(Stream: TStream);
var
  TypeData: PTypeData;
  PropList: PPropList;
  Count,i: Integer;

  // Локальные процедуры
  procedure WriteOrdProp;  // Запись целых и перечислимых данных
  var
    Value: Integer;
  begin
    Value:=GetOrdProp(self,PropList[i]);
    Stream.Write(Value,SizeOf(Value));
  end;

  procedure WriteFloatProp;  // Запись вещественных данных
  var
    Value: Extended;
  begin
    Value:=GetFloatProp(self,PropList[i]);
    Stream.Write(Value,SizeOf(Value));
  end;

  procedure WriteStringProp;  // Запись строки
  var
    Value: String;
    L: Integer;
  begin
    Value:=GetStrProp(self,PropList[i]);
    L:=Length(Value);
    Stream.Write(L,SizeOf(L));
    Stream.Write(PChar(Value)^,Length(Value));
  end;

  procedure WriteClassProp;  // Запись класса
  var
    Obj: TObject;
    SaveLoader: IStreamPersist;
    IsEmpty: Boolean;
  begin
    Obj:=GetObjectProp(self,PropList[i]);
    if (Obj is TGraphic) then begin
      IsEmpty:=TGraphic(Obj).Empty;
      Stream.Write(IsEmpty,SizeOf(Boolean));
    end;
    if Supports(Obj,IStreamPersist,SaveLoader) then begin
      SaveLoader.SaveToStream(Stream);
    end;
  end;

// Собственно сама процедура поиска свойств и записи
begin
  TypeData:=GetTypeData(ClassInfo);  // Получаем указатель на информацию
  Count:=TypeData.PropCount;  // Получаем количество свойств
  if Count>0 then begin
    // Выделяем память для списка свойств
    GetMem(PropList,SizeOf(PPropInfo)*Count);
    Try
      // Получаем список свойств
      GetPropInfos(ClassInfo,PropList);
      // Перебираем все свойства из списка и сохраняем их
      // в поток в соответствии с их типом
      for i:=0 to Count - 1 do begin
        case PropList[i].PropType^.Kind of
          tkEnumeration, tkInteger, tkChar, tkWChar: WriteOrdProp;
          tkFloat: WriteFloatProp;
          tkString, tkLString: WriteStringProp;
          tkClass: WriteClassProp;
        end;
      end;
    finally
      // Освобождаем память
      FreeMem(PropList,SizeOf(PPropInfo)*Count);
    end;
  end;
end;

Я не буду подробно комментировать каждую функцию из TypInfo.pas, по комментариям сами разберетесь, прошу только обратить внимание на локальную процедуру записи класса. Сначала мы получаем экземпляр самого объекта. Потом проверяем, не является ли он потомком TGraphic. Далее записываем в поток, является ли графический объект пустым. Дело в том, что если объект (например Bitmap) пустой, то вызов SaveToStream не запишет в поток ничего. При чтении объект не сможет узнать о том, что он должен быть пустым, и будет, как ни в чем не бывало читать следующие по очереди данные из потока. Само собой это вызовет ошибку. Честно говоря, мне не очень нравится, как я решил эту проблему. Если у Вас есть идеи получше - пишите в обсуждении статьи.

И в конце процедуры WriteClassProp самое главное. Запрашиваем интерфейс IStreamPersist и заодно проверяем, поддерживает ли вообще его объект. Если да, то вызываем метод интерфейса SaveToStream.

Процесс чтения свойств из потока аналогичен. Я не буду его рассматривать в статье, в прилагаемом примере вы его найдете и сами сможете разобраться.

Что дальше?

Вы думаете это все? Кроме класса TRttiObject в прилагаемом файле Вы найдете следующие классы:
TRttiList = class(TObjectList, IStreamPersist)

Предназначен для составления и управления списками объектов типа TRttiObject. Кроме того, поддерживает интерфейс IStreamPersist и может сам являться свойством объекта TRttiObject. Таким образом, вы можете составлять из этих классов сколь угодно сложные структуры, массивы, деревья и т.д.

TAsyncRttiList = class(TRttiList)

Тоже самое, но процесс сохранения и загрузки позволяет выполнять асинхронно, т.е. сразу возвращает управление, а когда процессы завершены, оповещает главный поток (Thread).

Вот теперь все. Надеюсь, эти материалы будут для Вас полезны и возможно у Вас появятся какие-либо идей, как исправить/дополнить их. Буду очень признателен!

Исходные коды (zip-архив, 2.3 K) (обновление от 3/27/2006 2:10:00 AM)

Автор: Юрий Спектор
Источник: www.delphikingdom.ru

Ссылки по теме
Алгоритмы Сортировки. Часть 1
Введение в Delphi 8
Работа с реестром в Delphi
Delphi и ресурсы компьютера
Советы начинающим программировать на Delphi
Структуры и базы данных, методы сортировки

Вся документация Pascal и Delphi

 

Компьютерная документация от А до Я - Главная

 

 
Интересное в сети
 
10 новых программ
CodeLobster PHP Edition 3.7.2
WinToFlash 0.7.0008
Free Video to Flash Converter 4.7.24
Total Commander v7.55
aTunes 2.0.1
Process Explorer v12.04
Backup42 v3.0
Predator 2.0.1
FastStone Image Viewer 4.1
Process Lasso 3.70.4
FastStone Image Viewer 4.0
Xion Audio Player 1.0.125
Notepad GNU v.2.2.8.7.7
K-Lite Codec Pack 5.3.0 Full


Наши сервисы
Рассылка новостей. Подпишитесь на рассылку сейчас и вы всегда будете в курсе последних событий в мире информационных технологий.
Новостные информеры. Поставьте наши информеры к себе и у вас на сайте появится дополнительный постоянно обновляемый раздел.
Добавление статей. Если вы являетесь автором статьи или обзора на тему ИТ присылайте материал нам, мы с удовольствием опубликуем его у себя на сайте.
Реклама на сайте. Размещая рекламу у нас, вы получите новых посетителей, которые могут стать вашими клиентами.
 
Это интересно
 

Copyright © CompDoc.Ru
При цитировании и перепечатке ссылка на www.compdoc.ru обязательна. Карта сайта.