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 &begin, &if, &end: integer; begin &begin := 10; &if := 100; &end := 1000; ShowMessage(IntToStr(&begin + &if + &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?