Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I include a method pointer in a typed constant?

Tags:

delphi

I'd like to use procedure of object in a record, like so:

TCommandRec = record
  name: string;
  fn: procedure of object;
end;

I can create an array with this by assignment:

commands: array [0..1] of TCommandRec;

...

commands[0].name := '-help';
commands[0].fn := DoHelp;
commands[1].name := '-load';
commands[1].fn := DoLoad;

What I'd really like to do is declare a constant:

const
  cmds: array [0..1] of TCommandRec =
  (
    (name: '-help'; fn: DoHelp),
    (name: '-load'; fn: DoLoad)
  );

However, I get errors for DoHelp and DoLoad - Constant expression expected. These are two methods of a class. Is there some syntax I need to use to make this work or am I stuck building the array at runtime?

like image 750
imekon Avatar asked Jun 15 '13 09:06

imekon


2 Answers

A method of object is what is known as a two pointer type. It encapsulates the following information:

  1. The address of the function.
  2. The address of the object, or the subject.

The former is known at compile time but generally the latter is not. Which is why you typically need to create these things at runtime.

If you can arrange that the subject is known at compile time then you can declare a typed constant of your record type. For example:

type
  TMyRecord = record
    Foo: procedure of object;
  end;

  TMyStaticClass = class
    class procedure Foo;
  end;

class procedure TMyStaticClass.Foo;
begin
end;

const
  MyRecord: TMyRecord = (Foo: TMyStaticClass.Foo);

Of course, that's only going to be useful to you if your functions are viable as class methods rather than instance methods. I'm just adding the code above to illustrate that you can have constant method pointers, so long as the subject is a compile time constant.

like image 119
David Heffernan Avatar answered Oct 09 '22 20:10

David Heffernan


You can store pointers to methods in your records (these are known at compile-time, so no problem specifying them in a const definition):

TCommandRec = record
  name: string;
  fn: Pointer;
end;

...
const
  cmds: array [0..1] of TCommandRec =
  (
    (name: '-help'; fn: @DoHelp),
    (name: '-load'; fn: @DoLoad)
  );

Then, when you need to call fn of cmds[i] (I suppose that the call occurs inside the same class which defines the DoHelp and DoLoad methods), write something like:

type TObjectProc = procedure of object;
var m: TMethod;
...
m.Code := cmds[i].fn;
m.Data := Self;
TObjectProc(m);
like image 32
nullptr Avatar answered Oct 09 '22 19:10

nullptr