1

After reading Embarcadero's documentation on procedural types & anonymous methods and David Heffernan's explanation on that matter, I still don't quite understand why the compiler forbids initializing a constant array of reference to function, as C_BAR in the example below.

program MyProgram;

{$APPTYPE CONSOLE}

{$R *.res}

type
  TFoo = function: Integer;
  TBar = reference to function: Integer;

  function FooBar: Integer;
  begin
    Result := 42;
  end;

const
  // This works
  C_FOO: array[0..0] of TFoo = (FooBar);

  // These lines do not compile
  // C_BAR: array[0..0] of TBar = (FooBar); // TBar incompatible with Integer
  // C_BAR: array[0..0] of TBar = (@FooBar); // TBar incompatible with Pointer

var
  Foo: array[0..0] of TFoo;
  Bar: array[0..0] of TBar;
begin
  Foo[0] := FooBar; // Foo[0] = MyProgram.FooBar
  Bar[0] := FooBar; // Bar[0] = MyProgram$1$ActRec($1CC8CF0) as TBar

  Foo[0] := C_FOO[0]; // Foo[0] = MyProgram.FooBar
  Bar[0] := C_FOO[0]; // Bar[0] = MyProgram$1$ActRec($1CC8CF0) as TBar
end.

Using the debugger, I can see Bar[0] being equal to some address (I think?), which tells me something is happening behind my understanding...

So is it possible to initialize a constant array like C_BAR in my example? If yes, how to do it, and otherwise, why?

Papaya
  • 332
  • 3
  • 15

1 Answers1

7

The pertinent documentation is here in the section on Typed Constants:

Typed constants, unlike true constants, can hold values of array, record, procedural, and pointer types. Typed constants cannot occur in constant expressions.

Declare a typed constant like this:

const identifier: type = value

where identifier is any valid identifier, type is any type except files and variants, and value is an expression of type. For example,

const Max: Integer = 100;

In most cases, value must be a constant expression; but if type is an array, record, procedural, or pointer type, special rules apply.

Those special rules for procedural types are as follows:

To declare a procedural constant, specify the name of a function or procedure that is compatible with the declared type of the constant. For example,

function Calc(X, Y: Integer): Integer;
begin
  ...
end;
type TFunction = function(X, Y: Integer): Integer;
const MyFunction: TFunction = Calc;

Given these declarations, you can use the procedural constant MyFunction in a function call:

I := MyFunction(5, 7)

You can also assign the value nil to a procedural constant.

So that explains why you can declare a typed constant with TFoo.

As for anonymous methods, they aren't listed anywhere in this documentation. Now, an anonymous method is implemented as an interface. That interface has a backing class, generated by the compiler, and therefore there needs to be an instance of that class created. That instance is allocated on the heap and this is (at least one reason) why you cannot declare an anonymous method to be a constant.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490