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,
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?
17 replies on “Hidden features in the Delphi Object Pascal language”
OK, this needs 1 more reopen vote so it doesn’t disappear. Can someone cast it?
I cast one, so it is safe for now, but a Moderator could still delete it.
Great article Jim, I hope it’s not deleted from SO, but you’re doing it right by rescuing the content.
One of the things worth mentioning as “hidden feature” is the untyped parameters, I’m sure there’s lot of people are not aware of:
procedure MyProc(var X);
On other affairs, in WordPress, inside blocks you don’t use the & syntax, since it is just “translated” into a pre html block.
Other thing is I was trying to post as a wordpress.com user, but I got “invalid security token” error message twice, and I had to re-write my comment twice
I just made a post a couple days ago about a hidden (or at least not particularly obvious or well-known) feature of the language: Any operator overload can return a value of an arbitrary type, even something like = or >, which you would expect to only return a boolean. There are a bunch of interesting things that can be done with this knowledge.
Mason, I will not call “interesting”, weird things like this:
var
I, A, B: Integer;
begin
A := 10;
B := 20;
I := A > B; //I now is 30, because the > operator really sum the opperands.
if A + B then
showMessage(‘and the + operator compares for inequality’);
end;
Operator overloading is clearly something that can be abused in a bad way, but there are good uses for it too.
@jachguate: I wouldn’t call that “interesting” either. But I also wouldn’t call it “possible”. Can you actually create that functionality? AFAIK there’s no mechanism that will allow you to redefine baked-in operators on Integer or other fundamental types.
What happened to the “Hidden Features of the Delphi IDE”, seems to have been deleted? Sometimes I think wtackoverflow is becoming as bad a Wikipedia.
It was deleted. I plan to copy the contents from there here too (I have access to see deleted posts).
[…] Otro poquito de Delphi: Hidden features in the Object Pascal Languaje […]
Asked a question about it on Meta: http://meta.stackoverflow.com/questions/222371/why-are-question-that-no-longer-fit-the-faq-being-deleted-instead-of-historical
When I was working with a Pascal variant language the semi-colon was described as being used to indicate the beginning of a new statement. I was already used to putting in the ‘normal’ place but thinking about the semi-colon that way started to make sense on why you can omit it.
Absolute var declaration is well explained in the book “Turbo Pascal 7: The complete reference” by Stephen O’Brien, published in 1993.
This book is excellent.
I’ve seen your example of “property Foo: TFoo read FFoo implements IFoo” in Marco Cantu’s Mastering Delphi 7 book. It is called Delegation there 🙂
TMyJumper = class (TInterfacedObject, IJumper)
private
fJumpImpl: TJumperImpl;
public
constructor Create;
property Jumper: TJumperImpl
read fJumpImpl implements IJumper;
destructor Destroy; override;
end;
See http://code.marcocantu.com/trac/marcocantu_marco-delphi-books/browser/masteringdelphi7/02/IntfDemo/WalkIntf.pas#
What about class operator overloading for classes (for non-NEXTGEN applications)?
You can descend a class, which members implements interfaces not directly from TInterfacedObject, moving this dependency into implementer class:
ITestable = interface
[‘{9D0AFE29-A6DD-420D-8AAE-ED19996B207D}’]
procedure Test;
end;
TTestableImpl = class(TInterfacedObject)
protected
procedure Test;
end;
TMyTestClass = class(TObject, ITestable)
private
FTestableImpl: TTestableImpl;
public
property TestableImpl: TTestableImpl read FTestableImpl implements ITestable;
end;
If you want to protect that SO post against deletion from passerby reviewers, try to ask for lock (maybe by flagging the post with the proper comment). There are many protected “hidden feature” questions, so why not also our Delphi one… Btw., this post was deleted by the same moderator at the same time when I was fighting to save “hidden features of Delphi IDE” (http://meta.stackoverflow.com/q/222285/179541). Quite a strange step though. Today, when I noticed that, I’ve asked for undeletion with moderator attention flag and Brad Larson undeleted it for us again. But without a lock, so it can be deleted again. Anyway, thanks for saving such a useful content!