Calling a procedure from a string

Hi!

I want to call a procedure by using a string. (the overall idea is that I want to use a text file as a script to perform actions by cranking out strings to the main program).
I have tried with the help of ChatGPT but it cannot solve the problem that in this example the procedure GoWest within TAvatar is not accessible from within ‘RegisterProcedure’, only the property ‘appearance’
(Player.GoWest is accessible within SetupPlayer but not within 'RegisterProcedure).

What is wrong here?

type
TProcedure = procedure of object;

TAvatar = class
public
appearance: string;
procedure GoWest;
end;

TViewMain = class
private
Player: TAvatar;
ProcedureDict: specialize TDictionary<string, TProcedure>;
public
procedure RegisterProcedure(const ProcName: string; Proc: TProcedure);
procedure ExecuteProcedureByName(const ProcName: string);
procedure SetupPlayer;
end;

Implementation

procedure TAvatar.GoWest;
begin
walk_west.Exists:= true;
walk_west.PlayAnimation(Personalia.FirstName + ‘walkwest’, true);
end;

procedure TViewMain.SetupPlayer;
begin
Player := TAvatar.Create;
RegisterProcedure(‘GoWest’, Player.GoWest);
end;

procedure TViewMain.RegisterProcedure(const ProcName: string; Proc: TProcedure);
begin
ProcedureDict.Add(ProcName, Proc);
end;

procedure TViewMain.ExecuteProcedureByName(const ProcName: string);
var
Proc: TProcedure;
begin
if ProcedureDict.TryGetValue(ProcName, Proc) then
Proc()
else
Writeln('Procedure not found: ', ProcName);
end;

end.

Do you want to use a textbox in the game where you type in “Player X go to the North”? If I understand this correct, you will need a parser. Parse the string for names, places, actions,…


 var
   inputtext: string;
   i: integer;
 
   inputtext:='Player X go to the North';
   i:=Pos('the North',inputtext);  //Pos gives you the position where
// the first parameter (the North) is found in the inputtext.
// In this case i = 16. 
 if i>0 then place:='the North'.

And then call the procedures.
Procedure(name,action,place);

Note that explaining what exactly doesn’t work can help greatly debugging your issue :smiley:

See the fixed example below, I’ve commented what I’ve changed:

program project1;
uses
  SysUtils, Classes, Generics.Collections;

type
  TProcedure = procedure of object;

  TAvatar = class
  public
    appearance: string;
    procedure GoWest;
  end;

  TViewMain = class
  private
    Player: TAvatar;
    ProcedureDict: specialize TDictionary<string, TProcedure>;
  public
    procedure RegisterProcedure(const ProcName: string; Proc: TProcedure);
    procedure ExecuteProcedureByName(const ProcName: string);
    procedure SetupPlayer;
    destructor Destroy; override;
  end;

var
  Player: TAvatar;
  View: TViewMain;

procedure TAvatar.GoWest;
begin
  WriteLn('West');
end;

procedure TViewMain.SetupPlayer;
begin
  Player := TAvatar.Create;
  // don't forget to create the dictionary before using it
  ProcedureDict := (specialize TDictionary<string, TProcedure>).Create;
  // don't forget that FPC needs "@" symbol before function reference, unlike Delphi
  RegisterProcedure('GoWest', @Player.GoWest);
end;

procedure TViewMain.RegisterProcedure(const ProcName: string; Proc: TProcedure);
begin
  ProcedureDict.Add(ProcName, Proc);
end;

procedure TViewMain.ExecuteProcedureByName(const ProcName: string);
var
  Proc: TProcedure;
begin
  if ProcedureDict.TryGetValue(ProcName, Proc) then
    Proc
  else
    Writeln('Procedure not found: ', ProcName);
end;

destructor TViewMain.Destroy;
begin
  // note that you must free instances you create
  ProcedureDict.Free;
  Player.Free;
  inherited;
end;

begin
  View := TViewMain.Create;
  View.SetupPlayer;
  View.ExecuteProcedureByName('GoWest');
  View.Free;
  readln;
end.

Thanks Eugene.
The @ did it. ChatGPT did not use it. (I asked the code in Pascal)
So as a test I just asked to change the code in free pascal but then also I got the same GoWest without @.
Then I complained to ChatGPT this answer was wrong without using the @.

Then it said:
“You’re right. In Free Pascal, method pointers require the @ operator to reference the method correctly.”
(and then the right code was displayed).

:grin:

Thanks for you reply!

No, I want to perform an action (movement, animation procedure ) by calling it with just a string.
EugeneLoza pointed out I had to add a @ before the procedure name, now it works. :slightly_smiling_face:

I noticed some warnings in the output. What could be the cause?

When you define your own generics specialization (using containers from Generics.Collections), FPC unfortunately makes a bunch of warnings.

You cannot do anything about them, you cannot fix them, luckily they also don’t cause any harm in how the code works. I have reported this to FPC team on Compiling code using TDictionary from Generics.Collections results in a number of warnings and notes that developer cannot do anything about (#40222) · Issues · FPC / FPC / FPC Source · GitLab , unfortunately nothing has been fixed as of yet.

When you build using the engine GUI editor or command-line build tool, we just hide these warnings – there’s nothing better we can do, unfortunately. When you compile from Lazarus IDE (as you do, looking at above screenshot) you can hide them yourself – right-click on the warning, and choose “Hide with project option …” or “Filter all messages of this type …” – both options should reliably hide these warnings in the future.

I know that “just hiding warnings” sucks. I don’t have any better option right now, I’m afraid. Everyone is most welcome to ping FPC developers to handle Compiling code using TDictionary from Generics.Collections results in a number of warnings and notes that developer cannot do anything about (#40222) · Issues · FPC / FPC / FPC Source · GitLab :slight_smile: I still recommend to use generics, and to use generics from Generics.Collections – aside from this problem (warnings), they work great, have nice API, allow for safer (type-checked) code, are compatible with both FPC and Delphi.

Ah, ok. So there is nothing wrong with my code or a missing file that is mentioned. Thanks.
:slight_smile: