Recent

Author Topic: [Solved] How to pass Dynamic Array of string to Format function  (Read 3385 times)

heejit

  • Full Member
  • ***
  • Posts: 245
[Solved] How to pass Dynamic Array of string to Format function
« on: October 23, 2018, 05:42:52 pm »
Or

How do I convert Dynamic Array of String to Array of Const ??
« Last Edit: October 23, 2018, 07:05:35 pm by jshah »

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Re: How to pass Dynamic Array of string to Format function
« Reply #1 on: October 23, 2018, 06:22:24 pm »
Not as easy as you think!
Here is some code from one of the programming dentists (Rudy Velthuis in this case)
Code: Pascal  [Select][+][-]
  1. {$ifdef fpc}{$mode objfpc}{$H+}{$endif}
  2. uses sysutils;
  3. type
  4.   TConstArray = array of TVarRec;
  5.  
  6. // Copies a TVarRec and its contents. If the content is referenced
  7. // the value will be copied to a new location and the reference
  8. // updated.
  9. function CopyVarRec(const Item: TVarRec): TVarRec;
  10. var
  11.   W: WideString;
  12. begin
  13.   // Copy entire TVarRec first
  14.   Result := Item;
  15.   // Now handle special cases
  16.   case Item.VType of
  17.     vtExtended:
  18.       begin
  19.         New(Result.VExtended);
  20.         Result.VExtended^ := Item.VExtended^;
  21.       end;
  22.     vtString:
  23.       begin
  24.         // Improvement suggestion by Hallvard Vassbotn: only copy real length.
  25.         GetMem(Result.VString, Length(Item.VString^) + 1);
  26.         Result.VString^ := Item.VString^;
  27.       end;
  28.     vtPChar:
  29.       Result.VPChar := StrNew(Item.VPChar);
  30.     // There is no StrNew for PWideChar
  31.     vtPWideChar:
  32.       begin
  33.         W := Item.VPWideChar;
  34.         GetMem(Result.VPWideChar,
  35.                (Length(W) + 1) * SizeOf(WideChar));
  36.         Move(PWideChar(W)^, Result.VPWideChar^,
  37.              (Length(W) + 1) * SizeOf(WideChar));
  38.       end;
  39.     // A little trickier: casting to AnsiString will ensure
  40.     // reference counting is done properly.
  41.     vtAnsiString:
  42.       begin
  43.         // nil out first, so no attempt to decrement reference count.
  44.         Result.VAnsiString := nil;
  45.         AnsiString(Result.VAnsiString) := AnsiString(Item.VAnsiString);
  46.       end;
  47.     vtCurrency:
  48.       begin
  49.         New(Result.VCurrency);
  50.         Result.VCurrency^ := Item.VCurrency^;
  51.       end;
  52.     vtVariant:
  53.       begin
  54.         New(Result.VVariant);
  55.         Result.VVariant^ := Item.VVariant^;
  56.       end;
  57.     // Casting ensures proper reference counting.
  58.     vtInterface:
  59.       begin
  60.         Result.VInterface := nil;
  61.         IInterface(Result.VInterface) := IInterface(Item.VInterface);
  62.       end;
  63.     // Casting ensures a proper copy is created.
  64.     vtWideString:
  65.       begin
  66.         Result.VWideString := nil;
  67.         WideString(Result.VWideString) := WideString(Item.VWideString);
  68.       end;
  69.     vtInt64:
  70.       begin
  71.         New(Result.VInt64);
  72.         Result.VInt64^ := Item.VInt64^;
  73.       end;
  74.     vtUnicodeString:
  75.       begin
  76.         // Similar to AnsiString.
  77.         Result.VUnicodeString := nil;
  78.         UnicodeString(Result.VUnicodeString) := UnicodeString(Item.VUnicodeString);
  79.       end;
  80.     // VPointer and VObject don't have proper copy semantics so it
  81.     // is impossible to write generic code that copies the contents
  82.   end;
  83. end;
  84.  
  85. function CreateConstArray(const Elements: array of const): TConstArray;
  86. var
  87.   I: Integer;
  88. begin
  89.   SetLength(Result, Length(Elements));
  90.   for I := Low(Elements) to High(Elements) do
  91.     Result[I] := CopyVarRec(Elements[I]);
  92. end;
  93.  
  94. // use this function on copied TVarRecs only!
  95. procedure FinalizeVarRec(var Item: TVarRec);
  96. begin
  97.   case Item.VType of
  98.     vtExtended:
  99.       Dispose(Item.VExtended);
  100.     vtString:
  101.       Dispose(Item.VString);
  102.     vtPChar:
  103.       StrDispose(Item.VPChar);
  104.     vtPWideChar:
  105.       FreeMem(Item.VPWideChar);
  106.     vtAnsiString:
  107.       AnsiString(Item.VAnsiString) := '';
  108.     vtCurrency:
  109.       Dispose(Item.VCurrency);
  110.     vtVariant:
  111.       Dispose(Item.VVariant);
  112.     vtInterface:
  113.       IInterface(Item.VInterface) := nil;
  114.     vtWideString:
  115.       WideString(Item.VWideString) := '';
  116.     vtInt64:
  117.       Dispose(Item.VInt64);
  118.     vtUnicodeString:
  119.       UnicodeString(Item.VUnicodeString) := '';
  120.   end;
  121.   Item.VInteger := 0;
  122. end;
  123.  
  124. // A TConstArray contains TVarRecs that must be finalized. This function
  125. // does that for all items in the array.
  126. procedure FinalizeVarRecArray(var Arr: TConstArray);
  127. var
  128.   I: Integer;
  129. begin
  130.   for I := Low(Arr) to High(Arr) do
  131.     FinalizeVarRec(Arr[I]);
  132.   Arr := nil;
  133. end;
  134.  
  135. var
  136.   ConstArray: TConstArray;
  137.  
  138. begin
  139.   ConstArray := CreateConstArray([1, 'Hello', 7.9, IntToStr(1234)]);
  140.   try
  141.     WriteLn(Format('%d --- %s --- %0.2f --- %s', ConstArray));
  142.     Writeln(Format('%s --- %0.2f', Copy(ConstArray, 1, 2)));
  143.   finally
  144.     FinalizeVarRecArray(ConstArray);
  145.   end;
  146.   ReadLn;
  147. end.
