Delphi: Call a function whose name is stored in a string

27,174

Solution 1

Please give more details on what are you trying to achieve.

As far as I know:

  • It is not possible to call a random function like that.
  • For class and object functions (MyObject.Function) this can be done with RTTI, but it's a lot of work.
  • If you just need to call one particular type of functions (say, function(integer, integer): string), it's a lot easier.

For the last one, declare a function type, then get a function pointer and cast it like this:

type
  TMyFuncType = function(a: integer; b: integer): string of object;

  TMyClass = class
  published
    function Func1(a: integer; b: integer): string;
    function Func2(a: integer; b: integer): string;
    function Func3(a: integer; b: integer): string;
  public
    function Call(MethodName: string; a, b: integer): string;
  end;

function TMyClass.Call(MethodName: string; a, b: integer): string;
var m: TMethod;
begin
  m.Code := Self.MethodAddress(MethodName); //find method code
  m.Data := pointer(Self); //store pointer to object instance
  Result := TMyFuncType(m)(a, b);
end;

{...}

//use it like this
var MyClass: TMyClass;
begin
  MyClass := TMyClass.Create;
  MyClass.Call('Func1', 3, 5);
  MyClass.Call('Func2', 6, 4);
  MyClass.Destroy;
end.

Solution 2

You didn't specify your Delphi version, However if you have Delphi 2010(+) you can do it using the enhanced RTTI, I'm not expert on them, but I tried this sample for you:

  TProcClass = class
    public
      procedure SayHi;
      function GetSum(X,Y:Integer): Integer;
  end;

uses
  Rtti;

{ TProcClass }

procedure TProcClass.SayHi;
begin
  ShowMessage('Hi');
end;

function TProcClass.GetSum(X, Y: Integer): Integer;
begin
  ShowMessage(IntToStr(X + Y));
end;

procedure ExecMethod(MethodName:string; const Args: array of TValue);
var
 R : TRttiContext;
 T : TRttiType;
 M : TRttiMethod;
begin
  T := R.GetType(TProcClass);
  for M in t.GetMethods do
    if (m.Parent = t) and (m.Name = MethodName)then
      M.Invoke(TProcClass.Create,Args)
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  ExecMethod('SayHi',[]);
  ExecMethod('GetSum',[10,20]);
end;

The good things, if you have procedure or function with parameters it will work without more work.

Solution 3

I'm surprised no one has suggested a dispatch table. This is exactly what it's for.

program RPS;

uses
  SysUtils,
  Generics.Collections;

type
  TDispatchTable = class(TDictionary<string, TProc>);

procedure Rock;
begin
end;

procedure Paper;
begin
end;

procedure Scissors;
begin
end;

var
  DispatchTable: TDispatchTable;

begin
  DispatchTable := TDispatchTable.Create;
  try
    DispatchTable.Add('Rock', Rock);
    DispatchTable.Add('Paper', Paper);
    DispatchTable.Add('Scissors', Scissors);

    DispatchTable['Rock'].Invoke; // or DispatchTable['Rock']();
  finally
    DispatchTable.Free;
  end;
end.

The implementation I wrote uses generics so it would only work with Delphi 2009+. For older versions it would probably be easiest to implement using TStringList and the command pattern

Solution 4

With Delphi 2010 you can uses JSON and SuperObject to invoke method with parametters.

http://code.google.com/p/superobject/source/browse/#svn/trunk

If you need, there is also an xml parser to transform xml to json.

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure TestMethod(const value: string);
  end;

var
  Form1: TForm1;

implementation
uses superobject;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  SOInvoke(Self, 'TestMethod', SO('{value: "hello"}'));
end;

procedure TForm1.TestMethod(const value: string);
begin
  Caption := value;
end;

Solution 5

If you are asking if there is something like the JavaScript eval() is possible in Delphi, no this is not (easily) achievable since Delphi compiles to native code.

If you need only to support some strings you can always do many if or a case... Something like:

if myString = 'myFunction' then
    myFunction();
Share:
27,174
Woutb21
Author by

Woutb21

Updated on August 13, 2020

Comments

  • Woutb21
    Woutb21 almost 4 years

    Is it possible to call a function whose name is stored in a string in Delphi?

  • himself
    himself over 13 years
    It is, for class functions. RTTI can be stored for them too, even with parameter and calling convention details. But it's PITA.
  • Jeroen Wiert Pluimers
    Jeroen Wiert Pluimers over 13 years
    +1 for the idea, especially for Delphi 2009 and earlier. With Delphi 2010+, the RTTI already has all the underlying info in it, so then Mohammed's and Henri's answer are easier.
  • menjaraz
    menjaraz over 12 years
    It's beside the point but still has to do with SuperObject. I know you are the right man at the right place :-). Can you pay a little attention to this question somewhere on SO?
  • itsols
    itsols over 11 years
    I've done this in VB6 and and it is a native-compiled language. So I believe it must be possible with Delphi as well (and in my case Lazarus Pascal)
  • itsols
    itsols over 11 years
    @RobMcDonell +1 for this idea. I have done a similar thing in the old VB6 and I'm trying to port this to Lazarus (which I'm very new to). I think your line TAction(MainForm.Components[I]).Execute; is the hub of the whole thing. Would you please direct me to a suitable doc on the command? Even a simple explanation would do. Thanks!
  • itsols
    itsols over 11 years
    It must be possible. If VB6 can do it, certainly Delphi must allow it.
  • itsols
    itsols over 11 years
    Excuse me - I'm pretty new to this platform. How can you assign a value like you've done in the firs line? What data type is TExec?
  • TheDude
    TheDude over 11 years
    @itsols: Sure I edited my answer, basically you put the line that says TExec = procedure of Object; just after the type keyword and that's it
  • Rob Kennedy
    Rob Kennedy over 11 years
    Please note that this code leaks at least one TProcClass instance for each method called this way.
  • Carlos Andrade
    Carlos Andrade over 6 years
    The best answer
  • Sertac Akyuz
    Sertac Akyuz about 6 years
    Also exhibits a possible mistake where the compiler cannot help. Correct the forward declaration of "Test" to match the actual function, leave "TMyProc" as is. Call "p" and have an access violation.
  • Didier Cabalé
    Didier Cabalé almost 6 years
    interesting, provided that the stored function is of the same type (here TProc)
  • crazy_in_love
    crazy_in_love about 5 years
    Just want to point that this is the correct answer to the OP actual problem. You can also enable/disable actions and these will be reflected in the controls that use them (so you get enabled / disabled menu items, popups, buttons, etc.)