AGB  ·  Datenschutz  ·  Impressum  







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

ClassType in ein Stream Speichern

Offene Frage von "Geri"
Ein Thema von Geri · begonnen am 14. Jan 2008 · letzter Beitrag vom 16. Jan 2008
Antwort Antwort
Seite 1 von 2  1 2      
Geri

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

ClassType in ein Stream Speichern

  Alt 14. Jan 2008, 12:22
Hallo zusammen

Ich möchte verschiedene Objekte in ein Stream speichern. Zu diesem Zweck schreibe ich den Klassenstyp als Zusatzinformation in den Stream. Beim Auslesen wird der Klassentyp in einer case ermittelt und die enstpechenden Load-Routine aufgerufen. Das Ganze sieht als Beispiel ungefähr so aus:

Delphi-Quellcode:
Procedure TPoint.Store(s:TStream);
var ct:TClass;
Begin
   ct:=self.ClassType;
   s.write(ct,sizeo(ct));
   s.write(P1,SizeOf(P1));
End;

// eine abgeleitete von TPont Klasse
Procedure TLine.Store(s:TStream);
var ct:TClass;
Begin
   inherited store(s);
   s.write(P2,sizeof(P2);
End;

//.. und dann dazu noch die Load-Routinen als Konstruktoren
Constructor TPoint.load(s);
Begin
  inherited create();
  s.read(P1,sizeof(P1));
end;

Constructor TLine.load(s);
Begin
  inherited Load(s);
  s.read(P2,sizeof(P2));
end;


// lade eine Objektliste aus einem Stream
Procedure Form1.LoadFromFile(Filename:String);
var ct:TClass;
    Point:TPoint
Begin
  Stream:=TStream.Create(FileName,....);
  Stream.Read(ct,Sizeof(Ct));
  if ct = TPoint then ObjectList.Add(TPoint.Load(Stream)) else
  if ct = TLine then ObjectList.Add(TLine.Load(Stream)) else
   memo1.lines.add('unknown object');
End;

Das Laden und Speichern von verschiedenen Objekten in ein File funktioniert sporadisch. Das Problem liegt bei der Ermittlung des Klassentyps. Wenn ich in Loadfromfile auf die Zeilte "if ct=TPoint" einen Breakpoint setze dann erkennt Delphi manchmal die Klasse und zweigt sie im ToolTip als Text, manchmal steht im ToolTip nur ein Hexwert drin.


Nun meine Fragen:
1.) Kann es sein, dass sich die ID für den Klassentyp laufende ändert?
2.) Andert sich der Klassentyp vielleicht nur nach einer Codemodifikation und erneutem Compilieren?

Vielen Dank für Eure Infos

Geri
Gerhard
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#2

Re: ClassType in ein Stream Speichern

  Alt 14. Jan 2008, 13:36
1. Das ist keine ID sondern ein Zeiger auf die RTTI und...
2. Ja, die liegt da, wo der Compiler die grad lustig hingepackt hat.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Geri

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

Re: ClassType in ein Stream Speichern

  Alt 14. Jan 2008, 14:29
Hallo Sirius

Aha, vielen Dank für Deine Infos. Die genannte Vorgehensweise wäre meiner Ansicht nach allerdings eine sehr schöne Lösung. Den Klassennamen selbst zu speichern führt zu Performanceeinbussen, wenn man z.B. merhrere Tausend Objekte "schnell" laden möchte.

Eine Lösung, die aber aufwändige zum Warten ist wäre eine Unit mit Konstanten anzulegen und für jede neue Klasse ein ID zu vergeben.

Gibt es hier vielleicht noch eine elegantere Lösung?


Freundliche Grüsse und nochmals vielen Dank für Deine Hilfe

Geri
Gerhard
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#4

Re: ClassType in ein Stream Speichern

  Alt 14. Jan 2008, 14:44
Implementiert ist eine ID o.ä. in TObject nicht. Was hast du denn für Zeitprobleme das Classname zu lange dauert?
Du kannst ja noch eine Elternklasse vor alles setzen, welche im Constructor aus dem Classname eine ID errechnet. Und gleichzeitig kannst du der auch noch eine Klassenmethode mitgeben, welche das Instanzieren des richtigen Objektes erledigt.
Aber um die Verwendung von Classname kommst du meines Wissens nicht herum.

