AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Referzen in ein Stream speichern

Ein Thema von Geri · begonnen am 14. Sep 2006 · letzter Beitrag vom 15. Sep 2006
Antwort Antwort
Seite 1 von 3  1 23      
Geri

Registriert seit: 23. Nov 2004
Ort: Feldkirch
47 Beiträge
 
Delphi 2005 Personal
 
#1

Referzen in ein Stream speichern

  Alt 14. Sep 2006, 20:03
Hallo zusammen

Ich habe wieder einmal eine Frage für die Expterten hier. Wie der Titel schon sagt, möchte ich Referenzen von Objekten in ein Stream speichern.

Geht das?
Falls ja, bitte wie?

Zum besseren Verständnis des Problems habe ich ein Programmgerüst der Objekte erstellt die gespeichert werden sollen.

TPoint ist ein Punkt mit zwei Koordinaten
TKante stellt die Verbingung zwischen zwei Punkten dar

Daraus entsteht schliessliche ein Netz oder ein Graph der gespeichert werden soll.
Delphi-Quellcode:


TPoint = Class
     mx,my:Integer;
     constructor Create(x1,y1:Integer);
     Constructor Save(S:Stream);
     Constructor Load(s:TStream);
    End;
    
    
TKante = Class
     mP1,mP2:TPoint;   
     constructor Create(P1,P2:TPoint);
     Constructor Save(S:Stream);
     Constructor Load(s:TStream);
   End;

TGraph = class
     PunkteListe:TList;
     KantenList:TList;
     constructor Create();   
    end;

constructor TPoint.create(x1,y1:Integer);
Begin
   mx:=x1;
   my:=y1;
End;
    
Constructor TPoint.Save(S:Stream);
Begin
   s.Write(mx,sizeof(mx));
   s.Write(my,sizeof(my));
End;

Constructor TPoint.Load(s:TStream);
Begin
   s.read(mx,sizeof(mx));
   s.read(my,sizeof(my));
End;

constructor TKante.create(P1,P2:TPoint);
Begin
   mP1:=P1;
   mP2:=P2;
End;   

Constructor TKante.Save(S:Stream);
Begin
   ???
End;

Constructor TKante.Load(s:TStream);
Begin
   ???
End;
         
    
constructor TGraph.Create();
var P1,P2:TPoint;
   K:TKante;
