Skip to content

Some differences betwen FPC ObjFpc mode and Delphi (and FPC Delphi mode)

Michalis Kamburelis edited this page May 22, 2024 · 10 revisions

Some differences betwen FPC ObjFpc mode and Delphi (and FPC Delphi mode)

Introduction

FPC compiler offers a few "syntax modes". They can chosen by

  • directives in Pascal code (like {$mode objfpc}, {$mode delphi}),

  • or by command-line options to FPC compiler (-Mobjfpc, -Mdelphi),

  • or by Lazarus project settings (in .lpi file; applicable if you compile from Lazarus IDE),

  • or by -Mxxx option in <compiler_options> in CastleEngineManifest.xml, if you compile using CGE editor or CGE build tool (see https://castle-engine.io/project_manifest#_compiler_options_and_paths ).

There are a few syntax modes in FPC, but within this page we will focus on two which are most popular: ObjFpc and Delphi syntax. They offer 100% the same capabilities, and are very compatible…​ but not absolutely compatible, there are small differences, which we outline below.

Delphi doesn’t have this concept (though it has a few options that affect syntax as well, we mention some of them below).

As the names suggest, FPC "Delphi mode" is very compatible with the actual Delphi.

FPC "ObjFpc mode" is a bit more encouraged by FPC developers (though it may depend on who you ask :) ). More practically, new units created by Lazarus have {$mode objfpc}{$H+} and new Lazarus projects start with "mode ObjFpc" setting. So Lazarus developers encourage and use by default ObjFpc mode.

In Castle Game Engine, we follow Lazarus, so we use ObjFpc mode by default for your CGE projects, if they are being compiled with FPC. Though you can change it, for your projects: just put <option>-Mdelphi</option> in compiler options in CastleEngineManifest.xml . We show this example on https://castle-engine.io/project_manifest#_compiler_options_and_paths .

What are the differences? Along with some personal (Michalis) thoughts, sometimes biased, about "what is better"

Procedural variables

The procedural variables (pointers to functions) have slightly different syntax.

In FPC ObjFpc mode, changing "routine" to "address of routine" requires an explicit @ operator.

So e.g. you have to write

procedure TMyView.ButtonClick(Sender: TObject);
begin
end;

procedure TMyView.Start;
begin
  inherited;
  OnClick := @ButtonClick; // this is only OK in FPC ObjFpc
end;

On the other hand, Delphi decided that ButtonClick, without @, is already a valid "address of `ButtonClick`". In effect, this line will fail to compile:

OnClick := @ButtonClick; // this is only OK in FPC ObjFpc

(since in Delphi it means you try to take an address of the address of ButtonClick, so it’s an address of temp variable, not sensible).

Only this compiles:

OnClick := ButtonClick; // this is only OK in Delphi or FPC Delphi mode

Why this difference?

  • FPC ObjFpc approach is helpful to make some expressions unambiguous. E.g. what does it mean if OnMyEvent = MyFunction then …​, when events are defined as function: Integer → does this compare assigned events, or calls them? It gets even more complicated when the function returns the address of another function (like function: TNotifyEvent). In FPC ObjFpc mode, such expressions are simple to understand, because "without @, you cannot take address of a procedure, so the above calls the `MyFunction`".

  • From what I know, the advantage of "Delphi syntax" is just brevity. You can forget about @, and most expressions are obvious to the compiler, i.e. it can tell when you want to call a function, and when to take a function address.

What to do in your code?

  1. In CGE code and examples, we decided to just "live with it" and write {$ifdef FPC}@{$endif} everywhere needed. We like the extra clarity that comes from FPC ObjFpc requiring @. Also, many FPC / Lazarus users are most used to ObjFpc mode, since it’s the default mode set up in new Lazarus projects, so we abide to it. So we write

    OnClick := {$ifdef FPC}@{$endif} ButtonClick;

    To be perfectly valid, we should actually write

    OnClick := {$ifdef FPC_OBJFPC}@{$endif} ButtonClick;

    But for simplicity, we decided to assume "when CGE is compiled with FPC, we know it is compiled in FPC ObjFpc mode, since we set it ourselves in both command-line and by castleconf.inc".

  2. You can also choose a different approach: use "Delphi mode" with FPC. To do this, put -Mdelphi in compiler options in CastleEngineManifest.xml (Lazarus project settings also have a setting to change mode). See " 6.11. Compiler options and paths" in https://castle-engine.io/project_manifest#_compiler_options_and_paths , we explicitly show there and discuss using -Mdelphi.

    Then you have more consistent code between FPC and Delphi.