Specialize a type, not a var.

ASerge

  • Hero Member
  • *****
  • Posts: 2222
Re: How to pass Dynamic Array of string to Format function
« Reply #2 on: October 23, 2018, 06:32:19 pm »
How do I convert Dynamic Array of String to Array of Const ??
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2. {$LONGSTRINGS ON}
  3. {$APPTYPE CONSOLE}
  4.  
  5. uses SysUtils;
  6.  
  7. function FormatStrings(const FormatStr: string; const Args: array of string): string;
  8. var
  9.   VA: array of TVarRec;
  10.   i: Integer;
  11. begin
  12.   SetLength(VA, Length(Args));
  13.   for i := 0 to High(VA) do
  14.     with VA[i] do
  15.     begin
  16.       VType := vtAnsiString;
  17.       VAnsiString := Pointer(Args[i]);
  18.     end;
  19.   Result := Format(FormatStr, VA);
  20. end;
  21.  
  22. var
  23.   SA: array of string;
  24. begin
  25.   SetLength(SA, 2);
  26.   SA[0] := '0';
  27.   SA[1] := '1';
  28.   Writeln(FormatStrings('%s <> %s', SA));
  29.   Readln;
  30. end.

heejit

  • Full Member
  • ***
  • Posts: 245
Re: How to pass Dynamic Array of string to Format function
« Reply #3 on: October 23, 2018, 07:05:19 pm »
@Thaddy : Thanks

@ASerge : Thanks

I will use ASerge solution

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1312
    • Lebeau Software
Re: How to pass Dynamic Array of string to Format function
« Reply #4 on: October 24, 2018, 09:39:35 pm »
Code: Pascal  [Select][+][-]
  1. function FormatStrings(const FormatStr: string; const Args: array of string): string;

Since the function is setting the TVarRec.VType to a hard-coded vtAnsiString, I would suggest using array of AnsiString (or even array of RawByteString) to match:

Code: Pascal  [Select][+][-]
  1. function FormatStrings(const FormatStr: string; const Args: array of AnsiString): string;
  2. var
  3.   VA: array of TVarRec;
  4.   i: Integer;
  5. begin
  6.   SetLength(VA, Length(Args));
  7.   for i := 0 to High(VA) do
  8.     with VA[i] do
  9.     begin
  10.       VType := vtAnsiString;
  11.       VAnsiString := Pointer(Args[i]);
  12.     end;
  13.   Result := Format(FormatStr, VA);
  14. end;

Otherwise, the code will fail when string is UnicodeString when using {$Mode DelphiUnicode} or {$ModeSwitch UnicodeStrings}.

Alternatively, use {$IFDEF FPC_UNICODESTRINGS} to set the VType to either vtUnicodeString or vtAnsiString accordingly:

Code: Pascal  [Select][+][-]
  1. function FormatStrings(const FormatStr: string; const Args: array of string): string;
  2. var
  3.   VA: array of TVarRec;
  4.   i: Integer;
  5. begin
  6.   SetLength(VA, Length(Args));
  7.   for i := 0 to High(VA) do
  8.     with VA[i] do
  9.     begin
  10.       {$IFDEF FPC_UNICODESTRINGS}
  11.       VType := vtUnicodeString;
  12.       VUnicodeString := Pointer(Args[i]);
  13.       {$ELSE}
  14.       VType := vtAnsiString;
  15.       VAnsiString := Pointer(Args[i]);
  16.       {$ENDIF}
  17.     end;
  18.   Result := Format(FormatStr, VA);
  19. end;
« Last Edit: October 24, 2018, 09:41:42 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

heejit

  • Full Member
  • ***
  • Posts: 245
Re: [Solved] How to pass Dynamic Array of string to Format function
« Reply #5 on: October 24, 2018, 10:42:34 pm »
@Remy Lebeau : Thanks

 

TinyPortal © 2005-2018