Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   array of const für spätere Verwendung speichern (https://www.delphipraxis.net/188104-array-const-fuer-spaetere-verwendung-speichern.html)

BigAl 31. Jan 2016 17:31

array of const für spätere Verwendung speichern
 
Hallo zusammen,

ich kämpfe hier gerade mit einem Problem. Ich habe eine Methode geschrieben, welche als Parameter "array of const" beinhaltet. Dieses "array of const" soll zu einem späteren Zeitpunkt an die "Format"-Funkion übergeben werden. "array of const" ist ja bekanntermaßen ein "array of TVarRec". Also habe ich das auch so deklariert (Strukturen und Code für bessere Übersichtlichkeit gekürzt):

Delphi-Quellcode:
  PRec = ^TRec;
  TRec = record
    Params: array of TVarRec;
  end;
In meiner Methode werden die Werte dann mit folgendem Code zwischengespeichert:

Delphi-Quellcode:
procedure Something(const Args: array of const);
var
  RecPtr: PRec;
begin
  New(RecPtr);
  SetLength(RecPtr^.Params, Length(Args));
  for I := Low(Args) to High(Args) do
    RecPtr^.Params[I] := Args[I];
end;
Soweit so gut. Der generierte Record wird einer Thread-Liste abgelegt und dann von einer anderen Thread verarbeitet. Das funktioniert auch alles bestens, solange ich Strings oder Konstante Werte in "Args" übergebe. Übergebe ich aber z.B. eine numerische Variable, so ist diese nur verfügbar, solange ich in der Methode "Something" bin. Beim Verlassen der Methode wird diese finalisiert und löst dann natürlich eine Exception aus, sobald auf diese zugegriffen wird. Ich habe das getestet indem ich in der Methode geblieben bin, bis die Thread die Parameter verarbeitet hatte...

Zum weiteren Verständnis: Die Thread holt die Records nacheinander vom Puffer (TThreadList) und verarbeitet diese asynchron zum aufrufenden Programm. Leider können die Args erst dort verarbeitet werden, da die endgültigen Formatstrings erst in der Thread verfügbar sind...

Mir fällt leider im Moment keine elegante Lösung ein. Irgendwie müsste ich mich da manuell um "Initialize" und "Finalize" kümmern. Hat jemand von euch eine Idee? Ich hoffe das Problem ist einigermaßen verständlich geschildert und ich habe mich beim "Pseudo"-Code nicht zu sehr vertippt :-).

PS: Eine Lösung wäre bei bestimmten Typen (z.B. VExtended) die als Pointer gespeichert werden diese mit New(RecPtr^.Params[I].VExtended) dynamisch zu allokieren und den Wert mit RecPtr^.Params[I].VExtended^ := Args[I].VExtended^ zu übernehmen. Später müsste dann dieser mit Dispose(RecPtr^.Params[I].VExtended) dann wieder freiegeben werden. Ist aber sehr umständlich und man müsste die richtigen Typen ermitteln und das bei allen (auch zukünftigen) Typen korrekt durchführen. Ist nicht sehr elegant.

Freue mich über alle Antworten!

Alex

Uwe Raabe 31. Jan 2016 18:42

AW: array of const für spätere Verwendung speichern
 
Kannst du einen minimalen Testfall bereitstellen?

himitsu 1. Feb 2016 09:47

AW: array of const für spätere Verwendung speichern
 
