Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass method of arbitrary type into a procedure and identify it?

Tags:

delphi

I have a thread library which has three constructors for three different method types. I want them to be merged into one and some kind of logic to tell them apart inside the constructor. Is it possible? As there is TValue for values and such, is there a similar thing for method types?

I have the following types supported for now;

TAgThreadMethod1 = procedure of object;
TAgThreadMethod2 = procedure;
TAgThreadMethod3 = procedure(const AThread: TAgThread) of object;

and the constructors are like so:

constructor Create(const AOnRun: TAgThreadMethod1); overload; virtual;
constructor Create(const AOnRun: TAgThreadMethod2); overload; virtual;
constructor Create(const AOnRun: TAgThreadMethod3); overload; virtual;

For reference, I don't want the user to have the ability to change the worker method at a later time after the construction at all. So if a solution exists which can do something like this in a single constructor, is also welcome;

constructor Create
            (const AOnRun: [Some type which can hold arbitrary method types]);
begin

  // code to identify the method contained in AOnRun.
  // if supported, assign it the correct handler.

end;
like image 742
Umair Ahmed Avatar asked Mar 27 '14 21:03

Umair Ahmed


People also ask

How do you pass arbitrary number of arguments to a function?

The * symbol is used to pass a variable number of arguments to a function. Typically, this syntax is used to avoid the code failing when we don't know how many arguments will be sent to the function.

What are the 2 ways of passing an argument to a method?

The two most prevalent modes of passing arguments to methods are “passing-by-value” and “passing-by-reference”.

How do you pass a method as an argument in Java?

Instead, we can call the method from the argument of another method. // pass method2 as argument to method1 public void method1(method2()); Here, the returned value from method2() is assigned as an argument to method1() . If we need to pass the actual method as an argument, we use the lambda expression.


2 Answers

There isn't any good way to do this, because the whole point of a method pointer is to be invoked at some later point, and you can't do that unless you know its signature. So losing the distinction between signatures is very counterproductive.

If you only want to have to store one type of call inside your object, you could make the three constructors each create an anonymous method with a unified signature that wraps calls to the three types, and just store that instead of having to deal with multiple distinct method types. But what you're asking for, specifically, won't work.

like image 149
Mason Wheeler Avatar answered Oct 30 '22 17:10

Mason Wheeler


I think that the best that you can do is have a constructor that accepts the most general form of the procedural type. That is TProc<TAgThread>. This will be the master constructor, the only virtual constructor. You can then delegate the other constructors to the master constructors.

To recap, the declaration in SysUtils is:

type
  TProc<T> = reference to procedure(Arg1: T);

So your master constructor is:

constructor Create(AOnRun: TProc<TAgThread>); overload; virtual;

The other constructors are not virtual and might look like this:

constructor Create(AOnRun: TAgThreadMethod1); overload;

Implemented as:

constructor TMyClass.Create(AOnRun: TAgThreadMethod1);
begin
  Create(
    procedure(Thread: TAgThread)
    begin
      AOnRun();
    end;
  );
end;

The master constructor, the virtual one, does all the work. The other constructors, the non-virtual ones, adapt their arguments to fit that of the master constructor.

like image 38
David Heffernan Avatar answered Oct 30 '22 18:10

David Heffernan