Search code examples
delphidelphi-xe2

Is possible to declare string type with restricted length that doesn't start from 0/1?


In Delphi it is possible to declare subranges for integer values. For example:

type
  myInt = 2..150

Which restricts values of myInt type to values from 2 to 150. But what if I want to restrict the length of a string?

If I write:

type 
  myString = string [150]

I declare mystring to be 150 bytes long and restrict the length to be from 0, 1, 2, etc. up to 150. But how do I restrict the length to between 2 and 150, for example? Of course, I can check the length of a string and raise an exception, but does Delphi include some syntax specific to this situation, similar in style to subranges?

This obviously does not work, but I would like something like:

type
  myString = string[2..150] 

If it is not possible, then I can just check length, raise exception, etc.


trying this code:

var
 str1, str2, str3: TRestrictedString;
begin
  str1.Create(2, 5, 'pp');
  str2.Create(2, 5, 'aaaa');
  str3.Create(2, 10, str1 + str2);
  writeln (str3.getstring)
end

or:

var
 str1, str2, str3: TRestrictedString;
begin
  str1.Create(2, 5, 'pp');
  str2.Create(2, 5, 'aaaa');
  str3.Create(2, 10);
  str3.SetString(str1 + str2);
  writeln (str3.getstring)
end

or:

var
 str1, str2, str3: TRestrictedString;
begin
  str1.Create(2, 5, 'pp');
  str2.Create(2, 5, 'aaaa');
  str3.Create(2, 10);
  str3 := str1 + str2;
  writeln(str3.GetString);
end

All of these raise an exception. Is possible to solve this? For multiple operations on a string is it necessary to split the function into more parts? In the constructor, is it better to add a check that minlength < maxlength? If I set minlength > maxlength it raises an exception.


Solution

  • I would do

    type
      TRestrictedString = record
      strict private type
        TBounds = record
          MinLength,
          MaxLength: integer;
        end;
      strict private
        FStr: string;
      public
        Bounds: TBounds;
        procedure SetString(const AString: string);
        function GetString: string;
        constructor Create(AMinLength, AMaxLength: integer); overload;
        constructor Create(AMinLength, AMaxLength: integer; const AString: string); overload;
        constructor Create(const AString: string); overload;
        class operator Implicit(S: string): TRestrictedString;
        class operator Implicit(S: TRestrictedString): string;
        class operator Equal(const A, B: TRestrictedString): boolean;
        class operator NotEqual(const A, B: TRestrictedString): boolean;
        class operator Add(const A, B: TRestrictedString): TRestrictedString;
      end;
    
    { TRestrictedString }
    
    
    constructor TRestrictedString.Create(AMinLength, AMaxLength: integer);
    begin
      Bounds.MinLength := AMinLength;
      Bounds.MaxLength := AMaxLength;
      FStr := '';
    end;
    
    constructor TRestrictedString.Create(AMinLength, AMaxLength: integer;
      const AString: string);
    begin
      Bounds.MinLength := AMinLength;
      Bounds.MaxLength := AMaxLength;
      SetString(AString);
    end;
    
    class operator TRestrictedString.Add(const A,
      B: TRestrictedString): TRestrictedString;
    begin
      result.Bounds := A.Bounds;
      result.SetString(A.GetString + B.GetString);
    end;
    
    constructor TRestrictedString.Create(const AString: string);
    begin
      Bounds.MinLength := 0;
      Bounds.MaxLength := MaxInt;
      FStr := AString;
    end;
    
    class operator TRestrictedString.Equal(const A, B: TRestrictedString): boolean;
    begin
      result := A.GetString = B.GetString;
    end;
    
    function TRestrictedString.GetString: string;
    begin
      result := FStr;
    end;
    
    class operator TRestrictedString.Implicit(S: TRestrictedString): string;
    begin
      result := S.GetString;
    end;
    
    class operator TRestrictedString.NotEqual(const A,
      B: TRestrictedString): boolean;
    begin
      result := A.GetString <> B.GetString;
    end;
    
    class operator TRestrictedString.Implicit(S: string): TRestrictedString;
    begin
      result.Create(S);
    end;
    
    procedure TRestrictedString.SetString(const AString: string);
    begin
      with Bounds do
        if (length(AString) < MinLength) or (length(AString) > MaxLength) then
          raise Exception.Create('Invalid length of string.');
      FStr := AString;
    end;
    

    Now you can do very natural things, like

    procedure TForm1.Button1Click(Sender: TObject);
    var
      str: TRestrictedString;
    begin
      str.Create(5, 10);         // Create a string w/ length 5 to 10 chrs
      str.SetString('Testing!'); // Assign a compatible string
      ShowMessage(str);          // Display the string
    end;
    

    You can also do just

    str.Create(5, 10, 'Testing!');
    ShowMessage(str);
    

    You can add string the usual way:

    var
      s1, s2, s3: TRestrictedString;
    begin
      s1.Create(2, 10, 'Hi ');
      s2.Create(2, 10, 'there!');
      s3 := s1 + s2;
      ShowMessage(s3);
    end;
    

    or even

    var
      s1, s3: TRestrictedString;
    begin
      s1.Create(2, 10, 'Hi ');
      s3 := s1 + 'there!';
      ShowMessage(s3);
    

    When you add two TRestrictedStrings, or one TRestrictedString and a string, the result will have the same restriction as the first operand. You can try

    var
      str: TRestrictedString;
    begin
      str.Create(5, 10);
      str.SetString('Testing!');
      str := str + '!!';
      ShowMessage(str);
    

    which will work, but not

    var
      str: TRestrictedString;
    begin
      str.Create(5, 10);
      str.SetString('Testing!');
      str := str + '!!!';
      ShowMessage(str);
    

    Just beware that assigning a string to a TRestrictedString will also assign the 'bounds' of the string, that is, the TRestrictedString will have bounds set to 0 and MaxInt. Thus, no matter how a s: TRestrictedString is restricted, an assignment s := 'some string' will always work.

    Update: Chris Rolliston used this answer as inspiration for a very interesting article.