Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - PReal()
Mortal-Shadow - Mi 27.05.09 13:43
Titel: PReal()
Hi,
zwecks Datenübertragung arbeitet Narses in seinem Tut so:
Delphi-Quelltext
1: 2:
| SetLength(BinMsg,4); PInteger(@BinMsg[1])^ := MyInt; |
bevor er BinMsg als String sendet.
Nun wollte ich so einen Real übertragen:
Delphi-Quelltext
1: 2:
| SetLength(BinMsg,8); PReal(@BinMsg[1])^ := MyReal; |
Leider gibt es PReal nicht.
Kann ich das irgendwie umgehen?
Mfg.
Moderiert von
Narses: Topic aus Sonstiges (Delphi) verschoben am Mi 27.05.2009 um 14:07
jaenicke - Mi 27.05.09 13:57
Du könntest es deklarieren. ;-)
Oder du könntest einen "richtigen" Typ wie Single oder Double nehmen, da sind die Pointertypen bereits deklariert...
Narses - Mi 27.05.09 14:11
Moin!
Mortal-Shadow hat folgendes geschrieben : |
zwecks Datenübertragung arbeitet Narses in seinem Tut so:
[...]
Nun wollte ich so einen Real übertragen:
[...]
Leider gibt es PReal nicht.
Kann ich das irgendwie umgehen? |
Real ist ein generischer Datentyp, den du nicht in so einem Kontext verwenden solltest, wo es auf das Bytelayout im RAM ankommt :!:
In diesem Fall Single, Double oder Extended verwenden (und diese Typen sind im TProtocolAdapter bereits drin). :idea: ;)
(btw: war Real nicht eh deprecated? :gruebel:)
(//EDIT: kann sein, das war Real48... :?)
cu
Narses
Mortal-Shadow - Mi 27.05.09 14:16
Ok, das ist ja nicht das Problem.
Laut Delphi-Hilfe sind Real und Double eh gleich:
Zitat: |
The generic type Real, in its current implementation, is equivalent to Double. |
Btw: wenn es gleich ist - warum sollte es dann nicht verwendet werden?
Narses - Mi 27.05.09 14:22
Moin!
Mortal-Shadow hat folgendes geschrieben : |
Btw: wenn es gleich ist - warum sollte es dann nicht verwendet werden? |
Weil es in einer zukünftigen Delphi-Version eben nicht mehr Double, sonder Extended oder Hyperble oder ... sein könnte. ;)
cu
Narses
jaenicke - Mi 27.05.09 14:23
Den entscheidenden Teil hast du selbst geschrieben:
Mortal-Shadow hat folgendes geschrieben : |
Zitat: |
The generic type Real, in its current implementation, is equivalent to Double. |
|
Du weißt nicht wann sich das ändert und weißt im Grunde nichts genaues über den Datentyp...
Gammatester - Mi 27.05.09 16:06
Die Lösung ist einfach:
Delphi-Quelltext
1: 2:
| SetLength(BinMsg,sizeof(MyReal)); move(@BinMsg[1], MyReal, sizeof(MyReal)); |
Gruß Gammatester
Narses - Mi 27.05.09 17:04
Moin!
Gammatester hat folgendes geschrieben : |
Die Lösung ist einfach |
...keine gute Idee, um hier keine Zweifel aufkommen zu lassen. ;)
cu
Narses
Gammatester - Mi 27.05.09 18:22
Narses hat folgendes geschrieben : |
Moin!
Gammatester hat folgendes geschrieben : | Die Lösung ist einfach | ...keine gute Idee, um hier keine Zweifel aufkommen zu lassen. ;)
cu
Narses |
Und was soll daran keine "gute Idee" sein? Hat man Dir eingeimpft, "move" ist böse? Das ist richtig, wenn man nicht weiß, was man tut, oder wenn man nicht weiß, wohin man schreibt. Beides ist hier nicht der Fall: Ich weiß, was ich tue, und der Speicherplatz ist da. Im übrigen dies sogar
besser als die vorgestellten Lösungsversuche via Pointer/Typecast, da dort setlength mit fixer Länge benutzt wird und dann kann's wirklich krachen.
Gammatester
Narses - Mi 27.05.09 23:16
Moin!
Gammatester hat folgendes geschrieben : |
Und was soll daran keine "gute Idee" sein? |
Da du scheinbar Probleme beim Lesen hast, hier nochmal extra für dich: :)
jaenicke hat folgendes geschrieben : |
Zitat: | The generic type Real, in its current implementation, is equivalent to Double. | Du weißt nicht wann sich das ändert und weißt im Grunde nichts genaues über den Datentyp... |
Demnächst bitte lesen und verstehen, bevor du auf die Barrikaden gehst. :|
Gammatester hat folgendes geschrieben : |
Hat man Dir eingeimpft, "move" ist böse?
[...]
Ich weiß, was ich tue |
Scheinbar nicht, zumindest fehlt dir entscheidende Einsicht in das angesprochene Tutorial. :nixweiss:
Gammatester hat folgendes geschrieben : |
Im übrigen dies sogar besser als die vorgestellten Lösungsversuche via Pointer/Typecast, da dort setlength mit fixer Länge benutzt wird und dann kann's wirklich krachen. |
Nein, es ist weder
besser noch schlechter, es ist schlicht gleichwertig - wenn man
nicht auf generische Typen setzt. Und genau das ist es, was ich hier die ganze Zeit sage... :roll:
es geht im Tutorial nämlich unter Anderem darum, eine vorhersagbare Anzahl Bytes pro Frame zu bekommen - das geht aber mit deiner Lösung nicht, da generische Typen unsicher/unbekannt (in diesem Zusammenhang) sind; aber erstmal draufhauen, auch wenn man nicht weiß, worum es eigentlich geht... *seufz*
cu
Narses
Gammatester - Do 28.05.09 09:56
Ich sehe zwar nicht, warum ich entscheidende Einblicke in Dein Tutorial nehmen soll, um solche einfache Aufgaben zu beantworten, habe es jetzt aber runtergeladen und mal die angesprochene Stelle angesehen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure TForm1.Button1Click(Sender: TObject); var BinMessage: String; begin SetLength(BinMessage,4); PInteger(@BinMessage[1])^ := Length(Edit1.Text); BinMessage := BinMessage +Edit1.Text; ClientSocket1.Socket.SendText(BinMessage); end; |
Leider muß ich feststellen, daß das angebotene noch schlechter ist als ich gedacht habe. Nicht nur, daß Du genau den Fehler machst, den Du anprangerst (Verwendung von generischen Typen unter Annahme einer festen Größe, und integer ist ja wohl der generische Typ schlechthin), nein: es wird darüber hinaus wieder einmal der Typ String zur Verarbeitung/Transport von Binärdaten benutzt.
Zitat: |
es geht im Tutorial nämlich unter Anderem darum, eine vorhersagbare Anzahl Bytes pro Frame zu bekommen - das geht aber mit deiner Lösung nicht, da generische Typen unsicher/unbekannt (in diesem Zusammenhang) sind; |
Mit Deiner Lösung geht es wie gesagt auch nicht. Wenn Du es besser machen willst, solltest Du eine Hilfsvariable einführen:
Delphi-Quelltext
1: 2: 3: 4: 5:
| var TxtLen: longint;
TxtLen := Length(Edit1.Text); PLongint(@BinMessage[1])^ := Txtlen; |
Zitat: |
aber erstmal draufhauen, auch wenn man nicht weiß, worum es eigentlich geht... *seufz* |
Wo habe ich auf wen drauf gehauen? Das die beschriebene Lösung mit Sicherheit keinen Speicher überschreibt, wirst Du ja wohl zugeben müssen. Und warum sie dann nicht besser sein soll, als die bei denen das passieren kann, ist nicht einsehbar.
Gammatester
BenBE - Do 28.05.09 10:17
Gammatester hat folgendes geschrieben : |
Ich sehe zwar nicht, warum ich entscheidende Einblicke in Dein Tutorial nehmen soll, um solche einfache Aufgaben zu beantworten, habe es jetzt aber runtergeladen und mal die angesprochene Stelle angesehen: |
Ein Tutorial zu lesen ide die Kurzform von RTFM. Hilft also oftmals ;-)
Gammatester hat folgendes geschrieben : |
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure TForm1.Button1Click(Sender: TObject); var BinMessage: String; begin SetLength(BinMessage,4); PInteger(@BinMessage[1])^ := Length(Edit1.Text); BinMessage := BinMessage +Edit1.Text; ClientSocket1.Socket.SendText(BinMessage); end; |
Leider muß ich feststellen, daß das angebotene noch schlechter ist als ich gedacht habe. Nicht nur, daß Du genau den Fehler machst, den Du anprangerst (Verwendung von generischen Typen unter Annahme einer festen Größe, und integer ist ja wohl der generische Typ schlechthin), |
Gut, die 4 könnte man durch ein SizeOf(Longint) ersetzen und PInteger durch PLongint, aber eine Umstellung von Integer auf 64 Bit wird so schnell nicht erfolgen.
Gammatester hat folgendes geschrieben : |
nein: es wird darüber hinaus wieder einmal der Typ String zur Verarbeitung/Transport von Binärdaten benutzt. |
Sorry, wenn man keine Ahnung hat ;-)
Zu der Problematik zwecks Strings und Binärdaten habe ich mich durchaus schon mit
Narses unterhalten und dabei kam relativ eindeutig raus, dass die String-API von Delphi für die verwendeten Funktionen Binary Safe ist. Zumal: Was würde es deiner Ansicht nach besser machen, wenn an jeder Stelle mit PChar und einem manuellen Malloc rumgefrickelt wird? Das einzige Problem mit String ist die Multi-Byte-Änderung in D2k9; die lässt sich aber mit einer Einfachen Änderung von String auf AnsiString korrigieren. Ein Array of Byte bietet prinzipiell ähnliche Möglichkeiten; vom Pointer-Gefrickel unterscheidet sich das aber von Strings in keinster Weise.
Gammatester hat folgendes geschrieben : |
Zitat: | es geht im Tutorial nämlich unter Anderem darum, eine vorhersagbare Anzahl Bytes pro Frame zu bekommen - das geht aber mit deiner Lösung nicht, da generische Typen unsicher/unbekannt (in diesem Zusammenhang) sind; |
Mit Deiner Lösung geht es wie gesagt auch nicht. Wenn Du es besser machen willst, solltest Du eine Hilfsvariable einführen:
Delphi-Quelltext 1: 2: 3: 4: 5:
| var TxtLen: longint;
TxtLen := Length(Edit1.Text); PLongint(@BinMessage[1])^ := Txtlen; | |
Wozu die Hilfsvariable? Ohne die ist durch den Typecast eh der Ziel-Datentyp vorgegeben; da bedarf es keiner Variable um etwas festzustellen, was eh bekannt ist.
Gammatester hat folgendes geschrieben : |
Zitat: | aber erstmal draufhauen, auch wenn man nicht weiß, worum es eigentlich geht... *seufz* |
Wo habe ich auf wen drauf gehauen? Das die beschriebene Lösung mit Sicherheit keinen Speicher überschreibt, wirst Du ja wohl zugeben müssen. Und warum sie dann nicht besser sein soll, als die bei denen das passieren kann, ist nicht einsehbar.
Gammatester |
Gammatester hat folgendes geschrieben : |
Und was soll daran keine "gute Idee" sein? Hat man Dir eingeimpft, "move" ist böse? Das ist richtig, wenn man nicht weiß, was man tut, oder wenn man nicht weiß, wohin man schreibt. Beides ist hier nicht der Fall: Ich weiß, was ich tue, und der Speicherplatz ist da. Im übrigen dies sogar besser als die vorgestellten Lösungsversuche via Pointer/Typecast, da dort setlength mit fixer Länge benutzt wird und dann kann's wirklich krachen.
Gammatester |
Ist sie eben nicht, da du mit Move die Möglichkeit zur Typprüfung der Daten verlierst, weshalb eben der Compiler NICHT prüfen kann, ob der Platz ausreicht, was mit der Typecast-Variante durchaus möglich wäre, zudem sogar mit ggf. einer Konvertierung der Daten, sollte noch etwas fehlen.
Narses - Do 28.05.09 10:39
Moin!
Gammatester hat folgendes geschrieben : |
mal die angesprochene Stelle angesehen |
Du hast zwar nicht die angesprochene Stelle zitiert, aber als Beispiel soll das ausreichen. ;)
Gammatester hat folgendes geschrieben : |
es wird darüber hinaus wieder einmal der Typ String zur Verarbeitung/Transport von Binärdaten benutzt. |
Deine Aussage beweist, dass du wohl eher mit Vorstellungen "geimpft" worden bist, die du besser nochmal auf den Prüfstand stellen solltest. :| Auch hier würde es helfen, das Tutorial mal genauer anzusehen. ;)
Gammatester hat folgendes geschrieben : |
Wenn Du es besser machen willst, solltest Du |
...besser AnsiString verwendet haben, das ist richtig. Das Tutorial ist allerdings entstanden, bevor D2k9 rauskam. Da es aber OpenSource ist, darf der geneigte Leser das gerne selbst durchführen.
Gammatester hat folgendes geschrieben : |
Das die beschriebene Lösung mit Sicherheit keinen Speicher überschreibt, wirst Du ja wohl zugeben müssen. |
Das habe ich doch nie bezweifelt. ;) Und ja, dein Ansatz ist sowohl syntaktisch als auch konzeptionell funktionsfähig, auch das habe ich nicht bestritten. Allerdings ist er - wie
BenBE bereits erläutert hat - nicht typensicher und darüber hinaus auch nicht performant: wir reden hier über CPU-native Datentypen, warum soll ich zum Schreiben/Lesen solcher Daten eine Kopierschleife aufrufen, wenn das die CPU auch in einem (logischen) Schritt tun kann? :zwinker:
cu
Narses
Gammatester - Do 28.05.09 11:08
Zu letzten Mal: der Thread handelt doch wohl nicht davon, ein Tutorial zu lesen, sondern um die konkrete Aufgabe, die Bytes (ja Bytes, nicht chars) einer Real-Variable in einen Speicherbreich zu bewegen.
BenBE hat folgendes geschrieben : |
Ist sie eben nicht, da du mit Move die Möglichkeit zur Typprüfung der Daten verlierst, weshalb eben der Compiler NICHT prüfen kann, ob der Platz ausreicht, was mit der Typecast-Variante durchaus möglich wäre, zudem sogar mit ggf. einer Konvertierung der Daten, sollte noch etwas fehlen. |
Das ist mit der Narses-Variante auch nicht möglich, weil der ganz schöne Check-Mechanismus via Pointertypecast ausgehebelt wird. Nur kommt bei dieser Variante eben noch
Pointer-Frickelei (wie Du Dich ausdrückst) ins Spiel, die anschließend groß und breit erklärt werden muß. Ein saubere und professionelle Lösung wäre wie gesagt,
keine Strings zu verwenden, sondern zB Records.
Da das aber alles mit PReal()^ nicht mehr viel zu tun, soll's das von mir aus gewesen sein.
Gammatester
Narses - Do 28.05.09 11:17
Moin!
Gammatester hat folgendes geschrieben : |
der Thread handelt doch wohl nicht davon, ein Tutorial zu lesen, |
Der Thread bezieht sich ganz konkret auf ein bestimmtes Tutorial. Wie soll man also brauchbar darüber diskutieren, wenn man es nicht kennt... :nut: :zwinker:
Gammatester hat folgendes geschrieben : |
sondern um die konkrete Aufgabe, die Bytes (ja Bytes, nicht chars) einer Real-Variable in einen Speicherbreich zu bewegen. |
Das ist deine Interpretation davon, das haben wir schon bemerkt, ja. :roll:
Gammatester hat folgendes geschrieben : |
weil der ganz schöne Check-Mechanismus via Pointertypecast ausgehebelt wird. |
Ein Typecast ist das Erzwingen eines Typs, nicht das "Aushebeln", irgendwie hast du da scheinbar was falsch verstanden. :idea:
Gammatester hat folgendes geschrieben : |
Ein saubere und professionelle Lösung wäre wie gesagt, keine Strings zu verwenden, sondern zB Records. |
Dazu gibt´s auch einen FAQ-Beitrag [
http://www.delphi-library.de/topic_Probleme+beim+SendenEmpfangen+von+records++dyn+Objekten_60793.html], den ich dir mal ans Herz legen möchte :les: ach sorry, vergiss es wieder, du liest ja nicht so gerne, ich vergaß... :flehan:
cu :wave:
Narses
Gammatester - Do 28.05.09 12:29
Bei soviel Sarkasmus und Ignoranz dann doch ein allerletzen Mal.
1. Die Originalfrage war:
Zitat: |
Leider gibt es PReal nicht.
Kann ich das irgendwie umgehen? |
Darauf habe ich exakt, vollständig, fehlersicher geanwortet.
2. Du brauchst mir nicht zu erklären, was ein Typecast ist. Fakt ist, daß durch Deine Typecast-Verwendung ein Check nicht möglich ist. Eben weil ein
Pointer-Typecast verwendet wird und kein Typcast für den Basistyp.
3. Zu Records, FAQ, etc: Das Records sind kein brauchbarer Ersatz für ein Protokoll sind, ist doch wohl offensichtlich - allerdings Strings noch weniger. Wenn man Deine Ausführung liest, könnte man sich fragen, warum denn überhaupt Records gebraucht werden, und ob man nicht aus den Delphi-, WIN32-APIs etc die Records und structs entfernen sollte.
4. Laß uns ohne Polemik übereinstimmen, daß wir nicht übereinstimmen.
Mortal-Shadow - Do 28.05.09 12:55
So, die Diskussion hat mich jetzt auf eine andere Frage gebracht: (Wenn ich ein neues Thema aufmachen soll, bitte sagen)
Und zwar wegen string vs Ansistring.
Ab D2009 ist string ein unicodestring.
Aber was verändert sich dadurch für die übertragung?
Soweit ich das bei google gefunden habe, ist dass nur eine Performance-Verbesserung, da bei unicode delphi selbst die
Referenzen zählt, bei ansi das betriebssystem.
Das dürfte aber doch auf die übertragung keinen einfluss haben, oder?
jaenicke - Do 28.05.09 13:12
Mortal-Shadow hat folgendes geschrieben : |
Soweit ich das bei google gefunden habe, ist dass nur eine Performance-Verbesserung, da bei unicode delphi selbst die
Referenzen zählt, bei ansi das betriebssystem. |
Äh, nein. AnsiStrings (der normale String bis Delphi 2007) und die neuen UnicodeStrings werden von Delphicode verwaltet, inkl. Referenzzählung.
WideStrings dagegen, die es als Unicode auch vor Delphi 2009 bereits gab, sind lediglich Wrapper um die vom Betriebssystem angebotenen Unicode-Zeichenketten. Diese haben keinen Referenzzähler.
Der Unterschied zwischen AnsiString und UnicodeString ist, dass AnsiStrings auch AnsiChars enthalten, die jeweils ein Byte pro Zeichen belegen. UnicodeStrings hingegen enthalten die neuen Chars, die mehr als ein Byte belegen. In der aktuellen Implementierung 2 Byte. heißt: Die Größe des belegten Speichers in Byte bei AnsiStrings ist gleich der Länge des Strings. Bei UnicodeStrings hingegen bei 2 Byte pro Buchstaben zweimal so viel. Das muss man bei Speicheroperationen bedenken.
Allgemein kann man die Größe des Speichers aus der Größe eines Buchstabens berechnen:
Delphi-Quelltext
1:
| MemorySize := Length(DeinString) * SizeOf(Char); |
bzw. umgekehrt
Delphi-Quelltext
1:
| StringLength := MemorySize div SizeOf(Char); |
Auf diese Weise kann man den selben Code unter Delphi 2007 und früher und Delphi 2009 und später nutzen. Allerdings dürfen dann bei Netzwerk- und Interprozesskommunikation nicht Unicode und Nicht-Unicode Programme gemischt werden, da das natürlich nicht kompatibel ist.
Narses - Do 28.05.09 13:29
Moin!
Gammatester hat folgendes geschrieben : |
Bei soviel Sarkasmus und Ignoranz |
Ignoranz? Nein, sich nicht auf Schablonen-Denken einlassen: ja. ;)
Gammatester hat folgendes geschrieben : |
dann doch ein allerletzen Mal. |
Ich geb´ dir mal einen guten Rat für´s Leben: sieh zu, dass du immer steigerungsfähig bleibst. :zwinker:
Gammatester hat folgendes geschrieben : |
Die Originalfrage war: Zitat: | Leider gibt es PReal nicht.
Kann ich das irgendwie umgehen? | Darauf habe ich exakt, vollständig, fehlersicher geanwortet. |
Die Originalfrage bezog sich konkret auf eine Anwendung im Rahmen eines Tutorials, das du nicht lesen willst. :nixweiss: Dein Vorschlag ist zwar syntaktisch korrekt, aber konzeptionell, performancetechnisch und typenbasiert keine gute Idee im Rahmen des genannten Tuts. Offensichtlich reden wir hier aneinander vorbei.
Gammatester hat folgendes geschrieben : |
Du brauchst mir nicht zu erklären, was ein Typecast ist. |
Scheinbar schon: ;)
Delphi-Quelltext
1: 2: 3: 4: 5:
| var PInt: PInteger; Float: Double; begin PInt^ := Float; |
Delphi-Compiler hat folgendes geschrieben: |
[Fehler] Unit1.pas(xx): Inkompatible Typen: 'Integer' und 'Double' |
Gammatester hat folgendes geschrieben : |
Das Records sind kein brauchbarer Ersatz für ein Protokoll sind, ist doch wohl offensichtlich - allerdings Strings noch weniger. |
Leider muss ich auch hier wieder korrigieren: Ich verwende Strings als Datencontainer, nicht als Protokoll, du vergleichst hier Äpfel mit Birnen. :nixweiss:
Gammatester hat folgendes geschrieben : |
Wenn man Deine Ausführung liest, könnte man sich fragen, warum denn überhaupt Records gebraucht werden, und ob man nicht aus den Delphi-, WIN32-APIs etc die Records und structs entfernen sollte. |
Ich kann dir nicht folgen. :?
Gammatester hat folgendes geschrieben : |
Laß uns ohne Polemik übereinstimmen, daß wir nicht übereinstimmen. |
Jap, das lässt sich manchmal nicht vermeiden. ;)
---
Mortal-Shadow hat folgendes geschrieben : |
Und zwar wegen string vs Ansistring.
Ab D2009 ist string ein unicodestring.
Aber was verändert sich dadurch für die übertragung?
[...]
Das dürfte aber doch auf die übertragung keinen einfluss haben, oder? |
Es ändert sich leider sehr viel, da ich die Strings als Datenlager (in diesem Sinne) "missbrauche". Es kommt also im Rahmen des BinProto-Tutorials bei den Strings nur darauf an, dass diese eine bestimmte Menge Daten (hier: Bytes) per Compiler-Magic automatisch verwalten. Deshalb ist der Ansatz darauf ausgelegt, dass ein AnsiString (1-Byte-String) verwendet wird, mit einem Unicode-String wird das nicht funktionieren.
Gammatester wird jetzt vielleicht fett grinsen, da er genau das als "Sünde" einstuft, aber unter der Annahme, dass String=AnsiString gilt (was man ab D2k9 jetzt halt nächträglich im Tut ändern muss), ist es die einfachste Variante, um mit Speicherblöcken zu hantieren und auf fertige und bewährte Objekte zurückzugreifen. :nixweiss: Abgesehen davon ist es für Anfänger vermutlich einfacher zu verstehen, als mit der MemManager-API und Pointerlisten rumzumurksen. ;)
Performance-Technisch sehe ich da keine Unterschiede (wenn es denn anwendbar wäre).
cu
Narses
Gammatester - Do 28.05.09 13:57
Narses hat folgendes geschrieben : |
Gammatester hat folgendes geschrieben : | dann doch ein allerletzen Mal. | Ich geb´ dir mal einen guten Rat für´s Leben: sieh zu, dass du immer steigerungsfähig bleibst. :zwinker:
|
Jetzt muß es halt das aller-allerletze Mal sein (das ist beliebig iterationsfähig, also bitte keine weiteren guten Ratschläge).
Zitat: |
Scheinbar schon: ;)
Delphi-Quelltext 1: 2: 3: 4: 5:
| var PInt: PInteger; Float: Double; begin PInt^ := Float; |
Unit1.pas(xx): Inkompatible Typen: 'Integer' und 'Double' |
Ich denke, daß Du das bewußt falsch verstehen willst. Ich sehe nirgends ein @. Die diskutierte Situation ist
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| var Int: Integer; Float: Double; type PDouble = ^Double; begin PDouble(@int)^ := Float; |
Narses - Do 28.05.09 14:20
Moin!
Gammatester hat folgendes geschrieben : |
Jetzt muß es halt das aller-allerletze Mal sein (das ist beliebig iterationsfähig, also bitte keine weiteren guten Ratschläge). |
Hey, das erinnert mich an Terry Pratchett, Trolle und Zählen: 1, 2, 3, Viele, Viele-1, Viele-2, Viele-3, Viele-Viele, ... :lol:
Gammatester hat folgendes geschrieben : |
Ich sehe nirgends ein @ |
Ja, das ist korrekt, ich wollte es vereinfachen. :nixweiss:
Gammatester hat folgendes geschrieben : |
Die diskutierte Situation ist Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7:
| var Int: Integer; Float: Double; type PDouble = ^Double; begin PDouble(@int)^ := Float; | |
Nein, ist sie nicht, denn der generierte Pointer zeigt bei dir auf einen einfachen Datentyp (und das auch noch auf dem Stack), bei mir
immer auf den Anfang eines dynamischen Strings. Da ein Heap-Block aber immer min. 16 Bytes groß ist, ich aber nur einfache Datentypen typkonform gecastet so "verwerflich" da rein schreibe, kann es prinzipiell nicht zu einem Problem kommen - selbst wenn die Typlänge nicht passen sollte (was ich durch Vermeidung von generischen Typen ausgeschlossen haben sollte). :idea:
//EDIT: ---------- (hatte jetzt erst Zeit, um ausführlicher auf die "diskutierte Situation" einzugehen)
Zunächst mal hast du Recht, wenn du sagst, dass ich beim Verzicht auf generische Typen auch kein
Integer verwenden darf. Ja, der Punkt geht voll auf mein Sündenkonto (ich bin ketzerischer Weise davon ausgegangen, dass die Sprache Delphi es nicht mehr erleben wird, dass Integers 64 Bit breit sind). Ich bereue und werde deshalb im Folgenden
Longint dafür verwenden. :flehan:
Kommen wir zur Analyse der wirklich diskutierten Version(en):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| SetLength(BinMsg,4); PLongint(@BinMsg[1])^ := MyLongint; SetLength(BinMsg,sizeof(MyLongint)); move(@BinMsg[1], MyLongint, sizeof(MyLongint)); |
Abstrahieren wir mal das, was da im Prinzip passiert:
- Speicherplatz reservieren
mein Ansatz: garantierte Basistypmenge (Konstante laut Doku)
dein Ansatz: dynamisch ermittelte Größe von Basistypen entsprechen immer der Basistypgröße (ersetzt der Compiler durch eine Konstante)
Fazit: gleichwertig
- Pointer generieren
mein Ansatz = dein Ansatz, identisch -> @BinMsg[1]
Fazit: gleichwertig
- Daten schreiben
mein Ansatz: Pointer-Typecast auf einen Basistyp, einfache Schreiboperation der CPU
dein Ansatz: Kopierschleife, gesteuert durch eine vom Compiler eingesetzt Konstante für die Basistypgröße
Fazit: mein Ansatz ist performanter, da die CPU das durch eine native Operation abwickeln kann, dein Ansatz ist eine Schleife, Aufwand: O(1) < O(n)
- Ergebnis aus Sicht deines Ansatzes: gleichwertig - gleichwertig - höherer Aufwand :|
Abschließend noch etwas zu dem von dir angepassten Beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| var Int: Integer; Float: Double; begin PDouble(@Int)^ := Float; var Int: Integer; Float: Double; begin move(@Int, Float, SizeOf(Float)); |
Das sollte das von dir modifizierte Beispiel ad absurdum führen, da deine Version genau so zielsicher Speicher überschreibt, wie meine. Ich wollte mit dem Beispiel ursprünglich zeigen, dass der Compiler bei einem Pointer-Typecast sehr wohl die Schreiboperation typensicher ausführt. Dass der referenzierte Pointer natürlich keiner Typprüfung unterliegt, ist ja wohl klar (oder: der Sinn der Sache). Deshalb muss ich die Unterstellung, ich wolle absichtlich etwas falsch verstehen, an dieser Stelle klar an dich zurückgeben!
Gesamtfazit: Unsere Ansätze sind funktional gleichwertig, meiner ist aber performanter. Wie du also darauf kommst, dass dein Ansatz prinzipiell "besser" sein soll, ist mir nicht klar. "Universeller einsetzbar" im Austausch gegen "besser" fände ich angemessen. ;)
Dass das ganz konkret auf mein Tutorial bezogen "keine gute Idee" ist (OK, das habe ich in dem betreffenden Beitrag leider nicht dazu geschrieben, allgemein ist diese Aussage sicher nicht korrekt - hier scheint unser Hauptmissverständnis zu liegen; sorry also dafür), hat zwei Gründe:
- keine generischen Typen, da der FrameAssembler TCmdSeq (das ist nämlich die Stelle im Tut, um die es hier wirklich geht) vorhersagbare Framegrößen erzeugen soll (by-design)
- dein Ansatz, Basistypen per move() zu verarbeiten, mir (im Sinne des Tuts) zu nahe legt, das ganze auch mit Verbunddatenstrukturen zu tun, womit die FrameAssembler-Klasse ihre Allgemeingültigkeit verliert, weil sie zu stark ausdifferenziert
cu
Narses
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!