Below is more discussion and arguments why FPC ObjFpc mode is a good idea:

Consider writing this, in Delphi or FPC Delphi mode:

if X = Y then ...

What does it do, if X and Y are procedural variables? In Delphi mode ({$mode delphi} in FPC or in actual Delphi), this is a tricky question. It stems from the fact that in Pascal, you call parameterless function without parenthesis, i.e. without trailing (). If X and Y point to parameterless function returning e.g. integers, if X = Y then will actually call them and compare results. Only in other cases, function pointers will be actually compared.

TODO: I had a testcase to prove above, restore it here. compare_function_ptrs.lpr.

To be sure you compare function pointers, you add address operator, like

if @X = @Y then ...

Which is ugly, because in this case the @ doesn’t get an address of X (although "@X" is defined exactly as "get address of X variable" in cases of other types in Pascal). It merely indicates we really want the function pointer.

It’s even more fun if you want to really get "an address of the function pointer", as then you have to write

@@X

which is nonsense in normal circumstances (for other types, e.g. when X is integer). @@ gets an address of a temporary address, which is never useful and should actually generate compiler error. But in this case, it’s needed. Also when you assign to X, you do

@X := MyFunction;

which is also nonsense in normal circumstances --- @X is usually a temporary result, not l-value, how can you assign something to it?

In ObjFpc mode, it’s all more consistent. X and @ behave the same as for other types. The only new rule is "when you call a procedural variable holding a parameterless function, you do it like in C --- you add `()`". This means that calling X doesn’t look "Pascalish", as you have to add parenthesis:

X()

…​But everything else is more natural. In FPC ObjFpc mode, this:

if X = Y then

compares function pointers, when X and Y are e.g. of type function: Pointer. It intuitively compares X and Y contents --- regardless if X, Y are integers, function pointers or parameterless function pointers. Also

@X

is now always the address of X (regardless if X is of type Integer, or a function pointer). Assignment of one function variable to other is normal:

X := Y

and assignment of actual function to X explicitly uses @ operator on the right side, signifying that we take an address of function:

X := @MyFunction;

Method parameter name cannot conflict with class field, in ObjFpc mode

This one breaks the usual scoping rules for Pascal, but it does so with a good purpose, protecting you from common errors. Imagine:

TMyClass = class
  X: Integer;
  procedure Foo(X: Integer);
end;

This is allowed in Delphi mode. Inside Foo implementation, resolution follows normal Pascal rules: the same name, X, is allowed if in different scopes, and more local scope takes precedence.

procedure TMyClass.Foo(X: Integer);
begin
  X := 123; // changes the X parameter value, X field is untouched
end;

This is however dangerous: if you change Foo declaration to Foo(Y: Integer), the implementation will still be compiled…​ but it will do something totally different, probably very errorneouus, since you now change the object field. To prevent this, ObjFpc disallows this: parameter names must have different names than fields/methods/properties of the class. TMyClass above will simply not compile in ObjFPC mode, you have to change parameter name to something like AX, and you will probably remember to use AX inside Foo implementation. If you ever change Foo declaration, you will most likely get a compilation error until you correct all occurrences of AX.

Treating pointers like arrays, without using ^ (dereferencing)

In FPC ObjFpc mode, if you have

X: ^Integer;

and you use

X[2]

then ObjFPC treats this similar to C: it’s like X was a pointer to an array of Integer, and you want the 2nd one. So

Y: array [0..999] of Integer;
X: ^Integer;

...
X := @(Y[10]);
Assert(X^ = Y[10]);
Assert(X[0] = Y[10]);
Assert(X[2] = Y[12]);

TODO: restore my demo: array_ptrs.lpr

In Delphi, this is not allowed, you cannot treat a pointer to TTT like an array of TTT.

