AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Code-Bibliothek Library: Object-Pascal / Delphi-Language Delphi Records / Arrays /(Klassen) mit dynamischen Inhalt speichern
Thema durchsuchen
Ansicht
Themen-Optionen

Records / Arrays /(Klassen) mit dynamischen Inhalt speichern

Ein Thema von sirius · begonnen am 2. Jul 2007 · letzter Beitrag vom 8. Aug 2007
Antwort Antwort
Benutzerbild von sirius
sirius

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

Records / Arrays /(Klassen) mit dynamischen Inhalt speichern

  Alt 2. Jul 2007, 21:53
Motivation

Diese Unit soll Records oder auch Arrays mit dynamischen Inhalt speichern. Ich weis, dass man dafür eher Klassen mit published Properties nehmen sollte. Aber machnmal ist eine Klasse auch etwas Overkill.
Ich wollte auch schon lange mal diese Unit schreiben. Schon allein, um zu testen, ob es denn funktioniert. In meinen zahlreichen Tests hat es funktioniert.

Was kann es
Was soll es können

Der Inhalt folgender Bsp.-Deklarationen kann mit einem Funktionsaufruf in einen Stream (oder nonVCL-Unit: in einen String) gespeichert werden und wieder zurückgeschrieben werden.
Delphi-Quellcode:
//Dies sind nur Beispiele
//für die nonVCL-Variante bitte in der entspr. Unit nachlesen


type Tenum=(a,b,c,d); //Nur zur Definition,
type Tset=set of TEnum; //kann und muss nicht mit dieser Unit gespeichert werden


type tx=record
       i:integer;
       s:string;
end;

type Tmyrecord=packed record
       info:pointer;
       f:function:boolean of object;
       v1:variant;
       v2:array[1..2] of variant;
       v:array of variant;
       a:integer;
       s:Tset;
       a2:integer;
       z:array of tx;
       x1,x2:string;
       i:integer;
       d:double;
       p:pointer;
       t:array of string;
       q:array[1..4] of string;
       nx:tx;
end;

type Tsa=array[1..3] of Tmyrecord;
type Tda=array of Tmyrecord;
type Tdx=array of tx;

type TmyF=function:boolean of object; //nur zur Deklaration

type TmyInnerClass=class
         InS:string;
end;
type TMyClass=class(TMyInnerClass)
       public
         another_i:integer;
       private
         fi:integer;
         fx:tmyF;
         fs:string;
         fs2:string;
         frec:Tmyrecord;
         fc:char;
         fdx:Tdx;
       published
        // property kann aber ist hierfür irrelevant
  
end;
Wie man sieht, sind auch Klassen möglich. Dabei werden nicht die published properties beachtet,
sondern alle Variablen (inkl. aus abgeleiteten Klassen). Für mich war eigentlich nur das Record notwendig. Aber damit brauchte ich auch Variant, sowie statische und dynamische Arrays. Deswegen sind diese auch implementiert. Die Klassen sind noch aus eigenem Spieltrieb entstanden. Ob das mit den Klassen, notwendig ist, weis ich nicht. Vor allem sollte man da mächtig aufpassen, besonders wenn man die übergeordneten Klassen nicht selber geschrieben hat (siehe unten: Pointer-Variablen).


Wie funktioiert es

Die Unit bietet nach außen zwei Funktionen: SaveToStream und LoadfromStream
Aufgerufen/benutzt werden sie so:
Delphi-Quellcode:
procedure saveRecord(rec:TMyRecord);
var stream:Tfilestream;
begin
  stream:=TFileStream.create('Test.dat',fmcreate);
  SaveToStream(rec,Stream,TYPEINFO(TMyRecord));
  //hier können auch noch andere Records/Arrays/Klassen direkt angefügt werden
  stream.free;
end;

procedure SaveArray(Arr:Tda);
var stream:Tfilestream;
begin
  stream:=TFileStream.create('Test.dat',fmcreate);
  SaveToStream(Arr,Stream,TYPEINFO(Tda));
  stream.free;
end;

procedure SaveClass(const AObject:TObject);
var stream:Tfilestream;
begin
  stream:=TFileStream.create('Test.dat',fmcreate);
  SaveToStream(Aobject,Stream,Aobject.ClassInfo);
  stream.free;
end;


//Wenn man den Urpsrung des Records nicht mehr kennt (ähnlich wie bei SaveClass)
//kann man sich mit einer Art info-Variable vom Typ Pointer am Anfang des
//Records behilflich sein.
//Ich habe das schonmal vorbereitet ;-)

procedure initRecord(var rec:tmyrecord);
begin
  //wichtig ist, dass man irgendwo den info-Pointer setzt
  rec.info:=typeinfo(Tmyrecord);
end;
procedure saveUnknwonRecord(rec:ppointer);
var Stream:TfileStream;
begin
  Stream:=TFilestream.create('test.dat',fmcreate);
  savetoStream(rec^,stream,rec^);
  Stream.free;
end;
Was habe ich gemacht?
Da mir die Unit TypInfo nur begrenzt weitergeholfen hat, habe ich einfach mal geschaut, was der Compiler so macht und mich versucht danach zu richten. Dementsprechend ist das auch auf den Compiler von Delphi 7 zugeschnitten. Aber ich denke / ich hoffe dass es auch in anderen die Versionen geht, und der Aufbau der RTTI nicht geändert wurden. (Seit Delphi 4 sind alle Typen bekannt)

