Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2011.03.06;
Скачать: CL | DM;

Вниз

Очередной велосипед   Найти похожие ветки 

 
VLK32   (2010-11-27 17:02) [0]

Т.е. сериализация. Для меня основной вопрос в этом деле - Как сохранять ссылки? Если диаграмма объектов (т.е. во время исполнения) довольно сложная с кучей перекрестных ссылок то как это все дело выгружать/загружать. Я тут подумал и реализовал такую методу.

В базовом классе TClass32 (от которого порождаются все ост.классы кот. мы хотим сериализовать) есть имя экземпляра. В рамках  заданного класса оно уникально. И есть спец. класс TClassPool который в списке хранит привязки ИмяОбъекта - АдресОбъекта.

При создании экземпляра TClass32 автоматически инфа добавляется в пул. Ну и стандартно базовый класс и все остальные поддерживают Load/Save в поток.

Сериализация выглядит вот так:

function GlobalSave(const C:array of TClassPool; const P:Pointer):Boolean;
 var i : Integer;
 begin
  Result := False;
  try                
   if (Length(C)=0) or (P=Nil) then Exit;
   TMem32(P).Save(Length(C));
   for i:=0 to High(C) do C[i].SaveName(P);
   for i:=0 to High(C) do C[i].SaveData(P);
   Result := True;
  except
   // do nothing
  end;
 end;

function GlobalLoad(const C:array of TClassPool; const P:Pointer):Boolean;
 var i : Integer;
 begin
  Result := False;
  try
   if (Length(C)=0) or (P=Nil) then Exit;
   TMem32(P).Load(i);
   if i<>Length(C) then Exit;
   for i:=0 to High(C) do C[i].LoadName(P);
   for i:=0 to High(C) do C[i].LoadData(P);
   Result := True;
  except
   // do nothing
  end;
 end;

где P это указатель на поток.

При выгрузке. Сначала все сериализуемые пулы выгружают в поток имена своих классов и имена экземпляров. Потом пулы выгружают данные экземпляров используя метод Load последних.

При загрузке. Сначала все указанные пулы считывают имена экземпляров и создают их без загрузки внутренних данных. Примерно такой код:

function TClassPool.LoadName(const P:Pointer):Boolean;
 var i,n : Integer;
     S : String;
 begin
  Result := False;
  if P=Nil then Exit;
  try
   TMem32(P).Load(S);
   if AnsiUpperCase(S)<>AnsiUpperCase(FClass.ClassName) then raise Exception.Create("Incorrect Class Name");
   TMem32(P).Load(n);
   for i:=0 to n-1 do
    begin
     TMem32(P).Load(S);
     FClass.Create(S,Self);
    end;
   Result := True;
  except
   // do nothing
  end;
 end;

После этого все экземпляры всех пулов созданы, т.е. уже сущ. в оперативной памяти и во всех пулах иниц. привязка имен к адресам.

Дальше после for i:=0 to High(C) do C[i].LoadData(P); объекты грузят свои данные. А так как в поток вместо ссылок они записывали имена объектов то теперь они могут точно восстановить все свои ссылки запрашивая инфу из пулов.

В программе загрузка и выгрузка выглядит так:

M := TMem32.Create;
GlobalSave([TPlayer.Pool,TGroup.Pool],M);
M.SaveToFile(GetCurrentDir+"\local.dat");
M.Free;

M := TMem32.Create;
M.LoadFromFile(GetCurrentDir+"\local.dat");
GlobalLoad([TPlayer.Pool,TGroup.Pool],M);
M.Free;

Основная нагрузка на кодера ложится в программировании методов Save и Load, но надстройка  сериализации как видите довольно проста. Это хранилища инфы об экземплярах (запись и удаление инфы происходит автоматом и реализована в базовом классе) и две функции.

Прошу высказывать свои мнения по данной методе.


 
VLK32   (2010-11-27 17:09) [1]

Забыл добавить. TClassPool может быть пулом для любого класса порожденного от базового TClass32.


 
VLK32   (2010-11-27 17:20) [2]

Ну и сам   TClass32 = class(TObject)

  protected

   function  GetDataSize:Integer; virtual;
   function  GetMark:Integer; virtual;
   function  GetName:String; virtual;
   function  GetOwner:Pointer; virtual;
   procedure SetMark(const Value:Integer); virtual;
   procedure SetOwner(const Value:Pointer); virtual;
   procedure SetName(const Value:String); virtual;

  public

   function Clear:Boolean; virtual; abstract;
   function Copy(const P:Pointer):Boolean; virtual; abstract;
   function Save(const P:Pointer):Boolean; virtual; abstract;
   function Load(const P:Pointer):Boolean; virtual; abstract;

   constructor Create(const ObjectName:String=""; const Pool:TClassPool=Nil); virtual;

   property Owner : Pointer    read GetOwner   write SetOwner;
   property Mark : Integer     read GetMark    write SetMark;
   property Name : String      read GetName    write SetName;
   property DataSize : Integer read GetDataSize;

 end;

Как видим ни одного дополнительного байта по сравнению с TObject


 
Kerk ©   (2010-11-27 17:45) [3]

Ну так варианта только два. Либо вместо ссылок хранить тестовые идентификаторы, либо вместо ссылок на объект в памяти хранить ссылки на описание объекта в файле. А который из них лучше нужно конкретно по задаче смотреть.


 
VLK32   (2010-11-27 17:54) [4]

На самом деле вариант один - хранить имена. Исходя из этого я и сделал надстройку сериализации в виде объектов-пулов хранящих привязки имен объектов к адресам. Да это дополнительная память но ее нужно не так много. Кроме того сразу доступна базовая функциональность по управлению экземплярами во время исполнения. Очень удобно получать ссылку на объект по его имени в любой момент когда это понадобиться.

Хочу также заметить что порядок выгрузки/загрузки у меня произволен. Т.е. не имеет значения реальная последовательность создания экземпляров во время исполнения.



Страницы: 1 вся ветка

Текущий архив: 2011.03.06;
Скачать: CL | DM;

Наверх




Память: 0.48 MB
Время: 0.007 c
2-1292062969
rish
2010-12-11 13:22
2011.03.06
Integrated debugging


6-1235493550
d@vinchi
2009-02-24 19:39
2011.03.06
Получение данных TCPServer


2-1292090673
Анонимус
2010-12-11 21:04
2011.03.06
Вопрос про сплиттер, Delphi 7


15-1290612541
alexdn_
2010-11-24 18:29
2011.03.06
Почта


15-1290862182
MonoLife
2010-11-27 15:49
2011.03.06
900 терабайт на 1 г бактерий..