OTOH, in Delphi there’s a different shortcut. Namely, you can simply omit the ^ if the dereferenced type is already an array. That is, if X is a pointer and you write

X[2]

then Delphi treats it like

X^[2]

Obviously this makes sense only if X is a pointer to the array, like

type
  TIntegers = array [0..999] of Integer;
var
  X: ^TIntegers;

An often used trick in Delphi is declaring an "infinite" (in fact, as long as possible to compile, hence MaxInt trick) array, and use it as a comfortable pointer type:

type
  TMyRecord = record A, B, C: Integer; ... whatever else ... end;
  TMyRecords = array [0..MaxInt div SizeOf(TMyRecord) - 1] of TMyRecord;
  PMyRecords = ^TMyRecords;
var
  MyRecords: PMyRecords;

  ... // and for example you can do:
  MyRecords := GetMem(SizeOf(TMyRecord) * 10);
  // in Delphi (real Delphi or delphi mode in FPC) you can do:
  MyRecords[1].A := 123;
  // in both Delphi mode and objfpc mode you can also do it without omitting ^:
  MyRecords^[1].A := 123;

In FPC ObjFpc mode, you would rather do it like this:

type
  TMyRecord = record A, B, C: Integer; ... whatever else ... end;
  PMyRecord = ^TMyRecord;
var
  MyRecord: PMyRecord;

  ... // and for example you can do:
  MyRecord := GetMem(SizeOf(TMyRecord) * 10);
  // not writing ^ means that MyRecord is automatically treated as a pointer
  // to an array of whatever it was declared.
  MyRecord[1].A := 123;

There is a small catch in all this, place that both Delphi (real Delphi or {$mode delphi} in FPC) and ObjFPC mode compile, but interpret differently. Recall previous example of infinite array:

type
  TMyRecord = record A, B, C: Integer; ... whatever else ... end;
  TMyRecords = array [0..MaxInt div SizeOf(TMyRecord) - 1] of TMyRecord;
  PMyRecords = ^TMyRecords;
var
  MyRecords: PMyRecords;

  ... // and for example you can do:
  MyRecords := GetMem(SizeOf(TMyRecord) * 10);
  // in Delphi (real Delphi or {$mode delphi} in FPC) you can do:
  MyRecords[1].A := 123;

The catch is: actually you can use MyRecords[1].A also in ObjFPC mode. It will compile, but it will mean something that you probably don’t want. MyRecords is then treated as an array of whatever it actually points to…​ so MyRecords is a pointer to an array of TMyRecords arrays! MyRecords[1] refers to the 2nd array. Most definitely not what you wanted, and outside the allocated memory.

Morale: do not omit the ^ operator. Or pay attention to your mode: both ObjFPC and Delphi have a different idea when you can omit it and how it’s interpreted.

Note: Later Delphi actually introduces something very similar to FPC, "{$pointermath on}". This actually makes Delphi behave quite close to what FPC does in this regard. Our "castleconf.inc" (used in all CGE units) defines it now. See https://docwiki.embarcadero.com/RADStudio/Sydney/en/Pointer_Math_(Delphi) .

Default String is ShortString in ObjFpc mode, you probably want to change it to AnsiString

Finally, a small difference that is actually a small annoyance of ObjFPC mode. In ObjFPC, by default string = ShortString. That’s why you should almost always do

{$mode objfpc}{$H+}

at the beginning of your sources, to use modern Object Pascal string. Almost never you want to deal with ShortString and it’s shortcomings in modern code.

OTOH, {$mode delphi} automatically uses AnsiString by default. So simply using

{$mode delphi}

is enough. You can of course use {$mode delphi}{$H+}, but then the {$H+} is just superfluous.

ObjFPC behaves like this for backward compatibility. There were discussions about changing this default (and changing default FPC mode), but (so far) the backward compatibility argument wins.

Note that actual Delphi, recent versions does something different nowadays: default String is UnicodeString, not ShortString, not AnsiString. In CGE, we deal with it by adjusting to both compilers defaults: [Most code should use just String, and be prepared that it is 8-bit on FPC and 16-bit on Delphi. Only if writing to stream, explicitly use 8-bit AnsiString (in usual case, when you write UTF-8).](https://castle-engine.io/coding_conventions#strings_unicode)