Begin
   PunkteListe:=TList.Create;
   
   // erzeuge 10 zufällige Punkte
   for i:=0 TO 9 Do
   Begin
      P:=TPoint.create(Random(100),Random(100));
      PunkteListe.Add(p);
   End;   
   
   Erzeuge 4 zufällige Kanten
   For j:=0 TO 4 Do
   Begin
      Kantenliste:=TKante.Create(l[Random(9)],L[Random(9));
   End;    
   
End;
 
constructor TGraph.LoadFromstream();   
Begin
  // lade Punkte ??
  // lade Kanten ??

End;
Vielen Dank für Eure Tipps

Geri
Gerhard
  Mit Zitat antworten Zitat
14. Sep 2006, 20:05
Dieses Thema wurde von "Dax" von "Open-Source" nach "Sonstige Fragen zu Delphi" verschoben.
Das ist kein OS-Code..
Benutzerbild von 3_of_8
3_of_8

Registriert seit: 22. Mär 2005
Ort: Dingolfing
4.129 Beiträge
 
Turbo Delphi für Win32
 
#3

Re: Referzen in ein Stream speichern

  Alt 14. Sep 2006, 20:06
Das bezeichnet man als Objektpersistenz.

Da gibt es die dpCollection (mal suchen im Forum) und JEDI Obiwan, ein Objektpersistenzframework.
Manuel Eberl
„The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.“
- Terry Pratchett
  Mit Zitat antworten Zitat
Geri

Registriert seit: 23. Nov 2004
Ort: Feldkirch
47 Beiträge
 
Delphi 2005 Personal
 
#4

Re: Referzen in ein Stream speichern

  Alt 14. Sep 2006, 20:22
Hallo Manuel

Vielen Dank für den hilfreichen Hinweis. Ich habe mir dpCollection nun einmal herunter geladen.

Mir ist aber einfach nicht klar wie ich sie auf meinen Fall anwenden muss.

Hast du hieb bitte evtl. auch noch so einen guten Tipp, oder ein einfaches Beispiel in dem die Technik gezeigt wird?

Vielen Dank nochmals

Geri
Gerhard
  Mit Zitat antworten Zitat
Benutzerbild von 3_of_8
3_of_8

Registriert seit: 22. Mär 2005
Ort: Dingolfing
4.129 Beiträge
 
Turbo Delphi für Win32
 
#5

Re: Referzen in ein Stream speichern

  Alt 14. Sep 2006, 20:24
Nein, ich habe sie noch nie verwendet. Einfach weiter suchen.
Manuel Eberl
„The trouble with having an open mind, of course, is that people will insist on coming along and trying to put things in it.“
- Terry Pratchett
  Mit Zitat antworten Zitat
BenjaminH

Registriert seit: 14. Okt 2004
Ort: Freiburg im Breisgau
713 Beiträge
 
Turbo Delphi für Win32
 
#6

Re: Referzen in ein Stream speichern

  Alt 14. Sep 2006, 20:56
Damit wird das ganze ganz schnell ganz einfach:
http://www.delphipraxis.net/internal...l+dpcollection
Benjamin
  Mit Zitat antworten Zitat
Geri

Registriert seit: 23. Nov 2004
Ort: Feldkirch
47 Beiträge
 
Delphi 2005 Personal
 
#7

Re: Referzen in ein Stream speichern

  Alt 14. Sep 2006, 20:59
Hallo Benjamin

Vielen Dank für Deine Mühe.

Auf der Suche habe ich auch noch diesen Link gefunden: Link zu einem Beispiel

Ganz schlau bin ich aber trotzdem noch nicht geworden. Insbesondere weil ich Einträge in einem Tlist-Objekt speichern möchte.

Beste Grüsse

Geri
Gerhard
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#8

Re: Referzen in ein Stream speichern

  Alt 14. Sep 2006, 21:43
Hi,

du kannst das auch selber machen.

Der Aufbau deines Stream sieht immer so aus:

Klassenname als String
Daten zu diesem Objekt
Klassenname als String
Daten zu diesem Objekt

Dh. du speicherst in deinem Stream auch die Klassenname -> Self.ClassName als ShortString -> String der nur 255 Zeichen speichern kann, dafür in String[0] die Länge.

Fehlt noch der Punkt wie man von einem solchen String zurück zu einer Klasse kommt die man instanzieren kann. Dazu gehts du exakt so vor wie die VCL. Diese verwaltet eine TList -> RegisteredClasses und zwei Prozeduren -> RegisterClasses(Classes: array of TClass) und Funktion GetClass(const ClassName: String): TClass;

In deinem Code rufst du nun RegisterClasses([TPoint, TKante, TFlaeche]) in der Initialization Sektion auf.
Deine Stream-Leses-Routine liest erstmal einen ShortString aus dem Stream. Dann wird mit GetClass(NameAusDemStream).Create eine Instance erzeugt. Dieser Instanze übergibst du die Kontrolle des Streams damit sie nun ihre Daten auslesen kann.

Alle deine Klassen sollten von eniner Basisklasse abgeleitet sein, zb. TMyBaseClass.


Delphi-Quellcode:

type
  TMyBaseClass = class
  protected
    procedure LoadFromStream(Stream: TStream); virtual;
  end;
  
  TPoint = class(TMyBaseClass);
  TKante = class(TMyBaseClass);

procedure LoadObjects(List: TList; Stream: TStream);

  function ReadClassName: ShortString;
  begin
    Stream.Read(Result[0], SizeOf(Result[0]));
    Stream.Read(Result[1], Ord(Result[0]));
  end;

var
  Instance: TMyBaseClass;
begin
  while Stream.Position < Stream.Size do
  begin
    Instance := (GetClass(ReadClassName) as TMyBaseClass).Create;
    Instance.LoadFromStream(Stream);
    List.Add(Instance);
  end;
end;

procedure SaveObjects(List: TList; Stream: TStream);
var
  I: Integer;
  S: ShortString;
begin
  for I := 0 to List.Count -1 do
    with List[I] as TMyBaseClass do
    begin
      S := ClassName;
      Stream.Write(S[0], Ord(S[0]) +1);
      SaveToStream(Stream);
    end;
end;
  
initialization
  RegisterClasses([TPoint, TKante, TFlaeche,...]);
end.
Ist natürlich nur ein grobes Beispiel und man kann noch einiges verbessern. Statt zb. den langen Klasssennamen zu speichern köntest du auch zu jeder Klasse eine eigene ID erzeugen und diese dann als Byte,Word oder LongWord abspeichern. Das macht deinen Stream kompakter, besonders falls du tausende solcher Objekte speichern möchtest.

Bei einer meiner Klassen (hierarisch, sortierter Node Baum) mache ich es so das ich als erstes alle Klassennamen der verwendeten Objekte in den Stream speichere. Diese Liste dient nun als Lookuptabelle um einen Klassennamen=Klasse eine ID zu geben. Diese ID ist nichts anderes als der Index in diese Liste. Man speichert also zb. einen Baum aus 10000 Objekten mit 10 unterschiedlichen Klassen so das man als erstes eine sortierte Liste aus Strings=Klassenamen in den Stream speichert. Das sind also 10 Strings. Die nachfolgenden Klassen haben dann eine 1 Byte ID, der Index in diese Liste an der der Klassenname steht.
Somit sparst du viel Speicherplatz, weist von Anfang an welche Klassen im Stream gespeichert sind, und zur Laufzeit kannst du die Klasse einfach in dieser Liste direkt nachschlagen, ein Objeckt wird ja über seinen Byte-Index aus dem Stream identifiziert.

Die Verweise auf dpCollection sind gut gemeint, aber ich meine das du mit wenigen Zeilen Source selber schneller bist und auch was dabei lernst.

Gruß Hagen
  Mit Zitat antworten Zitat
Geri

Registriert seit: 23. Nov 2004
Ort: Feldkirch
47 Beiträge
 
Delphi 2005 Personal
 
#9

Re: Referzen in ein Stream speichern

  Alt 14. Sep 2006, 22:17
Hallo Hagen

Vielen Dank für Dein tolles (kurzes und sehr gut nachvollziehbares) Beispiel und die detaillierte Beschreibung!! Super!

Ich denke, nun ist mir klar, wie ich eine TListe speicher. Wie schreibe ich aber das Objekt Kante in ein Stream. Sind ja nur die Pointer auf die Punkte gespeichert, die in einer anderen Liste gespeichert sind.
Delphi-Quellcode:
TGraph = class
     PunkteListe:TList;
     KantenList:TList;
     constructor Create();
    end;

Procedure TPoint.Store(s:TStream)
Begin
  S.Write(x, SizeOf(x));
  S.Write(y, SizeOf(y));
End;

Procedure TPoint.Load(s:TStream)
Begin
  S.Read(x, SizeOf(x));
  S.Read(y, SizeOf(y));
End;

Procedure TPoint.Load(s:TStream)
Begin
  S.Read(x, SizeOf(x));
  S.Read(y, SizeOf(y));
End;

Procedure TKante.Store(s:TStream)
Begin
  S.Write( ...schreibe Referenz von P1 in das Stream???
  S.Write( ...schreibe Referenz von P2 in das Stream???
End;

Procedure TKante.Store(s:TStream)
Begin
  S.Read( ...lies Referenz von P1 in das Stream???
  S.Read( ...lies Referenz von P2 in das Stream???
End;

constructor TGraph.Create();
var P1,P2:TPoint;
    K:TKante;
Begin
   PunkteListe:=TList.Create;
    
   // erzeuge 10 zufällige Punkte
   for i:=0 TO 9 Do
   Begin
      P:=TPoint.create(Random(100),Random(100));
      PunkteListe.Add(p);
   End;
    
   // Erzeuge 4 zufällige Kanten
   For j:=0 TO 4 Do
   Begin
      Kantenliste:=TKante.Create(l[Random(9)],L[Random(9));
   End;
End;
Wenn ich Deinen Code compiliere, dann erhalte ich ein paar Meldungen
Delphi-Quellcode:
...
  Instance := (GetClass(ReadClassName) as TMyBaseClass).Create; //[Fehler] uHagen.pas(50): E2015 Operator ist auf diesen Operandentyp nicht anwendbar



...
      with List[I] as TMyBaseClass do // Operator nicht anwendbar

      SaveToStream(Stream); // wennn ich richtig verstanden habe, dann werden hier z.B. die Koordinaten für einen Punkt gespeichert. Wie speichere ich aber bitte die Kantenliste?
Vielen Dank nochmals und beste Grüsse

Geri
Gerhard
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#10

Re: Referzen in ein Stream speichern

  Alt 14. Sep 2006, 22:55
Ok, zuerst zu den Fehler:

Mein obiges Beispiel ist nicht getestet sondern eben nur ein Beispiel das mal eben reingehämmert habe. Solche Feinheiten sind somit deine Sache

Zum Problem der verlinkten Objekte:

Es gibt mehrere konzeptionelle Ansatzpunkt. Einmal könntest du in TMyBaseClass.SaveToStream() nur den Klassennamen abspeichern. Alle davon abgeleiteten Klasse müsen dann intern in ihrer überschriebenen Methode .SaveToStream() als erstes inherited SaveToStream() aufrufen. Das ist sowieso eine gute Idee. Nachteilig dabei ist der Punkt das wenn wir zb. von TPoint eine TPoint3D ableiten nun im Stream auch der Klassenname von TPoint drinnen stehen würde.

Also wird man mit 2 Methoden arbeiten müssen. .SaveToStream() als statische Methode in TMyBaseClass und .SaveData(Stream) als virtuelle Methode die TPoint etc. pp. überschreiben müssen. TMyBaseClass.SaveToStream() schreibt erstmal den Klassennamen in den Stream und ruft danach .SaveData(Stream) auf. Methode .SaveData() speichert die Daten des jeweiligen Objektes. Sollte diese Klasse zb. TKante sein so ruft sie auch noch FStartPoint.SaveToStream() und FStopPoint.SaveToStream() auf.

Bei .LoadFromStream() muß man dann einige Änderungen machen. Man könte .LoadFromStream() so deklarieren

  class function TMyBaseClass.LoadFromStream(Stream: TStream): TMyBaseClass; Diese Funktion lädt erstmal den Klassennamen aus TStream, erzeugt dann in Result eine Instanz von dieser Klasse und ruft Result.ReadData(Stream) auf. Man hat also nur in TMyBaseClass() eine Klassenmethode .LoadFromStream() und in den davon abgeleiteten Klassen überschreibt man dann .ReadData() um die eigentlichen Daten zu laden. Dein TKante Objet würde dann so arbeiten:

Delphi-Quellcode:
procedure TMyBaseClass.SaveToStream(Stream: TStream); static;
begin
  Stream.WriteString(ClassName);
  SaveData(Stream);
end;
 
procedure TMyBaseClass.SaveData(Stream: TStream); dynamic;
begin
// nothing todo
end;

class function TMyBaseClass.LoadFromStream(Stream: TStream): TMyBaseClass;
var
  Name: String;
  Class: TClass;
begin
  Name := Stream.ReadString;
  Class := GetClass(Name);
  if Class = nil then
     raise Exception.CreateFmt('Klasse "%" ist nicht registriert.', [Name]);
  if not (Class is TMyBaseClass) then
    raise Exception.CreateFmt('Klasse "%s" ist nicht von TBaseClass abgeleitet', [Name]);
  Result := TMyBaseClass(Class).Create; // .Create in TMyBaseClass muß virtuell deklariert sein !!
  Result.ReadData(Stream);
end;

procedure TMyBaseClass.ReadData(Stream: TStream); dynamic;
begin
end;

prcoedure TKante.SaveData(Stream: TStream); override;
begin
  Stream.Write(MeineDaten,...);
  FStartPoint.SaveToStream(Stream);
  FStopPoint.SaveToStream(Stream);
end;

procedure TKante.ReadData(Stream: TStream); override;
begin
  Stream.Read(MeineDaten, ...);
  FStartPoint := TMyBaseClass.LoadFromStream(Stream) as TPoint;
  FStopPoint := TMyBaseClass.LoadFromStream(Stream) as TPoint;
end;
.LoadFromStream() ist dann quasi ein Constructor und der Typcast mit "as TPoint" stellt sicher das keine falsch TMyBaseClass in deinen Feldern landet.


Oben sprach ich meine TNode Objekte an. Diese sind ebenfalls von TObjet abgeleitet. Das bedeutet das wir nun nichtmehr das Streaming-System der VCL benutzen können wie bei TComponent Klassen gewohnt. Denn diese machen das exakt genauso. Schue dir mal eine DFM Datei als Text an.

Um denoch meine TNode Objekte so abspeichern zu können wie die VCL habe ich eine TNodeStreamingComponent von TComponent abgeleitet. Beim Laden und Speichern wird also ein TNode Objekt innerhalb dieser Klasse gekapselt als Property. Das hat den riesigen Vorteil das man in den eigenen Objekten mit published Properties arbeiten kann und das VCL Streaming System diese Properties speichert und lädt. Das ist so wie bei TFont, TBrush, TPen Objekten die als Properties einer TComponent published wurden. Eine solche Klasse kann ansich nicht mit dem VCL Streaming gespeichert werden, sondern immer nur als Eigenschaft einer Komponente.

Der Vorteil ist dann:
Delphi-Quellcode:
type
  TmyNode = class(Tnode)
  private
    FX,FY: Integer;
  published
    property X: Integer read FX write FX;
    property Y: Integer read FY write FY;
  end;
Das reicht schon aus damit mein TNodeStreamingComponent, das eine TMyNode KLasse in sich kapselt, per VCL System die beiden Properties X und Y selbständig speichern und laden kann. Man muß also nicht mehr selber ständig diese Properties speichern und laden, sondern die bloße Deklaration als published read/write Properties reicht schon aus.

Ich weis, ich mache dich heis und lasse dich ohne Sourcen im Regen stehen Allerdings greift mein "Trick" in die Untiefen des VCL Streamings + TComponnet ein, und das müsstes du dann auch verstanden haben. Hier habe ich leider nicht die Zeit, den Platz und das Recht alles ganz genau aufzuzeigen. (Recht deshalb weil es ein kommerzielles Produkt ist).

Versichern kann ich dir aber das es über diesen Trick definitiv funktioniert. Immerhin der originale Erstsource lief schon in Delphi 1 und wurde mit der Zeit bis nach Delphi 7 portiert, OHNE das es Inkompatibilitäten in den gespeicherten Streams gab. Es geht also.

Gruß hagen
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 3  1 23      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:15 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz