Recent

Author Topic: Added Tostring and fromString helper for TRect, Opinions and thoughts?  (Read 2364 times)

jamie

  • Hero Member
  • *****
  • Posts: 6128
I needed to create a comma string for a Trect to store values in text format and noticed the TRect
helper does not have those...
 After looking around on the net I found the FMX.utls in Delphi has such functions close to that idea..
so I added this as an helper to the already helper in one of my common units for a program I have.
Code: Pascal  [Select][+][-]
  1. TRectHelper = Record Helper for  Trect
  2.    Function FromString(S:String):TRect;
  3.    Function ToString:String;
  4.   End;            
  5. Implementation
  6. Function ParseForInt(S:String;Var Index:Integer;Out NoMore:Boolean):Integer;
  7. Var
  8.   W:String;
  9. Begin
  10.   While (Index <= Length(S))and ( S[Index] in['-',' ','0'..'9'] ) Do
  11.     Begin
  12.       if S[Index] <> ' ' Then W := W+S[Index];
  13.       Inc(Index);
  14.     End;
  15.  Inc(Index);
  16.  NoMore := (Index > Length(S));
  17.  Result := W.ToInteger;
  18. End;
  19. Function TRectHelper.FromString(S:String):TRect;
  20.  Var
  21.   I:Integer;
  22.   NoMore :Boolean;
  23.  begin
  24.   Result :=TRect.Create(0,0,100,100);
  25.   I := 1;
  26.   Result.Left := ParseForInt(S,I,NoMore);
  27.   If Not NoMore Then
  28.   Result.Top := ParseForInt(S,I,NoMore);
  29.   If Not NoMore Then
  30.   result.Right := ParseForInt(S,I,NoMore);
  31.   if Not NoMore then
  32.   Result.Bottom := ParseForInt(S,I,NoMore);
  33.  end;
  34. Function TRectHelper.ToSTring:String;
  35. Begin
  36.     Result := Left.Tostring+','+Top.Tostring+','+Right.ToString+','+Bottom.Tostring;
  37. end;                                  
  38.  

  I am sure I missed something somewhere but as it stands it seems to work.
currenly I am using it to read and write a string in a IniFile but it has other usages.

 I plan on doing this to Tpoint also

Question:
  Is there any way to detect if SELF is valid ?
  because If I attempt to set Self while in there it crashes of course if I do this from the Record and not
from an instance.

 Example
   
  BoundsRect := Trect.fromString(S);

  Inside the Trect.FromString(S); I also tried to set the SELF along with the Results so it would act as a
double duty function, but I can't see how I can tell the difference between TRect and a RectName.FromString..

 Currently it looks like it is using the SELF from the caller if I do the TRect....
 
 I thought maybe the SELF parameter would be Nil in this case but it is not..
The only true wisdom is knowing you know nothing

ASerge

  • Hero Member
  • *****
  • Posts: 2240
1. In accordance with the coding style, all reserved words should be in lower case. Not If, String, Function, Record and so on, but if, string, function, record ans so on.
2. Always use const for string parameters.
3. ParseForInt duplicate the Val procedure behaviour. Use the Val.
4. TRect is a value, not a reference type. Those, forgot about nil.
5. Declaration
Code: Pascal  [Select][+][-]
  1. function TRect.FromString(const S: string): TRect;
  2. begin
  3. end;
almost equivalent to
Code: Pascal  [Select][+][-]
  1. procedure FromString(var Self: TRect; const S: string; var Result: TRect);
  2. begin
  3.   with Self do
  4.   begin
  5.   end;
  6. end;
Based on this you will understand what and how.

jamie

  • Hero Member
  • *****
  • Posts: 6128
The reason I didn't use VAL is because I would them need to partition the string or using a
Copy via the last VAL E return and hope the value is correct.

 I've seen some random behavior using VAL with the Error value..

So what I did here was to first set the string Index on the first call and then simply keep
calling and have the index keep moving in the Parser.

 This way I do not need the extra code of singling out the numbers using a Copy to pass to
VAL.

 I will look into experimenting with a version like that using VAL, I was kind of being performance
conscience.

 I also noticed I didn't use CONST to allow the compiler to pass via pointer for some extra speed..
I'll take care of that  :)

Thanks
The only true wisdom is knowing you know nothing

ASerge

  • Hero Member
  • *****
  • Posts: 2240
I understood you.
Variable W is also a piece of a string that you did not want to do with Copy.
The while loop can be replaced with one call Val, and .ToString the second.
And since Self is not used, it is better to declare so:
Code: Pascal  [Select][+][-]
  1. class function FromString(const S: string): TRect; static;
Sample:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2. {$APPTYPE CONSOLE}
  3. {$MODE OBJFPC}
  4. {$MODESWITCH ADVANCEDRECORDS}
  5.  
  6. uses Types, SysUtils;
  7.  
  8. type
  9.   TRectHelper = record helper for TRect
  10.     class function FromString(const S: string): TRect; static;
  11.     function ToString(const Delim: string = ','): string;
  12.   end;
  13.  
  14. function ExtractInt(var S: string; out Value: Integer): Boolean;
  15. var
  16.   ErrPos, CountToDel: Integer;
  17. begin
  18.   Val(S, Value, ErrPos);
  19.   if ErrPos = 0 then
  20.     S := ''
  21.   else
  22.   begin
  23.     CountToDel := ErrPos;
  24.     Val(Copy(S, 1, CountToDel - 1), Value, ErrPos);
  25.     Delete(S, 1, CountToDel)
  26.   end;
  27.   Result := (ErrPos = 0);
  28. end;
  29.  
  30. class function TRectHelper.FromString(const S: string): TRect;
  31. var
  32.   W: string;
  33.   V, i: Integer;
  34. begin
  35.   Result := TRect.Empty;
  36.   W := S;
  37.   for i := Low(Result.Vector) to High(Result.Vector) do
  38.     if ExtractInt(W, V) then
  39.       Result.Vector[i] := V
  40.     else
  41.       Break;
  42. end;
  43.  
  44. function TRectHelper.ToString(const Delim: string): string;
  45. begin
  46.   Result := Format('%d%s%d%s%d%s%d', [Left, Delim, Top, Delim, Right, Delim, Bottom]);
  47. end;
  48.  
  49. var
  50.   R: TRect;
  51. begin
  52.   R := TRect.FromString('1, 2, 3,');
  53.   Writeln(R.ToString);
  54.   Readln;
  55. end.

 

TinyPortal © 2005-2018