Was sind "nummerische Werte"?
Delphi-Quellcode:
  TVarRec = record { do not pack this record; it is compiler-generated }
    case Integer of
      0: (case Byte of
            vtInteger:      (VInteger: Integer);
            vtExtended:     (VExtended: PExtended);
            vtString:       (VString: PShortString);
            vtAnsiString:   (VAnsiString: Pointer);
            vtCurrency:     (VCurrency: PCurrency);
            vtVariant:      (VVariant: PVariant);
            vtWideString:   (VWideString: Pointer);
            vtInt64:        (VInt64: PInt64);
            vtUnicodeString: (VUnicodeString: Pointer);
Alles größer als Integer wird per Pointer referenziert und lebt nur so lange, wie es der aufrufende Code bereitstellt.
Strings sind referenzgezählt und in diesem Array wird die Referenz nicht mitgezählt (CONST) ... geht der Referenzzähler auf 0, so ist der String weg.

Fazit:
Du mußt selber einen Speicherplatz bereitstellen und die Inhalte umkopieren, wenn sie nicht im aktuellen Programmpfad behandelt werden.

z.B. ein paar Array of AnsiString/UnicodeString/Int64/Extended/Variant, auf deren Speicher dann deine TVarRec zeigen.
(alternativ nur Array of Variant und alles nach Variant konvertieren)

New/Dispose wären auch möglich, aber so mußt du dich nicht um das (interne) Speichermanagement kümmern.

Uwe Raabe 1. Feb 2016 12:31

AW: array of const für spätere Verwendung speichern
 
Ich würde das mal mit
Delphi-Quellcode:
TValue
versuchen. Der löst in
Delphi-Quellcode:
FromVarRec
die Referenzen auf die Pointer auf. Die Zuweisung kann in dem Fall sogar wegen
Delphi-Quellcode:
Implicit
so bleiben. Lediglich bei der Auswertung muss man dann halt was machen (z.B. über
Delphi-Quellcode:
AsVarRec
).

Delphi-Quellcode:
 PRec = ^TRec;
  TRec = record
    Params: array of TValue;
  end;
Delphi-Quellcode:
procedure Something(const Args: array of const);
var
  RecPtr: PRec;
begin
  New(RecPtr);
  SetLength(RecPtr^.Params, Length(Args));
  for I := Low(Args) to High(Args) do
    RecPtr^.Params[I] := Args[I];
end;

himitsu 1. Feb 2016 13:59

AW: array of const für spätere Verwendung speichern
 
TValue: Dieses Array kann man aber nicht an ein
Delphi-Quellcode:
array of const
übergeben?

Delphi-Quellcode:
procedure Something(const Args: array of const);
var
  RecPtr: PRec;
begin
  New(RecPtr);
  SetLength(RecPtr.Params, Length(Args));
  for I := Low(Args) to High(Args) do
    RecPtr.Params[I] := Args[I];
  //
  Format('', RecPtr.Params);
  Dispose(RecPtr);
end;
PS: Bei Record-Pointer kann man die Dereferenzierung weg lassen, wenn man auf Felder/Property/Funktionen zugreift, da Delphi das impliziet macht. Halt wie bei den Objekten, nur dass dort die explizite Angabe nicht erlaubt ist. :stupid:
Aber so lässt sich der Code später mal problemloser auf schöne Datenobjekte umstellen.

Uwe Raabe 1. Feb 2016 14:34

AW: array of const für spätere Verwendung speichern
 
Zitat:

Zitat von himitsu (Beitrag 1328969)
TValue: Dieses Array kann man aber nicht an ein
Delphi-Quellcode:
array of const
übergeben?

Nicht direkt, aber über eine Schleife lässt sich natürlich einfach wieder ein
Delphi-Quellcode:
array of TVarRec
aufbauen:

Delphi-Quellcode:
type
  TRec = record
  private
    function GetAsVarRecs: TArray<TVarRec>;
  public
    Params: array of TValue;
    constructor Create(const AParams: array of const);
    property AsVarRecs: TArray<TVarRec> read GetAsVarRecs;
  end;

constructor TRec.Create(const AParams: array of const);
var
  I: Integer;
begin
  SetLength(Params, Length(AParams));
  for I := Low(AParams) to High(AParams) do begin
    Params[I] := AParams[I];
  end;
end;

function TRec.GetAsVarRecs: TArray<TVarRec>;
var
  I: Integer;
begin
  SetLength(result, Length(Params));
  for I := Low(Params) to High(Params) do begin
    result[I] := Params[I].AsVarRec;
  end;
end;

procedure Test(const Fmt: string; const Args: array of const);
var
  rec: TRec;
  S: string;
begin
  rec := TRec.Create(Args);
  S := Format(Fmt, rec.AsVarRecs);
  Assert(S = Format(Fmt, Args));
end;


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