View Issue Details

IDProjectCategoryView StatusLast Update
0037088FPCRTLpublic2020-08-06 17:16
ReporterBi0T1N Assigned To 
PrioritynormalSeverityminorReproducibilityalways
Status newResolutionopen 
Product Version3.3.1 
Summary0037088: [Patch] Add TEnumerator to TStringBuilder to allow for-in loops
DescriptionThis patch allows iterating over TStringBuilder data via for-in loops and is Delphi compatible.
TagsNo tags attached.
Fixed in Revision
FPCOldBugId
FPCTarget
Attached Files

Activities

Bi0T1N

2020-05-16 22:38

reporter  

01-Add_TEnumerator_to_TStringBuilder_for_for-in_loops.patch (1,846 bytes)   
diff --git rtl/objpas/sysutils/syssb.inc rtl/objpas/sysutils/syssb.inc
index ae200cda88..4130e2f15f 100644
--- rtl/objpas/sysutils/syssb.inc
+++ rtl/objpas/sysutils/syssb.inc
@@ -36,6 +36,27 @@ begin
   Create(aValue,DefaultCapacity);
 end;
 
+{ Enumerator }
+
+function TStringBuilder.TCharEnumerator.GetCurrent: SBChar;
+begin
+  Result := FCurrentPosition^;
+  Inc(FCurrentPosition);
+end;
+
+function TStringBuilder.TCharEnumerator.MoveNext: Boolean;
+begin
+  Result := FCurrentPosition < FEndPosition;
+end;
+
+function TStringBuilder.GetEnumerator: TCharEnumerator;
+var
+  StartPosition: PSBChar;
+begin
+  StartPosition := @FData[0];
+  Result.FCurrentPosition := StartPosition;
+  Result.FEndPosition := StartPosition + Length;
+end;
 
 { Property getter/setter }
 
diff --git rtl/objpas/sysutils/syssbh.inc rtl/objpas/sysutils/syssbh.inc
index 9c9102a838..90570078ed 100644
--- rtl/objpas/sysutils/syssbh.inc
+++ rtl/objpas/sysutils/syssbh.inc
@@ -5,6 +5,16 @@
   private
     const
       DefaultCapacity = 64;
+    type
+      TCharEnumerator = record
+      private
+        FCurrentPosition: PSBChar;
+        FEndPosition: PSBChar;
+        Function GetCurrent: SBChar; inline;
+      public
+        Function MoveNext: Boolean; inline;
+        property Current: SBChar read GetCurrent;
+      end;
   private
     Function  GetCapacity: Integer;
     Procedure SetCapacity(AValue: Integer);
@@ -71,6 +81,8 @@
     Function EnsureCapacity(aCapacity: Integer): Integer;
     Function Equals(StringBuilder: TStringBuilder): Boolean; reintroduce;
 
+    Function GetEnumerator: TCharEnumerator; inline;
+
     Function Insert(Index: Integer; const AValue: Boolean): TStringBuilder;
     Function Insert(Index: Integer; const AValue: Byte): TStringBuilder;
     Function Insert(Index: Integer; const AValue: SBChar): TStringBuilder;
strbld_forin_tests.pas (774 bytes)   
program strbld_forin_tests;

{$mode Delphi}

uses
  SysUtils;

var
  c: Char;
  sb: TStringBuilder;
  i: Integer;

begin
  sb := TStringBuilder.Create;
  try
    sb.Append('Hello');
    sb.Append(' ');
    sb.Append('World!');
    sb.Append(' ');
    sb.Append('16052020');
    i := 0;
    for c in sb do
    begin
      writeln(c);
      if sb[i] <> c then halt(i);
      Inc(i);
    end;

    // test empty
    sb.clear;
    i := 0;
    for c in sb do
    begin
      writeln(c);
      if sb[i] <> c then halt(i);
      Inc(i);
    end;

    sb.Append('A');
    i := 0;
    for c in sb do
    begin
      writeln(c);
      if sb[i] <> c then halt(i);
      Inc(i);
    end;

  finally
    sb.Free;
  end;

  readln;
end.
strbld_forin_tests.pas (774 bytes)   

Bi0T1N

2020-08-06 00:03

reporter   ~0124594

Something wrong with the patch or just slipped through?

Sven Barth

2020-08-06 09:24

manager   ~0124607

Mainly slipped through I'd say...

Anyway, two remarks:
- is the TCharEnumerator named the same in Delphi? (cause I see that it has a GetEnumerator as well)
- in my opinion it's wrong to put the type into the private section if it's used in a public method, so better make that type public as well (I wish that FPC would be stricter here)

Sergey Larin

2020-08-06 15:07

reporter   ~0124617

Last edited: 2020-08-06 15:16

View 2 revisions

If TStringBuilder.TCharEnumerator will be public, then you can potentially write like this:

var
  SB: TStringBuilder;
  EN: TStringBuilder.TCharEnumerator;
begin
  SB := TStringBuilder.Create;
  ...
  EN := SB.GetEnumerator;
  SB.Free;
  ...
  EN.Current;

this will cause a runtime error.

Therefore, if there is no special reason, then it is better to keep TStringBuilder.TCharEnumerator private.

Bi0T1N

2020-08-06 17:15

reporter   ~0124620

Last edited: 2020-08-06 17:16

View 2 revisions

@Sven Barth
I think its named TEnumerator in Delphi as that is shown in the GetEnumerator (http://docwiki.embarcadero.com/Libraries/Rio/en/System.SysUtils.TStringBuilder.GetEnumerator) function. However, I choose TCharEnumerator as it clearly states what it does: enumerate through the chars (and e.g. not strings).

Issue History

Date Modified Username Field Change
2020-05-16 22:38 Bi0T1N New Issue
2020-05-16 22:38 Bi0T1N File Added: 01-Add_TEnumerator_to_TStringBuilder_for_for-in_loops.patch
2020-05-16 22:38 Bi0T1N File Added: strbld_forin_tests.pas
2020-08-06 00:03 Bi0T1N Note Added: 0124594
2020-08-06 09:24 Sven Barth Note Added: 0124607
2020-08-06 15:07 Sergey Larin Note Added: 0124617
2020-08-06 15:16 Sergey Larin Note Edited: 0124617 View Revisions
2020-08-06 17:15 Bi0T1N Note Added: 0124620
2020-08-06 17:16 Bi0T1N Note Edited: 0124620 View Revisions