Quantcast
Channel: Planet Object Pascal
Viewing all articles
Browse latest Browse all 1725

The Podcast at Delphi.org: Hidden features in the Delphi Object Pascal language

$
0
0

A list of hidden features in Delphi Object Pascal that are great, obscure, best avoided or remarkable.

This was copied from Stack Overflow’s question of the same name which is closed and flagged for deletion. Licensed under cc by-sa 3.0 with attribution required. I’ve made a few changes, updates and some copy editing. Original question by Johan and others on May 19 2011 at 18:34. Post inspired by Jeroen W. Pluimers’ post.


David M added:

My favourite (although I don’t know if I have ever written code that uses this) is how you can delegate implementation of an interface ‘supported’ by a class to one of the class’s properties. In a pure OO design sense, I don’t know if this is awful or beautiful – you’re basically lying about what your class’s methods are – but in a code cleanliness and separation sense it’s awesome.

For example (untested, written from memory):

type
IFoo = interface
['{3F996D68-1FD0-4490-AE60-8F735A9DFFE8}']
    function TheQueensHead: Byte; // Geddit?  They're bars 
    function TheBoar: Byte;
    function TeeFortyTwo: Byte;
  end;</p>
<p>TOnTheTown = class(TInterfacedObject, IFoo)
  private
    FFoo: TFoo; // Some class that actually concretely implements IFoo
  public
    constructor Create();
    property Foo: TFoo read FFoo implements IFoo; // This keyword is the awesome one
  end;

Now, if I remember my Delphi correctly (I mostly use C++ these days) you can call:

Party := TOnTheTown.Create();
Party.TheBoar;

where TheBoar is shown in the class declaration as implemented by that class, but code-wise you implement it in the class that the property Foo is of – the call is delegated to the property. Neat, isn’t it?

Even better, I just learned this very day while googling to check my memory was correct that starting with XE, you can do this in C++ too.

Either declare Party as IFoo or, perhaps, cast it (Party as IFoo). The point is that you have an object that you state implements various methods (because it claims to implement a particular interface) and yet it doesn’t – calls are redirected / delegated somewhere else.


Andreas Rejbrand added:

You can use & to prefix identifiers that would normally be invalid because they are reserved words.

procedure TForm1.Button1Click(Sender: TObject);
var
  &amp;begin,
  &amp;if,
  &amp;end: integer;
begin
  &amp;begin := 10;
  &amp;if := 100;
  &amp;end := 1000;
  ShowMessage(IntToStr(&amp;begin + &amp;if + &amp;end));
end;

You can use the ^A syntax to denote a Ctrl+A‘character’:

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
begin
  if Key = ^C then
    ShowMessage('The user wants to copy something.')
  else if Key = ^V then
    ShowMessage('The user wants to paste.')
end;

Something I have always found a bit funny is the compiler magic associated with the TGUID type. This is declared as

TGUID = packed record
  D1: LongWord;
  D2: Word;
  D3: Word;
  D4: array[0..7] of Byte;
end;

and so, a priori, one would not expect something naïve like

const
  g: TGUID = '{E4C26C63-CDD1-4450-9FE0-6F035E33CF90}';

to work, but it does. Compiler magic, it is!

One would expect

[DCC Error] Unit1.pas(27): E2010 Incompatible types: 'TGUID' and 'string'

Sertac Akyuz added:

Construct a dynamic array using it’s type’s constructor:

type
  TInts = array of Integer;

..

var
  Ints: TInts;
begin
  Ints := TInts.Create(3, 5, 1, 0, -2, 5);
end;

Not sure if this is documented or not but when I learned this from a newsgroup post after years of using Delphi, I was a bit surprised.

PS: Do not Free the array. :)


Cosmin Prund brought up The ABSOLUTE keyword.

This is a nice way to shoot yourself in the foot, because it throws type-safety out the window big time. Delphi’s RTL sources uses this in 5 different units. The first 4 only use it once per unit, then there’s Mxarrays.pas where this is used 22 times. What I’m trying to say is that the absolute keyword is rarely used.

// This is how it's useful:
procedure Test;
var i64: Int64;
    b:array[0..7] of Byte absolute i64; // starts at the same memory space as i64