Als Basis brauche ich deswegen auch neben der Adresse des records (oder Klasse, etc) noch den Pointer zur RTTI, den man mit TYPEINFO(TMyVariablenType) oder mit TObject.ClassInfo bekommt. Dort stehen dann u.a. eben diese wichtigen Informationen:
- Größe des Records
- Wo stehen dynamsiche Komponenten & Pointer zur RTTI dieser dynamischen Komponente
So kann ich mich dann durch alle Variablen durchhangeln (wenn ein Array wiedrum Strings ode Records beinhaltet,...)

Interessant ist das Zurückschreiben des Streams (LoadFromStream). Da ich hier nicht nur den Inhalt des Records schreiben muss, sondern auch aufpassen, dass der alte gelöscht wird, und der neue Inhalt den richtigen Referenzzähler bekommt. Und Typen wie WideString und Variant sollen auch "WINAPI-kompatibel" sein.
(Bei letzterem war besonders spannend, ein Array in einem Variant unterzubringen - Stichwort: SafeArray).



Was kann diese Unit nicht / Worauf ist zu achten
1. Besonders bei Klassen ist es wichtig zu beachten, dass nicht nur die Properties, sondern alle Variablen abgespeichert werden. Und beim Laden aus dem Stream, werden diese auch wieder zurückgeschrieben. Dabei könnten alle Pointer umgeschrieben werden, sodass sie ungültig werden. Dazu zählen neben den untypisierten Pointern (pointer) auch pcardinal,pchar,... alle Klassen und Interfaces. Diese zeigen dann nach dem Überschrieben durch LoadFromStream nicht nur ins Nirvana, sondern es bleiben auch Speicherlöcher, wenn diese Variablen die einzige Referenz auf reservierten Speicher waren. Der Grund liegt darin, dass zu pointern und Klassen keine Infos in der RTTI liegen.
Dasselbe gilt für diese Typen, wenn sie in Records oder Arrays enthalten sind.

2. Diese Unit kann generell keine Interfaces handhaben (noch nicht!)

In erster Instanz ist es wichtig, dass diese Unit Records mit Strings und Arrays speichern kann. Das war die ursprüngliche Idee, und das werde ich wahrscheinlich auch demnächst brauchen.


So, ich hoffe ich habe nix vergessen.
PS: Ich danke denen, die die Funktionen mal überprüfen.

Edit1: Mein Beispielprojekt zum Rumspielen zusätzlich angehängt
Edit2: Beim Zusammenstellen der ZIP habe ich etwas geschlafen
Edit3+4: Neu Versionen hochgeladen (Typecasting für D6)
Edit5: Jetzt dürften auch Klassen funktionieren die nicht direkt von TObject abgeleitet sind
Edit6: Da hat ich gestern bei den Klassen etwas zu viel geändert
Edit7+8: kleine Anpassungen
Edit9: nonVCL-Unit ergänzt
Angehängte Dateien
Dateityp: pas u_store_dynamic_params_117.pas (26,1 KB, 232x aufgerufen)
Dateityp: zip delphirtti_142.zip (10,8 KB, 171x aufgerufen)
Dateityp: pas u_store_dynamic_params_nonvcl_105.pas (28,2 KB, 121x aufgerufen)
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

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

Records/Klassen mit dynamischen Inhalt speichern (nonVCL)

  Alt 8. Aug 2007, 20:14
Auf Idee von Zacherl an dieser Stelle habe ich eine nonVCL-Variante geschrieben. Es wird nur die Unit Windows (neben System) verwendet.

Der Inhalt des Objecktes/Records/Arrays wird entweder in einen String geschrieben, oder es wird ein Pointer auf den Inhalt zurückgegeben. dieser String kann dann abgespeichert werden (was auch immer) und später wieder eingelesen werden.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Benutzerbild von SirThornberry
SirThornberry
(Moderator)

Registriert seit: 23. Sep 2003
Ort: Bockwen
12.235 Beiträge
 
Delphi 2006 Professional
 
#3

Re: Records / Arrays /(Klassen) mit dynamischen Inhalt speic

  Alt 8. Aug 2007, 20:28
Aber das Grundproblem bleibt. Wenn ich einen Record mit einem Pointer habe, kann der Compiler nicht wissen wie groß die Daten hinter dem Pointer sind, ob es ein Object ist und wie es gespeichert werden muss etc.
Es ist also immer nötig eigene Strukturen speziell zu speichern sofern keine einfachen Datentypen verwendet werden.
Jens
Mit Source ist es wie mit Kunst - Hauptsache der Künstler versteht's
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

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

Re: Records / Arrays /(Klassen) mit dynamischen Inhalt speic

  Alt 8. Aug 2007, 20:35
Wenn du ein Record mit einem "untypisierten" Pointer hast, dann ist es natürlich unmöglich. Dann könnte man aber auch sagen, dass in dem Fall an dem Konzept etwas falsch ist.
Es tauchte nur hin und wieder mal das Problem des Speicherns von Records auf, welche dynamische Arrays und/oder dynamische Strings enthalten. Um dynamische Arrays kam man bisher nicht drumrum. Um Strings ist man meistens mit platzsparenden 255 Zeichen langen Shortstrings gegangen (siehe DSDT).


btw.:
Wenn hinter den Pointer Objekte stehen, gibts ja das hier.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Antwort Antwort

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 11:06 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