Autor Beitrag
knittel
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 71
Erhaltene Danke: 2

Win XP, Win7, openSUSE
Delphi 7
BeitragVerfasst: Mo 15.07.13 01:59 
Hallo allerseits,

Ich schreibe mir eine Wrapper-Klasse um TFileStream (habe den topic trotzdem hier gepostet, weil das Problem etwas anderes betrifft), habe aber damit ein Problem. Ich habe mir eine Funktion zum Speichern geschrieben und eine zum Laden und die sieht wie folgt aus:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
function TMeFile.LoadNextElement(p: pointer): Byte;
var i, n: integer; str: string;
begin
FStream.Read(result, 1); // Rückgabewert ist das Identifikationsbyte
case result of
  0begin // 0 = Unbekanntes Format
    FStream.Read(n, 4);
    FStream.Read(p^, n);
    end;
  1begin // 1 = String laden
    FStream.Read(n, 4); // er lädt die Länge des Strings
    SetLength(str, n); // Länge setzen.
    if (n > 0then FStream.Read(pointer(str)^, n); // wenn die Länge größer 0 ist, soll er den String laden.
    // und hier beginnt das Problem, wie übergebe ich den String nun über den Pointer zurück??? Das hier unten ist mein verrückter Versuch.
    SetLength(string(p^), n); 
    Move(str[1], p^, n);
    end;
  2: FStream.Read(p^, 4);
  3: FStream.Read(p^, 4);
  end;
end;


Ich möchte halt, dass ich einfach nur schön MyFile.LoadNextElement(@MyInteger), MyFile.LoadNextElement(@MyString) benutzen kann. Mit meinem Code funktioniert das leider nicht so ganz. Ich hab zwar vom Umgang mit Pointern ein bisschen Ahnung, aber nur solange es sich um Listen/etc. handelt, leider nicht bei Strings. Könnt ihr mir irgendwie helfen und schauen, was ich an meinem Code verändern muss?

Danke schonmal! :)

_________________
"Wir können nicht fliehen!" "Wieso nicht?" "Sie haben mir die Schnürsenkel zusammengebunden!" "Die Schweine."
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mo 15.07.13 08:43 
Das Problem ist die Referenzzählung. Ein String, den du innerhalb der Methode erstellst, wird an deren Ende freigegeben. Wenn du einen Pointer darauf zurücklieferst, ist der daher nach dem Ende der Methode ungültig. (Davon abgesehen gehst du davon aus, dass ein Zeichen immer ein Byte hat, das würde mit neueren Delphiversionen nicht mehr funktionieren.)

In Zeile 8 schreibst du einfach in den Pointer, obwohl du gar nicht weißt, ob dort genug Platz ist. Keine gute Idee...

Grundsätzlich sollte immer die aufrufende Methode den Speicher reservieren und deine Methode diesen nur füllen. In neueren Delphiversionen würden sich dafür Generics oder anonyme Methoden anbieten, aber beides hatte Delphi 7 (wie in deinem Profil) damals nicht.

Was du machen kannst: Genauso wie die ReadBuffer und WriteBuffer Methoden nicht nur den Pointer angeben, sondern auch wie viel Platz darin ist. Ist nicht genug Platz, setzt du die Streamposition auf den Wert vor dem Leseversuch zurück und lieferst die benötigte Puffergröße zurück. So funktioniert es auch in der Windows API. Sprich:
Anfrage mit nil und Puffergröße 0, Rückgabe von 10 als Größe in Byte, SetLength auf den String, erneuter Aufruf mit dem Pointer auf den String und Puffergröße 10.

Wirklich elegant ist eine solche Kapselung allerdings erst mit Generics...
IhopeonlyReader
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 600
Erhaltene Danke: 23


Delphi 7 PE
BeitragVerfasst: Mo 15.07.13 15:31 
Ich würde sagen, der Fehler beginnt schon beim Aufbau der Procedure, du rufst die procedure auf ohne zu wissen was du empfängst musst aber schon etwas übergeben (z.B. Integer-Pointer)
Deshalb würde ich dir empfehlen den Identifikationsbyte VOR aufruf der procedure auszulesen und danach entscheiden welche Variable übergeben werden muss, sonst wird's mit dem weiterarbeiten schwer...
die Case-Abfrage kannst du also eigentlich aus der Procedure rausziehen..
Dann z.B. bei einem unbekannten Format, das ganze "ignorieren", da du damit eh nicht weiterarbeiten wirst, da du ja nicht weißt was es ist..
bei einem String ruft du dann eine extra dafür geschriebene Procedure auf und übergibst als Parameter schon ein String
Ist es eine Integer-Variable rufst du eine LoadIntegerFromStream-Procedure auf (selbergeschriebene) und übergibst als Parameter eine Integer Variable..

Eine Umsetzung mit Pointerübergabe halte ich allgemein für ungut.. Tut mir leid, aber ich sehe da keinen Sinn drin, etwas in eine Gruppe einzuordnen, wenn man den Typ eh wieder "verschinden lässt" über den Pointer...
Da könntest du auch
ausblenden Delphi-Quelltext
1:
2:
3:
FStream.Read(n, 4);
if n<=SizeOf(p^) then
 FStream.Read(p^, n);

wäre so wie ich das sehe das gleiche, da du den Typ außerhalb deine Procedure scheinbar nicht beachtest

_________________
Sucht "neueres" Delphi :D
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!

Für diesen Beitrag haben gedankt: knittel
knittel Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 71
Erhaltene Danke: 2

Win XP, Win7, openSUSE
Delphi 7
BeitragVerfasst: Mo 15.07.13 16:44 
Danke! Ich hab das jetzt genausogemacht wie du vorgeschlagen hattest und für jeden Typ eine eigene Load Prozedur geschrieben. Habs jetzt ausprobiert und funktioniert prima! Danke nochmal.

_________________
"Wir können nicht fliehen!" "Wieso nicht?" "Sie haben mir die Schnürsenkel zusammengebunden!" "Die Schweine."