Callback functions for both object and non-object functions

Using Delphi I want to use callback functions as parameters of a procedure.
Let's say I write an optimization routine declared as
procedure goldensearch(f: TGoalFunction;  ax, bx, cx, tol: Real; var xmin, fmin: Real);
TGoalFunction is a callback function that I can define as
TGoalFunction = function(x: Real): Real of object;
or as
TGoalFunction = function(x: Real): Real;
In the first case the function has to be a memberfunction of a class in the second case it is is not a member function.

Now I want to use the goldensearch with both object member functions en non-object functions. How do I do that? It seems now to me that I have to program two nearly identical goldensearch procedures with the only difference the parameter with and without "of object".

Is there a way to handle both situations in one implementation?
IgnaceLamineAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

ziolkoCommented:
very simply:

function NonObjectGoal(x: Real):Real;
begin
.
.
.
end;

TSomeObject = class()
  function ObjectGoal(x: Real):Real;
end;

function TSomeObject.ObjectGoal(x: Real):Real;
begin
  Result := NonObjectGoal(x);
end;

this way you can use it in two ways:
goldensearch(NonObjectGoal, ....)
or
goldensearch(someobject.ObjectGoal, ....)

ziolko.
0
IgnaceLamineAuthor Commented:
If I am correct, you are saying that I should always choose the "function of object" parameter type. If my goalfunction is part of a class, then I'm lucky and can use it straight away, if it's not part of a class I should create a dummy class for it. Looks like a hack to me, but usable. I wish there were a more elegant solution.

Any other suggestions? Solutions?
0
ziolkoCommented:
you can of course use object method as callback in same object as goldensearch() or
creating dummy class for callback only might work but that's not "elegant" way.
so i suggest two ways:
1. use non-object callback function
2. if you want to stick to objects use event that would more "delphi like" and clear to understand for most delphi devs.

TObjectGoalEvent = function(Sender:TObject;x: Real):Real of object;

TSomeClass = class(TObject)
private
  FOnObjectGoal: TObjectGoalEvent;
public
  procedure goldensearch(f: TGoalFunction;  ax, bx, cx, tol: Real; var xmin, fmin: Real);
  property OnObjectGoal: TObjectGoalEvent read FOnObjectGoal write FOnObjectGoal;
end;
.
.
.
procedure
TSomeClass.goldensearch(f: TGoalFunction;  ax, bx, cx, tol: Real; var xmin, fmin: Real);
begin
.
.
.
  if Assigned(FOnObjectGoal) then
    FOnObjectGoal(Self, your x value here);
.
.
.
end;

TCallback = class(TObject)
  function MyOnObjectGoalEventHandler(Sender: TObject;x: Real);
end;


var someobj: TSomeClass;
      callbackobj: TCallback;

someobj.OnObjectGoal := callbackobj.MyOnObjectGoalEventHandler

ziolko.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

swiatloCommented:
Investigate TMethod type in your help file.
It has two fields. Code, Data: Pointer;
Data points to an object, Code points to execution routine - method, but I think you can put a function addres in there.

var
  sp: procedure(val:Integer) of Object;
begin
      TMethod(sp).code:=funcaddr;
      TMethod(sp).data:=obj;

     if TMethod(sp).data<>nil then
       sp(5)                               //Execute as an object method
    else
       TMethod(sp).code(5) //Execute as stand alone function



0
swiatloCommented:
Myabe this would help, but I think that ziolko's warp a funciton is a better idea


unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    ObjFunc: TButton;
    Edit1: TEdit;
    StdFun: TButton;
    procedure ObjFuncClick(Sender: TObject);
    procedure StdFunClick(Sender: TObject);
  private
    { Private declarations }
    function formsinttostr(val:integer):string;
    procedure ExecuteFunction(tm:TMethod);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

function TForm1.formsinttostr(val:integer):string;
begin
  result:=inttostr(val);
end;

procedure TForm1.ExecuteFunction(tm:TMethod);
var
  sp: function(val:Integer):string of Object;
  fun: function(val:Integer):string;
begin

     if tm.data<>nil then
       begin
         TMethod(sp).code:=tm.code;
         TMethod(sp).data:=tm.data;
         Edit1.Text:=sp(5);                              //Execute as an object method
       end
     else
       begin
         fun:=tm.Code;
         Edit1.Text:=fun(7);            //Execute as stand alone function
       end;
end;

procedure TForm1.ObjFuncClick(Sender: TObject);
var
  sp: function(val:Integer):string of Object;
begin
  sp:=formsinttostr;
  ExecuteFunction(TMethod(sp));
end;

procedure TForm1.StdFunClick(Sender: TObject);
var
  tm:TMethod;
begin
  tm.code:=@inttostr;
  tm.data:=nil;
  ExecuteFunction(tm);
end;

end.
0
ziolkoCommented:
huh it's hard to tell which is better, but event oriented solution is more
"delphi like":)

ziolko
0
ziolkoCommented:
btw. with event like solution it's very easy to implement different behaviour
for handling "callback":

TCallback = class(TObject)
  function MyOnObjectGoalEventHandler_v1(Sender: TObject;x: Real);
  function MyOnObjectGoalEventHandler_v2(Sender: TObject;x: Real);
  function MyOnObjectGoalEventHandler_v3(Sender: TObject;x: Real);
  function MyOnObjectGoalEventHandler_v4(Sender: TObject;x: Real);
  function MyOnObjectGoalEventHandler_v5(Sender: TObject;x: Real);
end;

or

TCallback_v1 = class(TObject)
  function MyOnObjectGoalEventHandler(Sender: TObject;x: Real);
end;
TCallback_v2 = class(TObject)
  function MyOnObjectGoalEventHandler(Sender: TObject;x: Real);
end;
TCallback_v3 = class(TObject)
  function MyOnObjectGoalEventHandler(Sender: TObject;x: Real);
end;
TCallback_v4 = class(TObject)
  function MyOnObjectGoalEventHandler(Sender: TObject;x: Real);
end;
TCallback_v5 = class(TObject)
  function MyOnObjectGoalEventHandler(Sender: TObject;x: Real);
end;

and hook desired implementation

ziolko.
0
IgnaceLamineAuthor Commented:
Thanks ziolko, yeah I guess events are the way to go in Delphi. My only issue is that I don't like to use OOP stuff for core mathematical routines (in this case an optimization routine), which I like to keep procedural, but I might change my mind.

Swiatlo, you showed some very interesting stuff with the TMethod type. Not something I would use in practice, but the insight is worth a few points ;)
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.