Edit: wobei mir grad einfällt, dass sich eine ausgerechnete ID nicht lohnt. Die kann man ja nicht zurückrechnen. Aber wenn du alles in eine Klassenmethode einer Elternklasse packst. Also die Zuordnung ID<->Classname, dann dürfte ist die Wartbarkeit nicht unnötig erschwert.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Geri

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

Re: ClassType in ein Stream Speichern

  Alt 14. Jan 2008, 15:04
Hallo Sirius

Vielen Dank nochmals. Die Idee mit dem Generieren eines Keys ist mir auch schon in den Sinn gekommen. Die Implementation in der Basisklasse aus dem Klassenname wäre bestimmt sinnvoll. Damit liessen sich auch Klassen, welche nicht voneinander abgeleitet sind gut implementieren.

Vielleicht sollte ich auch mal einen Zeitmessung vornehmen. Vielleicht ist der Performaceverlust wirklich nicht so arg wie ich vermute

Werde mein Erkenntnisse dann noch posten.

Beste Grüsse und nochmals vielen Dank

Geri
Gerhard
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#6

Re: ClassType in ein Stream Speichern

  Alt 14. Jan 2008, 15:06
(Beachte obiges Edit )
Was ich mich grade frage ist, allerdings, dass du immer eine Zuordnungstabelle brauchts, egal ob ID oder String(ClassName)
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Geri

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

Re: ClassType in ein Stream Speichern

  Alt 14. Jan 2008, 16:06
Hallo Sirius

Vielen Dank für die Infos.

Ich meine schon, dass ich eine Möglichkeit zur Identfikation benötige. Die Daten stecken in einem File. Der Ladevorgang kann nach erneutem Aufruf des Programmes stattfinden.

Meine aktuelle Lösung sieht so aus, dass ich

Delphi-Quellcode:
Type TMyClassType = (
                             ctunknown,
                             ctPoint,
                             ctLine
                          );


type
  TPoint = class(TObject)
              mClassID:TMyClassType;
              Constructor Create();
              procedure Write(s:Stream);virtual;
           end;

Constructor TPoint.Create(s:TStream);
Begin
  inherited;
  mClassID:=ctPoint;
end;

Procedure TPoint.Write(s:TStream);
Begin
  s.write(mClassID,sizeof(mClassId); // schreibe den
End;
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: ClassType in ein Stream Speichern

  Alt 14. Jan 2008, 17:26
Du kannst den Stream in 2 Teile aufteilen. Am Ende des Streams wird ein Array of ClassName + ClassType gespeichert. Am Anfang stehen deine Objekte. Wenn du speicherst erzeugst du dynamisch ein Array of ClassName + ClassType. Ein Objekt schaut in diesem Array nach ob dort schon ein Record mit seinem ClassType oder ClassName drinnen steht. Wenn nicht wird ein neuer Record an's Array drangehangen. Der Index in dieses Array wird als Klassenindex im Stream gespeichert, also statt dem ClassType Zeiger oder ClassName. Wenn alle Objekte im Stream gespeichert wurden wird dieses Array noch gespeichert. Beim Laden wird zuerst dieses Array geladen. Dann werden die darin gespeicherten ClassName's benutzt um den aktuell gültigen ClassType Zeiger zu ermitteln. Hier im Forum findest du in der CodeLib eine Unit "EnumTypeInfo" mit der du das machen kannst. Nun werden die Objekte aus dem Stream geladen. Dabei lädst du den Klassenindex in das Array aus dem Stream und benutzt diesen zum direkten Zugriff auf das Array in dem ja der aktuelle Klassenname und ClassType drinnen steht. Im Array stehen also alle Klassen drinnen die im Stream gespeichert wurden. Zusätzlich hat das den Vorteil das der Stream kompakter wird als bei der Methode der VCL. Statt jedesmal den Klassennamen zu einem Objekt zu speichern und beim Laden jedesmal erst aus dem Klassennamen ein ClassType Zeiger zu machen, kostet bei der VCL Speicherplatz und Performance. Die Methode mit dem Array ist wesentlich effizienter und kostet effektiv weniger Speicherplatz.

Gruß Hagen
  Mit Zitat antworten Zitat
Geri

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

Re: ClassType in ein Stream Speichern

  Alt 15. Jan 2008, 01:11
Hallo Hagen

Schön, wieder mal Deine wertvolle Meinung zu lesen. Ich hoffe, es geht dir gut!

Bzgl. Deiner Lösung:
1.) Ich möchte ja keine visuellen Komponenten speichern. Deshalb sehe ich auch keine Ineffizienzen oder unnötige Speicherbelastung, wenn ich hergehe und jeder Objektklasse eine eindeutige ID zu deren Identfikation zuweise und diese direkt in der store-Routine "mitspeichere".

