My Top 3+1 features in the new RAD Studio 12 Athens

RAD Studio 12 Athens has been release yesterday, nov 7th 2023. This new release builds on the feature set of 11.x, enhancing existing features throughout the product. In this release there new features and a good number of bug fixes.

Starting from the splash screen which is clean and polished as usual in the lastest versions and with a brilliant red very “familiar” to the Delphi devs. Nice!

Splash Screen

🔔 Here’s how the splash screen looks like (apart the very good TestInsight from Stefan Glienke and the DMVCFramework design-time package installed in beta version. Remember, last stable version of DMVCFramework at the time of writing is available here. The beta version, which is dmvcframework-3.4.1-sodium (the so called “repo version”) is about to be released and already supports Delphi 12 Athens.

Just before starting with my usual cherry-picking, note that the official full “What’s New” is available at Embarcadero web site

Top 3+1 features in strictly random order!

1️⃣ Long and Multiline String Literals

As you probably know, I’m a programming language fan boy. A programming language must be expressive and simple to use; we usually face with complex problems, so the tool we use to solve them should not add complexity; ideally should be “trasparent” for our brain. This it’s not possibile (so far) but we all love KISS principle, so “easier = better” (this is one of the Python reasons to success, indeed).

This is the main reason because I’m quite happy about the removal of the limit of 255 chars in length for literal strings and, even more happy, about the multiline string support.

About the limit of 255 chars, well, you are no more limited by this length; just feel free to write string literal up to 4K chars (which is the limit of the editor for a single line code, not the compiler limit).

About the multi line strings, things are a bit more interesting.

Here’s some example about the new syntax.

procedure TForm13.FormCreate(Sender: TObject);
begin

  var lMyLongString := '''
    This is multiline string... finally
    I can write long text in my code. Hey! I don''t want
    to write the new "Divina Commedia" in my code, but
    something can be really useful to be able to write longer
    text in code.
    ''';

  { ********************************************************************** }
  { Remember, newlines after the initial ''' and before ''' are discarded. }
  { ********************************************************************** }

  { ******************** }
  { ** Other Examples ** }
  { ******************** }

  { Still Valid}
  lMyLongString :=
  '''
  This is multiline string... finally
  I can write long text in my code. Hey! I don''t want
  to write the new "Divina Commedia" in my code, but
  something can be really useful to be able to write longer
  text in code.
  ''';

  { Still Valid, but there will be 2 space before each line }
  lMyLongString := '''
    This is multiline string... finally
    I can write long text in my code. Hey! I don''t want
    to write the new "Divina Commedia" in my code, but
    something can be really useful to be able to write longer
    text in code.
  ''';

  { Still Valid, there will not be spaces before each line }
  lMyLongString := '''
  This is multiline string... finally
  I can write long text in my code. Hey! I don''t want
  to write the new "Divina Commedia" in my code, but
  something can be really useful to be able to write longer
  text in code.
  ''';

  { Error E2657 Inconsistent indent characters}
  lMyLongString := '''
This is multiline string... finally
I can write long text in my code. Hey! I don''t want
to write the new "Divina Commedia" in my code, but
something can be really useful to be able to write longer
text in code.
  ''';

  { Valid Again, check the column where the closing ''' are }
  lMyLongString := '''
This is multiline string... finally
I can write long text in my code. Hey! I don''t want
to write the new "Divina Commedia" in my code, but
something can be really useful to be able to write longer
text in code.
''';

end;

It’s also possibile to define which newline character/s must be used by the multi line strings. Directive TEXTBLOCK allows to specifiy NATIVE (default), CR, LF or CRLF.

{$TEXTBLOCK NATIVE/CR/LF/CRLF [<ident>]}

Using TEXTBLOCK directive you can specify how the line breaks in the multi line strings are treated. The additional <ident> parameter is just a “meta” information. It is ignored by the compiler but may be used by external tools to provide editing support. It may be JSON, HTML, XML, SQL, CSV etc to indicate the content and possibly drive a syntax highlighting tool. However it is just a marker, completely ignored by the compiler.

A complete example of TEXTBLOCK is the following.

{$TEXTBLOCK NATIVE JSON}
var lJSON := '''
  {
    "name": "Daniele",
    "surname": "Teti",
    "favoriteFoods": [
      "Pizza", 
      "Spaghetti Carbonara", 
      "T-Bone Steak",
      "Tiramisu"
    ]
  }
  ''';

However, from the compiler POV, it is equivalent to the following.

{$TEXTBLOCK NATIVE BLABLABLA}
var lJSON := '''
  {
    "name": "Daniele",
    "surname": "Teti",
    "favoriteFoods": [
      "Pizza", 
      "Spaghetti Carbonara", 
      "T-Bone Steak",
      "Tiramisu"
    ]
  }
  ''';

The param <ident>, BLABLABLA in the previous example, is just a note for some other tools, it doesn’t have any effect on compiled code.

2️⃣ Skia Integration

Skia4Delphi is a cross-platform 2D graphics library for Delphi and C++Builder based on Google’s Skia Graphics Library. It provides 2D API to render images across mobile, server, and desktop models. Skia support is a good news in first place for Firemonkey but also VCL gets a great new value. While you may be aware of Skia4Delphi, the Open Source project, RAD Studio doesn’t just “use” such project, but extends it including additional capabilities not found in the open-source projects, like the Vulkan driver.

What you can do with Skia4Delphi? There are a lot of webinars and samples showing the capabilities of Skia, but even if you are not interested in animations or other fancy stuff, Skia can be used also to easily improve UI of existing VCL application. In my case, I like to use Emoji to show familiar images to users. In this case Skia is very useful because, unlike plain TLabel, Skia TSkLabel supports colored Emoji (apart very deep customization, even letter by letter).

VCL application useing emoji and TSkLabel

3️⃣ More Enumerators in the RTL + general RTL improvements

Yes, finally RTL is using enumerators extensively! I’m enumerators fan. Since the first edition on my book “Delphi Cookbook” I show how it is simple to implement very expressive code pattern using enumerators. Also in all my Delphi trainings I show a number of examples to show to the attendants how much the enumerators can simplify and improve robustness of your code. Now, Delphi 12 has a lot of enumerators spread on all the RTL… I love it!

Just some examples:

TComponentEnumerator introduced in TComponent class allows to easily iterate on all the component contained in a parent.

  for var lComponent in Self do  // here "Self" is the form
  begin
    Memo1.Lines.Add(Format('%s (%s)', [lComponent.Name, lComponent.ClassName]));
  end;

TControlEnumerator introduced in TControl class allows to easily iterate on all the control contained in a parent. Also allows to filter controls by visibility, allows to recursively interate controls contained in a controls and so on. Very useful and simple to use!

  for var lControl in Self.GetControls([ceftAll]) do // here "Self" is the form
  begin
    Memo1.Lines.Add(Format('%s (%s)', [lControl.Name, lControl.ClassName]));
  end;

Enumerators on TTextReader class allows to enumerate text file contents line by line without loading all the file in memory. TTextReader is the parent class of TStringReader and TStreamReader, so we can use it easily as shown in the next snippets.

  Memo1.Clear;
  var lFile := TFile.OpenText('..\MainFormU.pas');
  try
    for var lLine in lFile do // Iteration line by line
    begin
      Memo1.Lines.Add(lLine);
    end;
  finally
    lFile.Free;
  end;

Even more useful: TFile and TDirectory fulli leverage enumerators to do what they do with files and directories.

  for var lLine in TFile.GetLinesEnumerator('..\MainFormU.pas') do  // << Look Ma!
  begin
    Memo1.Lines.Add(lLine);
  end;


  Memo1.Lines.Add('-------');
  for var lLine in TDirectory.GetFilesEnumerator('..\') do 
  begin
    Memo1.Lines.Add(lLine);
  end;

  Memo1.Lines.Add('-------');
  for var lLine in TDirectory.GetDirectoriesEnumerator('..\') do 
  begin
    Memo1.Lines.Add(lLine);
  end;

  Memo1.Lines.Add('-------');
  for var lLine in TDirectory.GetFileSystemEntriesEnumerator('..\') do
  begin
    Memo1.Lines.Add(lLine);
  end;

In 2014, in the first “Delphi Cookbook” I wrote also a TDataSetEnumerator to loop throug the dataset records, but if you did understand the concept, it is very simple to create it.

Good Job EMBT Guys! Enumerators are really powerful and expressive, use them as much as possibile!

Some other interesting changes in the RTL

While marginally related to enumerators, another things that I want to note is the new addition to System.Generics.Collections.pas:

  • System.Generics.Collections.THashSet<T>
  • System.Generics.Collections.TObjectHashSet<T: class>

THashSet<T> (and its object related version) is an implementation of a set. Both classes provide methods to do usual operation on sets and accept a generic TEnumerable<T>:

procedure ExceptWith(const AOther: TEnumerable<T>);
procedure IntersectWith(const AOther: TEnumerable<T>);
procedure UnionWith(const AOther: TEnumerable<T>);
function Overlaps(const AOther: TEnumerable<T>): Boolean;
function SetEquals(const AOther: TEnumerable<T>): Boolean;
function IsSubsetOf(const AOther: TEnumerable<T>): Boolean;
function IsSupersetOf(const AOther: TEnumerable<T>): Boolean;

Here’s a very simple utilization example:

procedure TMainForm.btnHashSetClick(Sender: TObject);
begin
  Memo1.Clear;
  var lSet := THashSet<String>.Create(['hello','world','hello']);
  try
    for var lS in lSet do // Enumerator is used!
    begin
      Memo1.Lines.Add(lS); 
    end; // Memo contains 'hello' 'world' (it's a set, so no duplicates allowed)
    var lSet2 := THashSet<String>.Create(['world']);
    try
      lSet.ExceptWith(lSet2);
      for var lS in lSet do
      begin
        Memo1.Lines.Add(lS); 
      end; // Memo contains just 'hello'
    finally
      lSet2.Free;
    end;
  finally
    lSet.Free;
  end;
end;

Pascal has sets arithmetic since its inception so having in the RTL a THashSet<T> class to use sets with any type is a very good addition.

Another interesting addition is a THTTPClient based on Curl. It is particularly useful on Linux but can be used on Windows too. To use it on Windows you have to add unit System.Net.HttpClient.Curl apart the unit normally used to work with THTTPClient. However, a IMHO “naive” implementation of TryISO8601ToDate raises an exception when tries to understand the HTTPS certificate dates, so it is a bit weird to use while debugging because you get exceptions in the RTL code at any request… quite annoying. Here’s an example.


uses
  System.IOUtils, 
  System.Net.HttpClient, 
  System.Net.URLClient, 
  System.Net.HttpClient.Curl {required to use curl};

procedure TForm13.btnCurlClick(Sender: TObject);
begin
  Memo1.Clear;
  var lClient := THTTPClient.Create;
  try
    lClient.ValidateServerCertificateCallback := ValidateCert;
    var lResp := lClient.Get('https://docwiki.embarcadero.com'); //raises exceptions by design, even if it works... :-(
    Memo1.Lines.Add('Homepage size in bytes is ' + lResp.ContentLength.ToString);
  finally
    lClient.Free;
  end;
end;

📣 In Windows, to use THTTPClient based on Curl, you need libcurl.dll available from here.

I tried hard to fit just 3 things in my “Top 3” but there is always a 4th thing… so here it is.

4️⃣ FireDAC improvements: Security and TFDQBE (Query by Example)

👉 QBE

In Delphi 12 Athens, FireDAC supports QBE (query by example) thanks to a new component called TFDQBE, which represents a query-by-example component.

In simple terms, your form which uses DB-Aware controls can be automatically transformed into a filter forms where in each fields you can write criteria. Can be really useful in some cases.

👉 Security

In Delphi 12 Athens FireDAC gots some security related features. Quiting from Embarcadero documentation: “These features and good overall security practices can contribute to developing more secure database applications”

TFDConnection gots the following properties:

  • AllowedCommandKinds - Limits the execution of SQL commands, preventing a hacker from modifying a query and making unwanted changes to the database.
  • AllowMultiCommands - Enables or disables the execution of multiple SQL commands. It is enabled by default and works only with databases offering the matching feature: SQLite, PostgreSQL, and MySQL.
  • AllowSQLChange - When enabled, this setting avoids changes to the text of a FireDAC SQL query at runtime. A method like DoSQLChanging would invoke CheckCanChangeSQL, now checking for the specific setting (among other conditions).
  • ExactUpdatedRecsNum - It is an Integer with a default value of -1. When it is set to > 0, in conjunction with FDQuery.FetchOptions.Mode := fmExactRecsMax; and FDQuery.FetchOptions.RecsMax := 1;.

How much is difficult to migrate to Delphi 12 Athens from 11.3?

It depends, obviously, but just before writing this post DMVCFramework, the most popular Delphi framework on github, has been updated to be compatible with the new Delphi 12 Athens in more or less 2 hours, where most of this time has been spent to remove HINTS and WARNINGS that were not arise in 11.3 Alexandria. Also some other projects usually compiled with no WARNINGS nor HINTS in 11.3 Alexandria, in 12 Athens shown WARNINGS and HINTS. Good to know that now the compiler is smarter! However the time spent is very low. As I usually says: “This is one of the reason why I love Delphi - it value your time!”.

Another nice thing is related to the compiler ability to generate GraphViz file with the unit dependencies. Very nice addition which I still have to investigate deeply.

Conclusions

RAD Studio 12 Athens is what you expect from a major version. Yes, there are things that should be fixed (someone said formatter? someone else synch highlight?) consists of many new features (some bigger than others) and quality improvements. Obviously, there are bugs still open (as with any non trivial piece of software) however this release is another step in the right direction. Keep going Guys!

This is my personal-and-subjective Top 3+1 features in RAD Studio 12 Athens, what’s yours?

Comments

comments powered by Disqus