Search code examples
arraysdelphiparsingcompatibility

How to parse empty string to array of byte?


I have to use an old unit that uses ansistring, ansichar et al on Android, previously built on Delphi 2007, so I'm working on making it compatible with XE7.

I've come across several lines that would have put an empty string into an AnsiString, like so: FAnsiString := '', but now that AnsiString is an array of bytes(FAnsiString : array of Byte) I get the message E2010 incompatible types.

I've tried to parse it by creating a type for the array (type TArrayOfByte = array of Byte) and trying to parse it like so: FAnsiString := TArrayOfByte (''). But then I get E2089 Invalid TypeCast.

How can I make it work correctly?

An simplification of how it should work is below. Basically I want a function that returns an array of bytes. Depending on the arguments, the result of that function should be an empty string.

{$ifdef android}
  type AnsiString = array of Byte;
{$endif}

function FooBar(arguments) : AnsiString
begin
  if false then
  Result := '';
end;

Solution

  • Delphi strings and dynamic arrays both contain a nil pointer when they are empty. So this:

    var
      FAnsiString: AnsiString;
    
    FAnsiString := '';
    

    Is effectively doing this behind the scenes:

    // FAnsiString := '';
    if Pointer(FAnsiString) <> nil then
    begin
      // decrement reference count, if 0 then free memory ...
      Pointer(FAnsiString) := nil;
    end;
    

    Dynamic arrays do the exact same thing. This:

    var
      FAnsiString: array of Byte;
    
    FAnsiString := nil;
    // Alternatively:
    // SetLength(FAnsiString, 0);
    

    Is effectively doing the same thing as the AnsiString cleanup behind the scenes:

    // FAnsiString := nil;
    if Pointer(FAnsiString) <> nil then
    begin
      // decrement reference count, if 0 then free memory ...
      Pointer(FAnsiString) := nil;
    end;
    

    Both AnsiString and dynamic arrays are compiler-managed ref-counted types. They are both auto-initialized to nil.

    Likewise, AnsiString comparisons for empty strings:

    var
      FAnsiString: AnsiString;
    
    if FAnsiString = '' then
    ...
    if FAnsiString <> '' then
    

    Are effectively just checking for a nil pointer:

    if Pointer(FAnsiString) = nil then
    ...
    if Pointer(FAnsiString) <> nil then
    

    Same with dynamic arrays, only you can do it without a typecast:

    var
      FAnsiString: array of byte;
    
    if FAnsiString = nil then
    ...
    if FAnsiString <> nil then
    

    The alternative is to use Length() and SetLength(), which works for both strings and dynamic arrays:

    var
      FAnsiString: AnsiString;
    
    if Length(FAnsiString) = 0 then
      ...
    
    if Length(FAnsiString) <> 0 then
      ...
    
    SetLength(FAnsiString, 0);
    

    var
      FAnsiString: array of byte;
    
    if Length(FAnsiString) = 0 then
      ...
    
    if Length(FAnsiString) <> 0 then
      ...
    
    SetLength(FAnsiString, 0);
    

    And, of course, there is always Andreas Hausladen's patch to enable AnsiString in the mobile compilers:

    The Return of the Byte-Strings

    System.ByteStrings support for XE5 Update 2 (and XE6 and XE7)