2.) Du schreibst "Classname + ClassTye": Wieso bitte beides speichern - oder handelt es sich beim "+" eigentlich um ein "oder"? Im Prinzip müsste es eigentlich ausreichen, wenn man lediglich vordefinierte IDs zu den Klassen speichert.
Ganz toll wäre dann wenn folgendes ginge:

Delphi-Quellcode:
  Stream.Read(ClassArrayIdx,sizeof(ClassArrayIdx));
  ClassInfoAry[ClassArrayIdx].Load(Stream);
ClaassInfoAry wäre ein Array von Klassentypen - evtl. sogar polymorph. Wie das Konstrukt aussehen müsste, das weiss ich nicht aber hast du sogar etwas in dies Richtung gemeint?

3.) Die genannte Library habe ich nicht gefunden aber wieso benötigt man diese. Wenn man den Klassennamen aus dem Array liest, dann lässt sich daraus ja direkt die entsprechende Referenz zum Objekt finden oder habe ich hier etwas falsch verstanden?

4.) Dein Ansatz finde ich allgemein eine sehr gute Lösung. Besonders gut finde ich, dass für abgeleitete Klassen ja eigentlich kein zusätzlicher Code geschriebene werden muss, z.B. wenn neue Klassen hinzu kommen.

Hoffe, alles richtig verstanden zu haben.

Beste Grüsse und nochmals vielen Dank für Deinen Input

Geri
Allg: Bei meinem Programm möchte ich auch Referenzen auf weitere Objekte speicheren. Ich behelfe mir dazu mit diversen IDs. Mich wundert es aber schon, dass Delphi hier nicht bereits ein mächtiges Konzept zur Verfügung stelltn...
Gerhard
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

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

Re: ClassType in ein Stream Speichern

  Alt 15. Jan 2008, 11:07
Hi,

ich schätze mal das dir noch par Informationen fehlen wie Delphis Klassenkonzept funktioniert.

Dein ClassType ist ein Zeiger in das CodeSegment eines Modules, also EXE, DLL oder Package. Der Zeiger zeigt dabei auf die Klassenstruktur. Das ist ein komplexer Record der durch den Compiler anglegt wird, pro deklarierter Klasse, und er enthält die VMT, DMT, ClassName, RTTI usw. zu einer Klasse. Wenn du nun deine EXE, DLL, Package neu kompilierst dann erzeugt der Compiler jedesmal diese Klassenrecords neu und linkt sie mit allem Code, Daten usw. in die EXE,DLL,Package ein. Dabei verschieben siech die Addressen der Zeiger. Also wenn deine Klasse TLine.ClassType = $0040678a sein sollte ist es sehr wahrschenlich das bei Neukompilation dieser Zeiger auf $0040bbbb oder sonstwohin zeigen kann. Dh. der ClassType Zeiger ist absolut ungeeignet als eineindeutige und dauerhaft gültige Referenez für eine Klasse. Aber der ClassName ist relativ unveränderlich. Deshalb schreibt das VCL Streaming Sytem, das was DFM's speichert und lädt, statt eienr ID oder des ClassType Pointers einfach jedesmal den Klassennamen des Objektes in den Stream. Speichert man also 1000x TLine Objekte in einen VCL Stream so steht in dem Stream auch 1000x 'TLine" als String drinnen. Genau genommen #5 + 'TLine', die #5 ist die Längenangabe des Strings. Ist TLine ein leeres Objekt so würde der Stream also schon 7000 Bytes groß sein.

Nun, beim Laden passiert folgendes. Das VCL System liest den Klassennamen des Objektes das erzeugt werden soll. Hier also 'TLine'. Dann scheut es in einer Liste die aus ClassType Records besteht diesen Klassennamen nach. Man kann mit ClassType.ClassName auf den Klassennamen einer Klasse zugreifen -> siehe Klassenmethoden. Ergo geht Delphi diese Liste er registrierten Klassen durch (siehe GetClass() und RegisterClass()), und vergleich ClassType.ClassName mit Objekt.Klassenname_aus_Stream. Sobald einee Übereinstimmung gefunden wurde gibt die Funktion GetClass() diesen ClassType zurück, deklariert als TClass -> TClass ist ein Zeiger auf eine Klassenhierarchie und damit defakto ein Zeiger auf einen ClassType Record der die Root einer Klassenhierarchie bilden soll.

