![]() |
Verständnisproblem Streams
Hallo!
Mir fällt auf, das eine Klasse, die einen Stream verwendet, nicht vom Programmierer bewußt registriert wird. In Turbo/Borland Pascal war das anders:
Delphi-Quellcode:
Nun meine Frage:
Unit StrmObj;
interface type TStreamableObject = object(TObject) constructor Init; constructor Load(var S: TStream); procedure Store(var S: TStream); end; //Dann stand sowas hier da: RStreamableObject: TStreamRec = ( ObjType: 100; VmtLink: ofs(TypeOf(TStreamableObject)^); Load: @TStreamableObject.Load; Store: @TStreamableObject.Store ); //Nun gab es eine Registrier-Prozedur wie folgt procedure RegisterStrmObj; //Der Name setzte sich aus dem Wort Register + Name der Unit zusammen implementation procedure RegisterStrmObj; begin RegisterType(RStreamableObject); end; end. Ein(e) Objekt/(Klasse) nuß also für die Verwendung mit Streams registriert werden. Wie und wo erledigt Delphi diese Aufgabe? Hätte man in Turbo/Borland Pascal auch im Objekt eine Methode definieren können, die das Objekt für den Stream registriert und die dann halt vom Konstruktor mit aufgerufen wird. Wenn nicht, warum nicht? Borland hat ja möglicherweise Gründe, warum die Streamregistrirung in TP/BP so und nicht anders realisiert wurde. Andererseits sehe ich bei den Delphi Streams nirgendwo eine Registrierung, die ich analog vornehmen müßte. Auf eine Aufklärung freut sich Traudix |
Re: Verständnisproblem Streams
wozu willst du denn eine Streamklasse registrieren? Was ist der Nutzen des ganzen?
Heutzutage wird einfach von TStream abgeleitet und die abstracten Methoden implementiert. |
Re: Verständnisproblem Streams
Hallo Sir Thornberry!
Zitat:
RegisterComponent, RegisterConponentClass,... usw. gesehen. [OT] Andererseits hab ich ein altes DOS Spiel, Könnte man doch portieren. Die Objects Unit aus Turbo Pascal, die es ja in Freepascal auch gibt, hab ich von dort übernommen und für Delphi übersetzt. Nun steht zum Beispiel die Frage, ob es sinnvoll ist, die Objekte dort um eine Streamregistrierungsmethode zu erweitern. Dann brauchte ich auch dort keine Registrierung mehr im Hauptprogramm, vielleicht noch nicht mal mehr in abgeleiteten Objekten, falls ich das Teil mal erweitere, und in neuen Objekten keine neuen Daten zu speichern sind. [/OT] Zitat:
das ich hinterher den Aufbau der VCL besser verstehe. Sichwort FCL. Un da mitzumachen, sollte ich die Arbeitweise der VCL wenigstens annähernd verstehen. (B) kann ich damit wieder was lernen. Vielleicht gibt es ja ne noch bessere Lösung als Streams für den gleichen Zweck, für den sie heute verwendet werden. Man kann nie wissen. Dann ist es immerhin sinnvoll, zu verstehen, wie die bisherige Lösung funktioniert. (C) Es gibt nun mal die FreeClx. Und ich habe zudem ne Delphi 3 Pro, wo die VCL Quellen dabei sind. Und da gucke ich mir die Quellen halt auch mal an und möchte dann auch verstehen, was dort drin passiert. Traudix |
Re: Verständnisproblem Streams
könntest du eventuell noch schreiben wozu damals Stream-Klassen registriert wurden?
RegisterComponents dient dazu eine Komponente in der IDE zu registrieren so das du Sie später einfach auf das Form packen kannst. Ich weiß nicht in wie weit du dich bereits mit Delphi und damit auch Object-Pascal auskennst. Aber solche Dinge wie "RegisterComponents" sind dann erst später wichtig wenn du selbst Komponenten schreiben willst. Zu Anfang sollte es jedoch auch reichen, selbst geschriebene, Komponenten einfach dynamich zu erzeugen. Zur Ausgangsfrage: Da ich nicht weiß wie es früher mit den Streamklassen war kann ich dir nicht so recht weiter helfen. Bei Delphi ist es so das mit RegisterComponents, Komponenten in der IDE "registriert" werden. Da alle Komponenten von TComponent abgeleitet sind (wie der Name ja schon sagt) haben diese auch einige GrundMethoden zum speichern einiger besonderer Properties. Einfache Integerproperties etc. werden später automatisch gespeichert (wenn sie registriert sind und auf das Form gepackt wurden). |
Re: Verständnisproblem Streams
Hallo Sir Thornberry!
Zitat:
Delphi-Quellcode:
Nicht die Stremklassen wurden registriert, sondern diejenigen Objekte, die man im Stream speichern wollte. Mein TStreamableObject sei ein solches Objekt, das ich auf einem Stream speichern will. Streamable (streamfähig, weiß, wie es sich in den Stream speichert und wieder aus ihm liest) habe ich hier als Name gewählt, um zu unterstreichen, das das Objekt eben fähig (engl. able) sein soll sich in einem Stream zu speichern und auch wieder gelesen zu werden.
//Der Registrierungsrecord war wie folgt definiert
type PStreamRec = ^TStreamRec; TStreamRec = Record //für jedes Objekt eindeutige Nummer //(war fehlerträchtig->(doppelte Nummern)) ObjType: word; //Adresse der VMT VmtLink: word; //Adresse von Load -> //für das Lesen der Daten vom Stream Load: Pointer; //Adresse von Store -> //Screiben in Stream Store: Pointer; //Nächster Registrierungsrecord Next: PStreamRec; end; //Es wurde einfach eine Liste mit den Registrierungsdaten aufgebaut. //Belegt wurden die Felder so, wie ich es im ersten Beitrag gezeigt habe. Man mußte dann für eigene Objekte eigene Load und Store Methoden schreiben:
Delphi-Quellcode:
Damit das Objekt weiiß, wie Datenfelder, untergeordnete Objekte (wie zB. Buttons auf Form) gespeichert werden müssen, damit sie beim wiedereinlesen nicht nur auf irgendwelche Speicherplätze gebracht werden, sondern eindeutig auch dem richtigen Objekt mit der richteigen VMT zugeordnet werden können, war (ist garantiert auch in Delphi) diese Registrierung notwendig. Und wenn sie notwendig ist und unter Delphi aber nicht vom Programmierer vorgenommen wird, muß diese Registrierung irgendwo in den Tiefen der VCL versteckt sein. In TP/BP mußte ich für jedes Objekt einen StreamRec schreiben, der belegt war, wie im ersten Beitrag gezeigt. Die Zahl ist eine laufende Nummer, der VmtLink ist die Adresse der Vmt des zu speichernden Objektes, dann folgen die Adressen von Load und Store, den Methoden, die für das Lesen bzw. Schreiben zuständig sind. Zusätzlich mußte das Objekt mit der Prozedur:
type
TStreamableObject = object(TObject) Datenfeld_1: Integer; Datenfeld_2: PString; //Zeiger auf einen String Datenfeld_3: PObject; //nur beispielhaft (In der Praxis irgendein sinnvoller Objektzeiger) constructor Init; constructor Load(var S: TStream); procedure Store(var S: TStream); virtual; end; //TObject war ganz einfach gestrickt, hatte keine Datenfelder und nur eine virtuelle Methode. //Damit war garaniert das die VMT das erste Feld (Offset 0) eines jeden Objektes war, da alle //Detenfelder erst DANACH folgten. type TObject = Object constructor Init; //Wie in Delphi auch-> sobald virtuelle Methoden-> Konstruktor procedure Free; //nicht virtuell, für Vertändnis hier unwichtig destructor Done; virtual; //Damit gibt es eine VMT end; //Jedes Streamfähige Objekt mußte natürlich von TObject abgeleitet werden, damit das //Konzept funktionierte. constructor TStreamableObject.Init; begin inherited Init; //die einenen Voreinstellungen end; constructor TStreamableObject.Load(var S: TStream); begin //wenn direkt von TObject abgeleitet -> inherited Init; //wenn zwischen meinem Objekt und TObject weitere Klassen dazwischen, dann //inherited Load(S); //um die geerbten Datenfelder auch zu berücksichtigen //Vorausgesetzt, es gibt einen geerbten Load Constructor, ja, Load war ein //Constructor //Hier also, weil direkt von TObject abgeleitet: inherited Init; S.Read(Datenfeld_1); S.ReadStr(Datenfeld_2); //Kommentar-> siehe Kommentare in der Store-Methode GetSubObjPtr(S, Datenfeld_3); // end; procedure TStreamableObject.Store(var S: TStream); begin //Wenn direkt von TObject abgeleitet, wird keine Store Methode geerbt, weil in TObject //keine definiert war, sonst inherited Store(S); S.Write(Datenfeld_1, sizeof(Datenfeld_1); S.WriteStr(Datenfeld_2); //Spezielle Methode, die weiß, wie Zeiger sinnvoll und korrekt //im Stream abgelegt werden PutSubObjPtr(S, Datenfeld_3); //Spezielle Methode, die weiß, wie Objektzeiger im Stream //gespeichert werden end; //Die Reihenfolge der Datenfelder muß beim Lesen und schreiben identisch sein //Nun weiß das Objekt, wie die Daten im Stream gespeichert werden. //Für das folgende Beispiel setze ich voraus, das ich ein Objekt TMyApplication //definiert habe, in welchem mein Objekt vom Stream gelesen und zum Programmende //auch wieder in den Stream geschrieben wird. //In der Anwendung stand dann sowas hier: constructor TMyApplication.Init; begin RegisterStrmObj; //Hier wird mein Objekt registriert inherited Init; //...eigene Voreinstellungen, wie in Delphi auch... end; procedure TMyApplication.LoadThisObjectFromStream; var ObjfromStream: TStreamableObject; Stream: TStream; StreamName: string; begin StreamName := 'MyStream.stm'; Stream.Init(StreamName,stOpenRead); ObjFromStream := PStreamableObject(Stream.Get); //Die Typumwandlung war nötig, weil der Stream Objekte oder Nachfahren vom Typ TObject //speicherte //Natürlich muß ich vorher den Typ PStreamableObject definiert haben als ^TStreamableObject; Stream.Done; //in Delphi Destroy end; //Analog konnte das Objekt gespeichert werden: procedure TMyApplication.SaveThisObjectToStream; var Stream: TStream; StreamName: string; begin StreamName := 'MyStream.stm'; Stream.Init(StreamName,stOpenRead); Stream.Put(MyObjectOnStream); //Die Typumwandlung war nötig, weil der Stream Objekte oder Nachfahren vom Typ TObject //speicherte //Natürlich muß ich vorher den Typ PStreamableObject definiert haben als ^TStreamableObject; Stream.Done; //in Delphi Destroy end; RegisterType(RMyObjectWhichShouldWorkWithStream); registriert werden. Deshalb die TStreamRec Konstante RStreamableObject, per Konvention, wie der Objekttyp, aber statt Präfix T wurde Präfix R davor gesetzt. Das wurde (dann auch per Konvention) so gemacht, das man eine Prozedur (KEINE METHODE) geschrieben hat: procedure Register[gefolgt vom unitnamen]; ---> siehe erster Beitrag Meine Beispielunit heißt StrmObj -> also
Delphi-Quellcode:
In der Anwendung wurde diese dann vom InitKonstruktor augerufen, womit das Objekt für die Zusammenarbeit mit dem Stream registriert, also vorbereitet war.
procedure RegisterStrmObj;
begin RegisterType(RStreamableObject); end; Ich bin sicher, das in Delphi die Klassen genauso für die Arbeit mit Streams registriert werden aber die Registrirung geschickt vor dem Programmierer verborgen wird und automatisch erledigt wird. Als erstes fallen mir da die Konstruktoren ein. Aber dann müßte ich ja bei Einführung neuer Datenfelder auch wieder selber Hand anlegen. Es muß also einen anderen Trick geben. Die Vorgehensweise in TP/BP war nämlich recht Fehlerträchtig, da eine Registrierung schnell vergessen wird. Delphi nimmt da ne ganze Menge Arbeit ab, aber was geht da in der VCL vor? Also, nicht die Streams, sondern die Objekte, die mit Streams zusammenarbeiten sollten, wurden registriert, dem Stream bekannt gemacht. Grüße von Traudix |
Re: Verständnisproblem Streams
Das wird in D32 über RegisterClass und TWriter/TReader gelöst.
Du kannst dir als Beispiel den Code von der ![]() Genauso, bzw. ähnlich, geht die IDE vor um DFM Daeienen zu erzeugen/laden und das Form/Frame/DataModule selbst benutzt es um sich zu initialisieren. Edit: Wenn du mit FCL .Net meinst, ist das Registrieren an sich unnötig, da reicht es trivialen Klassen ein SerialzeableAttribute zu verpassen, bzw. wenn man selbst Hand anlegen will implementiert man ISerialzable. Das Serialisieren erfolgt dann zum Beispiel über XmlSerializer, oder einem der Formatter (BinaryFormatter, SoapFormatter, ...). |
Re: Verständnisproblem Streams
Hallo Robert_G!
Danke erst mal. Das dpCollection-Beispiel muss ich mir nun erst mal durcharbeiten. Ich habe VCL gemeint. Interessant ist, das bei FCL .NET nur ein Serializable-Attribut gesetzt werden muß, wenn es sich un triviale Klassen handelt. Aber wenn die Klassen nicht so trivial sind, muß also auch dort was implementiert werden. Aber das kommt später. Vorerst soll es nur um die VCL gehen. Erst mal diese begreifen. Gruß Traudix |
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:18 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz