Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Generic record (https://www.delphipraxis.net/166902-generic-record.html)

bernhard_LA 5. Mär 2012 10:42

Generic record
 
Ich möchte mit Hilfe von GENERICS beliebige Records in ein ByteArray und wieder zurück umwandeln .
Für die Lösung wollte ich zum ersten mal GENERICS in Delphi verwenden.

Der Code unten spielt wenn ich anstelle von TGenericRecord eine beliebige Record Definition direkt einsetze.
Habe ich einen Denkfehler in meinem Ansatz ?




Delphi-Quellcode:

type
   TGenericRecord <TRecordType> = class
              /// value : TRecordType;
   end;


///
/// ---------------   HELPER FUNCTION FOR RECORD EXCHANGE ------------------
///

function MyRecordToByteArray(aRecord: TGenericRecord): TBytes;
var
  LSource: PAnsiChar;
begin
  LSource := PAnsiChar(@aRecord);
  SetLength(Result, SizeOf(TGenericRecord));
  Move(LSource[0], Result[0], SizeOf(TGenericRecord));
end;

function ByteArrayToMyRecord(ABuffer: TBytes): TGenericRecord;
var
  LDest: PAnsiChar;
begin
  LDest := PAnsiChar(@Result);
  Move(ABuffer[0], LDest[0], SizeOf(TGenericRecord));
end;

himitsu 5. Mär 2012 10:49

AW: Generic record
 
Dein genric Record ist eine generic Class :shock:

TGenericRecord<x> entspricht nicht TGenericRecord

Delphi-Quellcode:
function MyRecordToByteArray<X>(aRecord: TGenericRecord<x>): TBytes;
, da aber keine generischen Prozeduren möglich sind, muß dieses eine Methode eines Records oder eines Objekts werden.

statt
Zitat:

SizeOf(TGenericRecord)
muß es dann statt
Zitat:

SizeOf(TGenericRecord<X>)
werden, oder einfach nur statt
Zitat:

SizeOf(aRecord)
.

Stevie 5. Mär 2012 11:06

AW: Generic record
 
Ich glaub, du möchtest so etwas:

Delphi-Quellcode:
type
  TRecordSerializer = class
    class function ToByteArray<T: record>(const Value: T): TBytes; static;
    class function ToRecord<T: record>(const Value: TBytes): T; static;
  end;

class function TRecordSerializer.ToByteArray<T>(const Value: T): TBytes;
begin
  SetLength(Result, SizeOf(T));
  Move(Value, Result[0], SizeOf(T));
end;

class function TRecordSerializer.ToRecord<T>(const Value: TBytes): T;
begin
  Move(Value[0], Result, SizeOf(T));
end;
Denke aber bei diesem Ansatz auf jeden Fall daran, dass du damit keine managed types (z.B. string) behandeln kannst. Denn in dem Record befindet sich in diesem Fall nur ein Pointer auf den string.

bernhard_LA 5. Mär 2012 11:38

AW: Generic record
 
@ Stevie : in meinen records können leider auch Strings vorkommen .... kann damit Deine Lösung nicht verwenden
@ Himitsu : wenn ich keine Generic Typs als Funktions Rückgabewert verwenden kann --> dann muss ich wohl auf das gute & alte
function overloading zurückgreifen :oops:;



im Code unten maul der Compiler "TRecordType" kennt er nicht, ist doch klar.... kenne ich gerade selbst noch nicht .... sage ich dem Compiler schon und zwar dann wenn ich die Funktion auch verwenden will .... mit TGenericRecord<TAnwerndungsrecord>.Create, solange soll er halt warten


Delphi-Quellcode:

function MyRecordToByteArray(aRecord: TGenericRecord<TRecordType>): TBytes;
var
  LSource: PAnsiChar;
begin
  LSource := PAnsiChar(@aRecord);
  SetLength(Result, SizeOf(TMyRecord));
  Move(LSource[0], Result[0], SizeOf(TGenericRecord<TRecordType>));
end;

function ByteArrayToMyRecord(ABuffer: TBytes): TGenericRecord<TRecordType>;
var
  LDest: PAnsiChar;
begin
  LDest := PAnsiChar(@Result);
  Move(ABuffer[0], LDest[0], SizeOf(TGenericRecord<TRecordType>));
end;

himitsu 5. Mär 2012 12:00

AW: Generic record
 
Zurückgeben kann man es, aber
Delphi-Quellcode:
function MyFunction<T>(...): ...;
ist nicht möglich ... das geht leider nur bei Methoden (Record oder Object).

bernhard_LA 5. Mär 2012 12:11

AW: Generic record
 
was Delphi auf nicht compiliert :-(

Delphi-Quellcode:

type TRecordTypeA= ....
     ....
     ...
     ..


// gib mir ein record type 1 zurück
function testfunction (a : Integer) : TRecordTypeA; overload;
//  nun type 2 
function testfunction (a : Integer) : TRecordTypeB; overload;
//  usw ....
function testfunction (a : Integer) : TRecordTypeC; overload;


müsste ich etwas wie
Delphi-Quellcode:

procedure (a : Integer;  var TGenericRecordType<T> );
codieren.


bzw.
Zitat:

da aber keine generischen Prozeduren möglich sind, muß dieses eine Methode eines Records oder eines Objekts werden


TGenericTRecord <TRecordtype>= class

function ByteArrayToMyRecord(ABuffer: TBytes): TRecordtype
end;

himitsu 5. Mär 2012 12:23

AW: Generic record
 
Natürlich nicht.
Beim Überladen muß sich die Aufrufsignatur unterscheifen, also die Parameter müssen unterschiedlich sein.
Bei dir sind sie aber gleich.

Zitat:

da aber keine generischen Prozeduren möglich sind, muß dieses eine Methode eines Records oder eines Objekts werden
Delphi-Quellcode:
// geht
TGenericTRecord<TRecordtype> = class
  function ByteArrayToMyRecord(ABuffer: TBytes): TRecordtype;
  class function ByteArrayToMyRecord(ABuffer: TBytes): TRecordtype; static;
end;

// geht
TGenericTRecord = class
  function ByteArrayToMyRecord<TRecordtype>(ABuffer: TBytes): TRecordtype;
  class function ByteArrayToMyRecord<TRecordtype>(ABuffer: TBytes): TRecordtype; static;
end;

// geht nicht
function ByteArrayToMyRecord<TRecordtype>(ABuffer: TBytes): TRecordtype;

Stevie 5. Mär 2012 12:44

AW: Generic record
 
Zitat:

Zitat von bernhard_LA (Beitrag 1154570)
@ Stevie : in meinen records können leider auch Strings vorkommen .... kann damit Deine Lösung nicht verwenden

Ja, genau wie alle anderen Ansätze in diesem Thread, die auf SizeOf, Move und ähnlichem basieren

Pitschki1801 5. Mär 2012 13:44

AW: Generic record
 
schau mal hier rein vllt bringt dich das weiter Tut-Generics

bernhard_LA 5. Mär 2012 13:52

AW: Generic record
 
@himitsu :

warum benötige ich
Zitat:

class function ByteArrayToMyRecord(ABuffer: TBytes): TRecordType; static;
anbei meine aktuelle Lösung, scheint zu spielen ... im simplen Quick&Dirty - Test







Delphi-Quellcode:

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;



type

  TMyRecordA = record
    Details: string[255];
    FileName: string[255];
    FileDate: TDateTime;
    FileSize: Integer;
    Recordsize: Integer;
  end;

  TMyRecordB = record
    Details: string[255];
    x: Real;
    y: Real;
    z: Real;
  end;


  TMyRecordC = record
    FileDate: TDateTime;
    FileSize: Integer;
    x: Real;
    y: Real;
    z: Real;
  end;


  TGenericRecord<TRecordType> = class


    value : TRecordType ;

    /// <summary>
    /// Convert Byte to Record , outputtype : TRecordType
    /// </summary>
    /// <param name="ABuffer">
    /// Byte Array
    /// </param>
    function ByteArrayToMyRecord(ABuffer: TBytes): TRecordType;
    // class function ByteArrayToMyRecord(ABuffer: TBytes): TRecordType; static;

    /// <summary>
    /// Store a Record into a BYTE Array
    /// </summary>
    /// <param name="aRecord">
    /// adjust this record definition to your needs
    /// </param>
    function MyRecordToByteArray(aRecord: TRecordType): TBytes;
    // class function MyRecordToByteArray(aRecord: TRecordType): TBytes; static;

  end;


var
  Form1: TForm1;

implementation

{$R *.dfm}



function TGenericRecord<TRecordType>.MyRecordToByteArray(aRecord: TRecordType): TBytes;
var
  LSource: PAnsiChar;
begin
  LSource := PAnsiChar(@aRecord);
  SetLength(Result, SizeOf(TRecordType));
  Move(LSource[0], Result[0], SizeOf(TRecordType));
end;

function TGenericRecord<TRecordType>.ByteArrayToMyRecord(ABuffer: TBytes): TRecordType;
var
  LDest: PAnsiChar;
begin
  LDest := PAnsiChar(@Result);
  Move(ABuffer[0], LDest[0], SizeOf(TRecordType));
end ;


procedure TForm1.Button1Click(Sender: TObject);
var a_record  : TGenericRecord<TMyRecordA>;
     b_record  : TGenericRecord<TMyRecordB>;
     c_record  : TGenericRecord<TMyRecordC>;


     ABuffer  : TBytes;
begin


     ///  create class
     a_record :=TGenericRecord<TMyRecordA>.Create;
     b_record :=TGenericRecord<TMyRecordB>.Create;
     c_record :=TGenericRecord<TMyRecordC>.Create;




     /// einb paar werte zuweisen .....
     a_record.value.Details := '#1 bla bla';
     a_record.value.FileName := '#2 bla bla';
     a_record.value.Recordsize := 100;
     a_record.value.FileSize := 222;


     b_record.value.x := 1;
     b_record.value.y := 2;
     b_record.value.z := 3;



      ///  im buffer daten zwischenlagern
     ABuffer := a_record.MyRecordToByteArray(a_record.value);


     /// löschen oder mit anderen daten arbeiten
     a_record.value.Details := '.....';
     a_record.value.FileName := '----';
     a_record.value.Recordsize := -1;
     a_record.value.FileSize := -2;



     //  Daten wieder aus dem Buffer zurücklesen
     a_record.value := a_record.ByteArrayToMyRecord(ABuffer);


     /// alte daten sind wieder da :-)
     memo1.lines.add( a_record.value.Details) ;
     memo1.lines.add( a_record.value.Filename) ;

     ///  free class
     a_record.Free;
     b_record.Free;
     c_record.Free;

end;

end

Stevie 5. Mär 2012 14:06

AW: Generic record
 
Da du in deinen records shortstrings (string[n]) benutzt, geht die von mir vorgeschlagene Lösung sehr wohl.

Hat auch den Vorteil, dass du keinen extra Datentyp (die Wrapperklasse für den Record) brauchst:

Delphi-Quellcode:
var
  a_record : TMyRecordA;
  b_record : TMyRecordB;
  c_record : TMyRecordC;

  ABuffer : TBytes;
begin
  // ein paar werte zuweisen .....
  a_record.Details := '#1 bla bla';
  a_record.FileName := '#2 bla bla';
  a_record.Recordsize := 100;
  a_record.FileSize := 222;


  b_record.x := 1;
  b_record.y := 2;
  b_record.z := 3;


  // im buffer daten zwischenlagern
  ABuffer := TRecordSerializer.ToByteArray<TMyRecordA>(a_record);
  // du kannst in diesem Fall das <TMyRecordA> auch weglassen,
  // da der Compiler das durch den Typen des übergebenen Parameter erkennt
  // Stichwort Type Inference


  // löschen oder mit anderen daten arbeiten
  a_record.Details := '.....';
  a_record.FileName := '----';
  a_record.Recordsize := -1;
  a_record.FileSize := -2;


  // Daten wieder aus dem Buffer zurücklesen
  a_record := TRecordSerializer.ToRecord<TMyRecordA>(ABuffer);


  // alte daten sind wieder da :-)
  memo1.lines.add( a_record.Details);
  memo1.lines.add( a_record.Filename);
end;

Panthrax 5. Mär 2012 14:15

AW: Generic record
 
Liste der Anhänge anzeigen (Anzahl: 1)
Delphi-Quellcode:
type
  { Generika sind nicht nötig: }
  TMeinRecord = record
    Value: TBeliebigerFeldtyp;
    // mehr beliebige Feldtypen

    class operator Implicit(const Value: TMeinRecord): TBytes;
    class operator Implicit(const Value: TBytes): TMeinRecord;
  end;

class operator TMeinRecord.Implicit(const Value: TMeinRecord): TBytes;
var
  Size: NativeInt;
begin
  Size := SizeOf(Value);

  SetLength(Result, Size);

//  if Size > 0 then
    Move(Value, Result[0], Size);
end;

class operator TMeinRecord.Implicit(const Value: TBytes): TMeinRecord;
var
  Size: NativeInt;
begin
  Size := Length(Value) { entspricht Größe in Bytes };

  if Size <> SizeOf(Result) then
    raise Exception.Create('Ungültige Feldgröße');

  Move(Value[0], Result, Size);
end;
Eine Konsolenanwendung ist beigefügt.

Stevie 5. Mär 2012 15:10

AW: Generic record
 
Zitat:

Zitat von Panthrax (Beitrag 1154592)
Generika sind nicht nötig

Das Problem ist bei dir, dass du für jeden Recordtyp diesen Code schreiben musst. Mit Generics kannst du eben dieses vermeiden, da die entsprechenden Umwandlungsfunktionen mit jedem beliebigen Record arbeiten.

Panthrax 5. Mär 2012 15:49

AW: Generic record
 
Ja, stimmt,
Delphi-Quellcode:
Move
arbeitet mit keinem (und deshalb mit jedem) (Feld-) Datentyp. Im angehängten Quelltext ist das auch bemerkt. Ich wollte nicht fragen, wofür man das braucht, obwohl ich diese Frage wirklich im Kopf habe. (Bitte keine Antworten.) Stattdessen habe ich mich entschieden, etwas vorzuschlagen.

Wenn man es auf Feldtypen allgemein ausrichten will:
Delphi-Quellcode:
type
  Fieldtype = record
    class function ToBytes(const Value; const Size: NativeInt): TBytes; overload; static;
    class function ToBytes<T>(const Value: T): TBytes; overload; static;
    class function FromBuffer<T>(const Value; const Size: NativeInt): T; static;
    class function FromBytes<T>(const Value: TBytes): T; static;
  end;
Irgendwie sind alle Aufrufe schon ziemlich nah am
Delphi-Quellcode:
Move
...


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:27 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