0

This is a follow up to this post.

I refined my requirement based on the accepted answer posted here.

My *.dpr file:

program DuckD11;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  uDuckTyping in 'uDuckTyping.pas',
  uBirds in 'uBirds.pas';

procedure DoSomething(AObject: TObject);
begin
  Duck(AObject).Quack;
end;

var
  Bird: TBird;
  Ganagana: TGanagana;
  Canard: TCanard;
begin
  Writeln('Duck typing :');
  Writeln;

  Bird := TBird.Create('Bird');
  try
    DoSomething(Bird);
  finally
    Bird.Free;
  end;

  Ganagana := TGanagana.Create;
  try
    DoSomething(Ganagana);
  finally
    Ganagana.Free;
  end;

  Canard := TCanard.Create;
  try
    DoSomething(Canard);
  finally
    Canard.Free;
  end;

  Readln;
end.

uBirds.pas listing:

unit uBirds;

interface

uses
  SysUtils;

type
  {$METHODINFO ON}
  TBird = class
  private
    FName: string;
  public
    constructor Create(AName: string);
    procedure Quack;
  end;

  TGanagana = class
  private
    const cName = 'Ganagana';
  public
    procedure Quack;
  end;

  TCanard = class
  private
    const cName = 'Canard';
  public
    procedure Quack;
  end;

  {$METHODINFO OFF}

implementation

{ TBird }

constructor TBird.Create(AName: string);
begin
  FName := AName;
end;

procedure TBird.Quack;
begin
  Writeln(Format('  %s->Quack',[Self.FName]));
end;

{ TGanagana }

procedure TGanagana.Quack;
begin
  Writeln(Format('  %s=>Quack',[Self.cName]));
end;

{ TCanard }

procedure TCanard.Quack;
begin
  Writeln(Format('  %s::Quack',[Self.cName]));
end;

end.

My attempt coding uDuckTyping.pas:

unit uDuckTyping;

interface

type
  IDuck = interface
    ['{41780389-7158-49F7-AAA5-A4ED5AE2699E}']
    procedure Quack;
  end;

function Duck(AObject: TObject): IDuck;

implementation

uses
  ObjAuto;

type
  TDuckObject = class(TInterfacedObject, IDuck)
  private
    FObj: TObject;

    // ???

  protected
      procedure Quack;
  public
    constructor Create(AObject: TObject);
  end;

function Duck(AObject: TObject): IDuck;
begin
  Result := TDuckObject.Create(AObject);
end;

{ TDuckObject }

constructor TDuckObject.Create(AObject: TObject);
begin
  FObj := AObject;

  // ???
end;

procedure TDuckObject.Quack;
begin
  // ???
end;

end.

My question:

I want to use

  • ObjAuto.GetMethodInfo to ascertain the existence of the wrapped Quack method.
  • ObjAuto.ObjectInvoke to invoke the wrapped Quack method.

How can I complete the code ?

Community
  • 1
  • 1
menjaraz
  • 7,551
  • 4
  • 41
  • 81
  • Why don't you just call it, and catch the exception? I have to laugh that you asked for dynamic typing, and now you're reluctant to just use it, and handle the aftermath the only way you can (exception handling). – Warren P Mar 09 '12 at 16:35
  • 1
    I just try to reproduce `Duck typing` as Daniele Teti did in the DORM (Dorm.utils.TDuckTypedList) but with D2007. He used TRttiMethod in conjunction with Invoke (targeting Delphi 2010 onward) in two step : 1) Extract method Rtti and store it somewhere and eventually raise exception in case of absence of the method or signature mismatch 2) Use later the stored Rtti to Invoke the method with the appropriate parameters. – menjaraz Mar 09 '12 at 17:36
  • You're doing IDispatch based handling in Delphi, I guess, which is homomorphic to TRttiMethod storage in Delphi 2010 onward. – Warren P Mar 10 '12 at 17:29

1 Answers1

2

I end up getting it to work after many trial:

Modifications in the uDucktyping.pas unit:


Fields added as private in TDuckObject class definition

FQuackPMethodInfo: PMethodeInfoHeader;
FParamIndexes: array of Integer;
FParams: array of Variant;

Initialization of FQuackPMethodInfo in TDuckObject.Create implementation

FQuackPMethodInfo := GetMethodInfo(AObject, ShortString('Quack'));

To append just after FObj initialization statement.


Invokation of "Quack" within TDuckObject.Quack implementation

if Assigned(FQuackPMethodInfo) then
  ObjectInvoke(FObj, FQuackPMethodInfo, FParamIndexes, FParams);
menjaraz
  • 7,551
  • 4
  • 41
  • 81
  • 1
    I think what you are trying to do is similar to TVirtualInterface introduced in XE2. While it is easy for this simple case of a parameterless method this might get more complicated when you have multiple methods with arguments. While duck typing is basically the adapter pattern what you did is more like a specific adapter (TObject to IDuck). – Stefan Glienke Mar 12 '12 at 10:01
  • @Stefan Glienke: You are right, my sample is very focused and limited: I have to investigate how to properly address the handling methods with arguments and eventually return value. BTW, you mention TVirtualInterface and I know that you have done a backport of if, can you consider this [post](http://stackoverflow.com/questions/9690523/backport-of-rtti-trttiindexedproperty-to-delphi-xe) please? – menjaraz Mar 14 '12 at 04:34
  • @Stefan Glienke: I have scrutinized `DSharp.Core.Plugins.pas` and appreciate it very much. Yes! **TVirtualInterface** is very capable. – menjaraz Mar 30 '12 at 12:51