Board index » delphi » Class method pointers vs Object method pointers

Class method pointers vs Object method pointers

Hello,

I have just come across a problem that I wanted to solve
using method pointers. In short, I have a class containing
a list of other classes. I have a separate iterator class
that can iterate through the container class in various
ways, performing various actions.

Now what I'd like to do is to be able to pass a class
method pointer to this iterator, saying something like:
for each item you find in the container, call method
'GetX' of this container item.

This seems to be impossible to do, because (If I am correct,
please prove me wrong) method pointers always point to a
method of a specific object. It seems to be impossible to
define method pointers that point to a method of a class,
so that you can later say 'now do your
stuff on this instance of the class'.

What I'm basically asking is: is there something I've
overlooked? I hope there is, even though it's only a small
problem and there's an easy workaround. It's just that it
seems so easy to do (and I've done it in C++, which on the
other hand does not have object method pointers but only
class method pointers (at least last time I checked))

In case all this has become a bit unclear, I put some code
below to show you what I'm trying to do (and what I'm doing
instead at this moment).

Greetings,
Eric.

PS: Example:

  class TFoo
   ...
   function GetX:longint;
   function GetY:longint;
   ...
   class function ClassGetX(obj:TFoo):longint;
  end;

  class TFooContainer
   list:TFooList;
   ...
  end;

  class TFooIterator
   function First:TFoo;
   function Next:TFoo;
   ...
   function ForEach(func:classmethod):longint;
   ...
  end;

  implementation

  function TFooIterator.ForEach(func:classmethod):longint;
  var foo:TFoo;
      total:longint;    
  begin
   total:=0;
   foo:=First;
   while foo<>nil do
    begin
               { type func = function:longint of class }
     Inc(total, foo^.func);
     ^^^^^^^^^^^^^^^^^^^^^
               This seems to be impossible...

               { type func = function(obj:TFoo):longint of object }
     Inc(total, func(foo));
     ^^^^^^^^^^^^^^^^^^^^^
               This works but requires an extra 'getter' for each
               method of foo I want to access
     foo:=Next;
    end;
  end;

 procedure DoSomething(TFooContainer foos);
 var iter:TFooIterator;
 begin
  iter:=TFooIterator.Create(foos);

  iter.ForEach(TFoo.GetX);
  { func = function:longint of class }
  { The compiler doesn't like this syntax... }

  iter.ForEach(TFoo.ClassGetX);
  { func = function(obj:TFoo):longint of object }
  { This one works }

  iter.Free;
 end;

 

Re:Class method pointers vs Object method pointers


On 16 Feb 1996 13:07:31 GMT, E.W.Harbe...@twi.tudelft.nl (Eric

Quote
Harberts) wrote:
>Now what I'd like to do is to be able to pass a class
>method pointer to this iterator, saying something like:
>for each item you find in the container, call method
>'GetX' of this container item.

I don't think there's a simple way to do this.   The problem is that
'GetX' is quite likely different code for each of the different
objects, since they'll have different implementations of it.

There are non-simple ways to achieve the same effect, but I don't
think any are better than the one you came up with (passing a function
which calls the method).  Ones I can think of are:

Declare a user-defined message handling method in each of your
objects, and pass that message to the object.

Figure out the internal format of the VMT table, and build your own
"of object" function pointer for each element in the list.

Declare a method pointer field in the base class, and fill it in with
the method you're going to call.

Really, I'd stick with the solution you've got.

Duncan Murdoch

Re:Class method pointers vs Object method pointers


Quote
Duncan Murdoch (dmurd...@mast.queensu.ca) wrote:

: On 16 Feb 1996 13:07:31 GMT, E.W.Harbe...@twi.tudelft.nl (Eric

Quote
: Harberts) wrote:

: >Now what I'd like to do is to be able to pass a class
: >method pointer to this iterator, saying something like:
: >for each item you find in the container, call method
: >'GetX' of this container item.

: I don't think there's a simple way to do this.   The problem is that
: 'GetX' is quite likely different code for each of the different
: objects, since they'll have different implementations of it.

: There are non-simple ways to achieve the same effect, but I don't
: think any are better than the one you came up with (passing a function
: which calls the method).  Ones I can think of are:

: Declare a user-defined message handling method in each of your
: objects, and pass that message to the object.

: Figure out the internal format of the VMT table, and build your own
: "of object" function pointer for each element in the list.

: Declare a method pointer field in the base class, and fill it in with
: the method you're going to call.

I'd use a different approach. The RTTI has the info required, and TObject
has a class function called MethodAddress, if you just pass the NAME of
the method (not the actual method), and then get your routine to look up the
method address for each object, then you'll be able to do what you want.

Richard

Re:Class method pointers vs Object method pointers


In <4g1vij$...@mo6.rc.tudelft.nl> E.W.Harbe...@twi.tudelft.nl (Eric Harberts) writes:

Quote
>Now what I'd like to do is to be able to pass a class
>method pointer to this iterator, saying something like:
>for each item you find in the container, call method
>'GetX' of this container item.
>This seems to be impossible to do, because (If I am correct,
>please prove me wrong) method pointers always point to a
>method of a specific object.

Interesting problem!

I think it can be done by exploiting the implementation
of method pointers. A method pointer is really a record
of two pointers: One pointer to the method and one pointer
to the object instance. Lookup in the help for TMethod.

You could simply iterate through the list of object instances
and set the object part of the method pointer before calling it.

I haven't done any testing, but something like this should do it:

uses Classes, SysUtils;

type
  TIntMethod = function : Longint of object;

  TTestObj = class
    function TestFunc: longint;
  end;

  TIterator = class(TList)
    procedure ForEach(Func: pointer);
  end;

function TTestObj.TestFunc: longint;
begin
  Result := 42;
end;

procedure TIterator.ForEach(Func: pointer);
var
  Sum : longint;
  IntMethod :  TIntMethod;
  i : integer;
begin
  Sum := 0;
  TMethod(IntMethod).Code := Func;
  for i := 0 to Count-1 do
  begin
    with TMethod(IntMethod) do
    begin
      TMethod(IntMethod).Data := Items[i];
      Sum := Sum + IntMethod;
    end;
  end;
end;

var
  It : TIterator;
begin
  It := TIterator.Create;
  It.ForEach(@TTestObj.TestFunc);
end.

The only 'problem' with this is that you have to use the
address operator (@ or Addr) in the call to ForEach. Delphi
will not let you send a pointer to a method without a object
instance unless it is a class method (as you pointed out).

By declaring the parameter as an untyped pointer and just
sending the address of the method, we can get around this
restriction. We loose type-safety, but gain in flexibility.

You could send a pointer to any method or procedure or garbage
to the ForEach routine even though it expects only method
functions returning longint. This have to be stated in the
documentation of ForEach and is something the compiler cannot
help you with.

Now in ForEach we declare a local variable to hold the method
pointer for each object in the list. Before the loop we set the
Code part of the pointer and in the loop we set the Data (or
object instance) part.

--
 Hallvard Vassbotn  | Falcon AS (a Reuters company)  | Without programmers,
 hallv...@falcon.no | Stortorget 10,0155 OSLO,Norway | the world would stop!

Re:Class method pointers vs Object method pointers


On Mon, 19 Feb 96 10:12:24 GMT, hallv...@falcon.no (Hallvard Vassbotn)
wrote:

Quote
>I think it can be done by exploiting the implementation
>of method pointers. A method pointer is really a record
>of two pointers: One pointer to the method and one pointer
>to the object instance. Lookup in the help for TMethod.

>You could simply iterate through the list of object instances
>and set the object part of the method pointer before calling it.

I don't think this would work as a general solution, because if the
method is virtual or dynamic, you would want to call different code
depending on the type of the object.  I suspect that this would always
call the same code, which might or might not be appropriate for the
attached object.

Duncan Murdoch

Re:Class method pointers vs Object method pointers


In <3129d6f8.398...@130.15.126.54> dmurd...@mast.QueensU.CA (Duncan Murdoch) writes:

Quote
>On Mon, 19 Feb 96 10:12:24 GMT, hallv...@falcon.no (Hallvard Vassbotn)
>wrote:
>>I think it can be done by exploiting the implementation
>>of method pointers. A method pointer is really a record
>>of two pointers: One pointer to the method and one pointer
>>to the object instance. Lookup in the help for TMethod.

>>You could simply iterate through the list of object instances
>>and set the object part of the method pointer before calling it.
>I don't think this would work as a general solution, because if the
>method is virtual or dynamic, you would want to call different code
>depending on the type of the object.  I suspect that this would always
>call the same code, which might or might not be appropriate for the
>attached object.
>Duncan Murdoch

Yes, you are right. It only properly works for static methods, not
for virtual or dynamic methods. I did not think about that at the
time. But even with this restriction, it could be useful.

--
 Hallvard Vassbotn  | Falcon AS (a Reuters company)  | Without programmers,
 hallv...@falcon.no | Stortorget 10,0155 OSLO,Norway | the world would stop!

Re:Class method pointers vs Object method pointers


Quote
hallv...@falcon.no (Hallvard Vassbotn) wrote:
>>Now what I'd like to do is to be able to pass a class
>>method pointer to this iterator, saying something like:
>>for each item you find in the container, call method
>>'GetX' of this container item.
>>This seems to be impossible to do, because (If I am correct,
>>please prove me wrong) method pointers always point to a
>>method of a specific object.
>I think it can be done by exploiting the implementation
>of method pointers. A method pointer is really a record
>of two pointers: One pointer to the method and one pointer
>to the object instance. Lookup in the help for TMethod.

Thanks for this hint. I have had nearly the same problem.  
I have a class  TViewer  with the method  ViewObject(Obj: TObject); .
The method ViewObject should be able to retrieve data from Obj  if
it has the method GetViewData(Var Data :TViewData).
Obj can be any descendant of TObject and the class type of Obj does
not have to exist at compile-time.

With your hint of using TMethod, I have found the following solution:

  tGetViewData = procedure(Var Data :TViewData) of object;

  procedure TViewer.ViewObject(Obj: TObject);
  var GetViewData :tGetViewData;
        Data  :TViewData;
        Mptr  :Pointer;

  begin
     ....
     Mptr:=Obj.MethodAddress('GetViewData');
     if Assigned(Mptr) then begin
        tMethod(GetViewData).code:=Mptr;
        tMethod(GetViewData).data:=Obj;
        ...
        GetViewData(Data);    {Call the Method Obj.GetViewData(Data) }
        ....
     end;
  end;

ViewObject first has to check if Obj has the method, which is needed
to view the Object and then it calls this method.

If somebody knows a better solution to this problem, please tell me.

-----
Manfred Paula

Other Threads