begin
  i64 := 7;
  WriteLn(b[3]);
end;

// This is how you shoot yourself in the foot:
procedure Test;
var s: string;
    i: NativeInt absolute s;
begin
  s := 'Test';
  Dec(i, 7);
  ShowMessage(s); // will give you garbage or AV
end;

Variant records are weired-looking records where some part of the record is “variable”. Every time I see one used I say That’s an useful trick, but never managed to actually use one in my own code. And I do use weird unsafe things like the absolute keyword and pointer arithmetic.

Example:

type
  TKindOfShape = (ksCircle, ksSquare);

  Shape = record
    case Kind:TKindOfShape of
      ksCircle:
        (
          R: Double;
          CenterX, CenterY: Double;
        );
      ksSquare:
        (
          Top, Left, SideSize: Double;
        );
  end;

Warren Postma brought in some ancient syntax:

Not so much hidden, as ancient are the digraphs supported by the original Wirth pascal, which still work in Delphi:

procedure Dummy;
var
   a:array (. 0 .. 2 .) of Integer;
begin

end;

The character sequence (. and .) replace [ and ] on systems where the [ and ] are not even available, and are called digraphs because they are two characters. C/C++ for example, supports some Trigraphs (three character symbols), too.

I think most Delphi programmers have seen comments that are written with (* and *) which are the common digraph form of { and }.


lkessler added:

Almost everyone knows about the increment and decrement function:

Inc(N) // to increment N by 1.
Dec(N) // to decrement N by 1.

But did you know they have a second parameter, which is the amount of the increment?

Inc(N, I) // to increment N by I.
Dec(N, D) // to decrement N by D.

Now I think these are for people who start using Delphi after using other languages. Personally I prefer:

N := N + 1;
N := N - 1;
N := N + I;
N := N - D;

which generates exactly the same code as Inc and Dec do, with equivalent optimizations.

Don’t forget about Pred and Succ, which when applied to integers returns the value one less and one greater without changing the original value. And you can string them, e.g.

Pred(Pred(Pred(N)));

NGLN points out it is possible to omit the parameter list of routines that are declared in the interface section. For functions it is even possible to omit the result type, which leads to weird looking implementations like:

function GetText(var A: Integer; const B: Char; C: Word = 0): String;

implementation

function GetText;
begin
  Result := 'Where am I going?';
end;

Obviously, this does not apply to overloaded routines. ;)

Sometimes when someone else’s code passes by, this is still surprising. Users would have maintenance reasons.


Rudy Velthuis points out that there is a calling convention called “winapi” which (at the moment, at least) is completely identical to “stdcall”?

Example:

function CryptReleaseContext(hProv: HCRYPTPROV; dwFlags: DWORD): BOOL; winapi;

This is fully equivalent to:

function CryptReleaseContext(hProv: HCRYPTPROV; dwFlags: DWORD): BOOL; stdcall;

lkessler always seems to forget about Method Overloading. Multiple routines can share the same name. The number of parameters and type of each parameter determine which will be called.

e.g.:

Function Multiply (A, B: integer): integer; overload;
Function Multiply (A, B: double): double; overload;

Early Delphi users might not know about this one because it was added in Delphi 4.

See: http://www.delphibasics.co.uk/RTL.asp?Name=Overload

Also there is a nice list of new Delphi language features added since Delphi 7, along with good examples of each at: http://edn.embarcadero.com/article/34324

It includes:

  • Inlining
  • Operator Overloading
  • Class Helpers
  • Strict Private
  • Strict Protected
  • Records with Methods
  • Class abstract
  • Class sealed
  • Class const
  • Class type
  • Class var
  • Class property
  • Nested classes
  • Final methods
  • Sealed methods
  • Static class methods
  • For-in loop

Jørn E. Angeltveit point out that the ; is used to separate statements, not end statements, so you actually don’t need the last ; before end:

procedure DoNothing;
begin
  Inc(i);
  Dec(i)
end;

It is more convenient to add statements and move statements when they all end with the ;, so I definitely wouldn’t recommend to implement this a new practice at the workplace :)


What else should be on the list?


Viewing all articles
Browse latest Browse all 1725

Trending Articles