FMX Strings – Best Practices

bv

In C the first character of an array begins at index zero (0), legacy strings were essentially an array of characters terminated by a null character. To determine the length of the string you would use the strlen function. The practice of indexing from zero is common to all languages based on the C syntax including C++, Java, C#, Dart, etc.

In Pascal, however, there is a shortstring type which can store up to 255 characters, the first character (at index zero) is used to store the length of the string, the actual string contents begin at index one (1). As Pascal evolved into Object Pascal, this legacy was maintained as new string types emerged, including AnsiString and the new unicode String type.

In recent years, Delphi has gained the ability to deploy to mobile devices. A decision was made to use zero based indexes for strings when targeting mobile, but for compatability purposes to stay with one based indexes when targeting the desktop. This recently proved problematic for me because FMX allows you to target all platforms (mobile and desktop) from a single code base.

Having programmed mostly in C based languages I just assumed (whilst in late night zombie auto pilot mode) zero based strings, which resulted in a range error when running my application on Windows. The culprit, my parsing function:

I := 0;
while (FState <> psComplete) and (I < Length)…
Ch := Xml[I];

This confusion led to proposals to turn off zero based strings completly:

ZEROBASEDSTRINGS, Just Don’t

After reaching out to the community and doing a bit of research, the following are my recommendations for using strings in FMX.

You can turn off zero based strings as mentioned here:

Zero-based strings (Delphi)

But as per the recommendation on that page, if you use NEXTGEN compilers, don’t!

However, the mobile compilers and the RTL for the Delphi mobile use 0-based strings by default, so you must use 0-based strings when you are using the RTL in Delphi applications for mobile platforms.

I started digging around in the Delphi RTL and noticed that in SysUtils the TStringHelper functions explicitly turn on zerobased strings.

{$ZEROBASEDSTRINGS ON}
function TStringHelper.Substring(StartIndex: Integer): string;
begin
Result := System.Copy(Self, StartIndex + 1, Self.Length);
end;
...
{$ZEROBASEDSTRINGS OFF}

After noticing that, I then found official confirmation here:

System.SysUtils.TStringHelper

If you use the newer TStringHelper functions you can just assume zero based index everywhere.

Next, as pointed out by Erik van Bilsen, using the newer functions makes looping safe. As per my previous example I should use the Chars[] indexer rather than the original [] indexer:

I := 0;
while (FState <> psComplete) and (I < Length)…
Ch := Xml.Chars[I];

This is now safe on all platforms.

However, Chars[] is readonly. To write characters into the string I need the original [] indexer. In that case I need to be careful about range errors. This is where the Low/High functions are useful (notice I need the <= operator in this case):

I := Low(Xml);
while (FState <> psComplete) and (I <= High(Length))…
Ch := Xml[I];

This is now safe on all platforms.

Following these three practices you should have no problems with strings on any platform. For more information see here:

Migrating Delphi Code to Mobile from Desktop

Thanks to the Delphi Developer FB group for all their feedback.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s