Damit das funktioniert muß man also einmalig alle Klassen beim System registrieren die man später aus einem Stream laden können möchte.

Statt nun wie die VCL einfach zu jedem gespeicherten Object immer den vollständigen Klassennamen zu speichern, präferiere ich also eine Stream-globale Index-Tabelle, ein Array das alle im Stream befindlichen Klassen, also deren Namen + ClassType, enthält und deren Indexe in dieses Array statt dem Namen real gespeichert werden. Das hat den Vorteil das der Stream kompakter wird. Beim Laden wird man zuerst dieses Array laden, dann alle ClassType Zeiger aktualisieren auf die real benutzen Zeiger in dem aktuellen Modul. Die gespeicherten ClassType Zeiger können ja absolut falsch sein und an eine beliebige Stelle im Codesegment zeigen. Man muß sie also so korregieren das sie an die Stelle im Codesegment zeigen an der sich auch die Klassendeklaration befindet mit dem gleichen Klassennamen. Dazu nimmt man also den Klassennamen aus dem Array[] und sucht die dazugehörige Klassenstruktur im CodeSegment. Es gibt 2 Wege das zu machen. 1. man geht wie die VCL vor und benutzt eine globale Liste von registrierten Klassen. Der Entwickler muß dann für alle Klassen RegisterClass() aufrufen damit das Streamingsystem aus einem String als Klassennamen ein TClass und damit ClassType Zeiger machen zu können. Das wäre der geplante und nachvollziehbar richtigere Weg, allerdings mit Arbeit verbunden. Der 2. Weg ist ein Trick von mir. Mit diesem Trick kann man über alle RTTI -> Run Time Type Information alle zum Prozess gehörigen Module itertieren. Dh. der Trick ermöglicht quasi eine Iteration über alle im Projekt compilierten und deklarierten Typen, zb. zu den Typen Integer, Byte, Char, TColor, TPanel, Enums, Mengen, Interfaces, Klassen usw. usw. werden RTTI Records im Modul = CodeSegement gespeichert. Man kann nun mit meinem Trick also auch alle im Projekt einglinkten Klassen iterieren und somit zu einem String als Klassennamen die Klasse = TClass = ClassType herausbekomen.
Und exakt das machen wir mit dem geladenen Array of ClassName + ClassType. Zu jedem dort gespeicherten ClassName ermitteln wir den korrekten ClassType Pointer. Wir suchen also pro Klassennamen nur EINMALIG die Class bzw. ClassType. Wennn wir 1000 'TLine' Objekte gespeichert haben so steht 1000x im Stream ein Word, Index in das Array, und am Ende steht ein Array das 'TLine' und einen 4 Bytes Zeiger enthält. Wenn wir Laden holen wir erstmal dieses Array, suchen vom String 'TLine' die korrekte Class: TClass -> ClassType, entweder aus einer eigenen Registrationsliste oder eben mit meinem Trick. Danach laden wir die Objekte. Per Index greifen wir auf's Array zu und holen von dort die korrekte TClass = ClassType. Wir suchen also pro Klasse im Stream nur einmalig die Klasse. Die VCL sucht dagegen JEDESMAL pro Objekt die Klasse.

Nun, dieses Verfahren hat im Grunde nur Vorteile. Es ist schneller, kompakter als die VCL. Gegenüber deinem ID basierten System musst du dich hier ich um die Eindeutigkeit der IDs kümmern, das machen wir über den Klassennamen usw. Zudem benötigt dieses System keinen aktiven Code in deinen Klassen, so wie dein ID System. Dein ID System muss nämlich explizit und manuell und permanent sauber durch den Programmierer gepflegt werden, und verursacht wenn es Fehler enthält massive Probleme bei der Wartung der Software. Man kann sich nämlich dumm&dusselig suchen wenn man nicht bemerkt das das ID-System auf Grund von mehrdeutigen IDs ganz andere Klassen geladen hat als das es sich laden sollte.

Unit mit Helperfunctions um alle RTTI->Run Time Type Infos eines oder aller Module zu einem Prozess zu iterieren:
http://www.delphipraxis.net/internal...065&highlight=

Gruß Hagen

Gruß Hagen
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


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 08:38 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