Recent

Author Topic: Get Address of overloaded function  (Read 11646 times)

yogo1212

  • New Member
  • *
  • Posts: 22
Get Address of overloaded function
« on: February 15, 2014, 12:31:47 am »
Hello  :)

I was wondering if it is somehow possible to obtain the address of an overloaded procedure.
Consider this example:
Code: [Select]
type
  TSomeProcedure = procedure (meow: Integer);

var stuff: TSomeProcedure;

procedure DoStuff(meow: String);
// and:
procedure DoStuff(meow: Integer); overload;

This wont work:
Code: [Select]
stuff := @DoStuff;
Is there a way to achieve this goal?

kind regards

Edit: For the sake of completeness; this is the expected error:
Code: [Select]
Error: Incompatible type: Got "<address of procedure(String);Register>", expected "<procedure variable type of procedure(Integer);Register>"
« Last Edit: February 15, 2014, 12:34:29 am by yogo1212 »

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Get Address of overloaded function
« Reply #1 on: February 15, 2014, 01:03:25 am »
Here is one way to do it:
Code: [Select]
unit mainOverloadedProc;

{$mode objfpc}{$H+}

interface

uses
  Forms;

type

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  end;

  TSomeIntProcedure = procedure (meow: Integer);
  TSomeStrProcedure = procedure (meow: String);

procedure DoStuff(meow: String);

procedure DoStuff(meow: Integer); overload;

var IStuff: TSomeIntProcedure;
    SStuff: TSomeStrProcedure;
    Form1: TForm1;

implementation

uses LazLogger;

{$R *.lfm}

procedure DoStuff(meow: String);
begin
  DebugLn(['address of SStuff is ', SStuff]);
end;

procedure DoStuff(meow: Integer);
begin
  DebugLn(['address of IStuff is ',IStuff]);
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  SStuff:=@DoStuff;
  IStuff:=@DoStuff;
  DoStuff('');
  DoStuff(0);
end;

end.   

On my system this gives
Code: [Select]
address of SStuff is 00427950
address of IStuff is 004279C0

yogo1212

  • New Member
  • *
  • Posts: 22
Re: Get Address of overloaded function
« Reply #2 on: February 15, 2014, 01:31:32 am »
Oh well,

I am impressed !
But, after some tests, i found that this does not compile  %) :
Code: [Select]
unit unit1;

{$mode objfpc}{$H+}

interface

uses
  Forms, StdCtrls, Classes, sysutils;

type

  { TForm1 }

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure Memo1Change(Sender: TObject);
  end;

  TSomeIntProcedure = procedure (meow: Integer);
  TSomeStrProcedure = procedure (meow: String);

  TThing = record
    IStuff: TSomeIntProcedure;
    SStuff: TSomeStrProcedure;
  end;

procedure DoStuff(meow: String);
procedure DoStuff(meow: Integer); overload;

function Thing(      IStuff: TSomeIntProcedure;    SStuff: TSomeStrProcedure): TThing;

var
    Form1: TForm1;
    something: TThing;

implementation

uses LazLogger;

{$R *.lfm}

procedure DoStuff(meow: String);
begin
  DebugLn(['address of SStuff is ', something.SStuff]);
end;

procedure DoStuff(meow: Integer);
begin
  DebugLn(['address of IStuff is ', something.IStuff]);
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  something := Thing(@DoStuff, @DoStuff);
  DoStuff('');
  DoStuff(0);
end;

end.

Code: [Select]
unit1.pas(57,30) Error: Incompatible type for arg no. 1: Got "<address of procedure(AnsiString);Register>", expected "<procedure variable type of procedure(LongInt);Register>"

passing as paremeters does not seem to function  :(
but then, you might have an answer to that too; am looking forward to it

Thank you for your quick response!

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: Get Address of overloaded function
« Reply #3 on: February 15, 2014, 03:26:25 am »
If you have this
Code: [Select]
procedure DoStuff(meow: String);
procedure DoStuff(meow: Integer); overload;

Then @DoStuff will point to DoStuff(meow: String) because it is defined first. That's what means "Got "<address of procedure(AnsiString)". You expect int in first parameter so it's not compatible. howardpc was showing alternative ways to determine the pointer, and your approach will just not work, not even with typecasting. Some different way is needed.

Leledumbo

  • Hero Member
  • *****
  • Posts: 8819
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Get Address of overloaded function
« Reply #4 on: February 15, 2014, 05:15:59 am »
Quote
your approach will just not work, not even with typecasting.
Hmm... I found this works:
Code: [Select]
{$mode objfpc}{$H+}

type

  TSomeIntProcedure = procedure (meow: Integer);
  TSomeStrProcedure = procedure (meow: String);

  TThing = record
    IStuff: TSomeIntProcedure;
    SStuff: TSomeStrProcedure;
  end;

var
  something: TThing;

procedure DoStuff(meow: String);
begin
  WriteLn('address of SStuff is ' + HexStr(PtrUInt(@something.SStuff),8));
end;

procedure DoStuff(meow: Integer); overload;
begin
  WriteLn('address of IStuff is ' + HexStr(PtrUInt(@something.IStuff),8));
end;

function Thing(IStuff: TSomeIntProcedure; SStuff: TSomeStrProcedure): TThing;
begin
end;

begin
  something := Thing(TSomeIntProcedure(@DoStuff), TSomeStrProcedure(@DoStuff));
  DoStuff('');
  DoStuff(0);
end.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Get Address of overloaded function
« Reply #5 on: February 15, 2014, 12:44:22 pm »
Yes, the strongly typed nature of Pascal is a great help here (as is so often the case).
Typed procedure pointers allow accurate typecasting, since to the compiler the different procedure signatures mean the two identically named routines are entirely distinct; and the polymorphic "DoStuff" name supplied by the programmer can be resolved unambiguously, so the correct overloaded routine is identified and called.

User137

  • Hero Member
  • *****
  • Posts: 1791
    • Nxpascal home
Re: Get Address of overloaded function
« Reply #6 on: February 15, 2014, 04:29:53 pm »
Hmm... I found this works:
...
Not for me...

Try with this:
Code: [Select]
function Thing(IStuff: TSomeIntProcedure; SStuff: TSomeStrProcedure): TThing;
begin
  IStuff(0);
  SStuff('');
end;
and main program
Code: [Select]
begin
  something := Thing(TSomeIntProcedure(@DoStuff), TSomeStrProcedure(@DoStuff));
  readln()
end.

I get:
Code: [Select]
address of SStuff is 0040D004
address of SStuff is 0040D004

hinst

  • Sr. Member
  • ****
  • Posts: 303
Re: Get Address of overloaded function
« Reply #7 on: February 15, 2014, 04:42:43 pm »
This worked for me: // FreePascal 2.6.2, mode: ObjFPC
Code: [Select]
program project1;

procedure Print(const s: string);
begin
  WriteLN('String: ', s);
end;

procedure Print(i: Integer);
begin
  WriteLN('Integer: ', i);
end;

type
  TPrintString = procedure(const s: string);
  TPrintInteger = procedure(i: Integer);

var
  printString: TPrintString;
  printInteger: TPrintInteger;

begin
  printString := @Print;
  printInteger := @Print;
  printString('ABC');
  printInteger(100);
end.
Prints:
Code: [Select]
String: ABC
Integer: 100
Turns out compiler determines address of which overloaded procedure to take based on type of result. Which is in fact strange because FPC in general follows this rule: do not determine anything from type of result, only from type of arguments. I guess developers decided to make an exception for this case
Too late to escape fate

hinst

  • Sr. Member
  • ****
  • Posts: 303
Re: Get Address of overloaded function
« Reply #8 on: February 15, 2014, 04:58:03 pm »
an important addition:
typed @ operator compiler option should be enabled to make this work
I recommend one always keep this compiler option enabled
User137 you probably have this setting disabled; that is way it does not work for you
Too late to escape fate

Leledumbo

  • Hero Member
  • *****
  • Posts: 8819
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Get Address of overloaded function
« Reply #9 on: February 15, 2014, 04:59:37 pm »
Quote
Turns out compiler determines address of which overloaded procedure to take based on type of result. Which is in fact strange because FPC in general follows this rule: do not determine anything from type of result, only from type of arguments. I guess developers decided to make an exception for this case
No, FPC does it right: determine only from the signature, which doesn't include result type. Those are pure procedures, no functions.
Quote
an important addition:
typed @ operator compiler option should be enabled to make this work
I recommend one always keep this compiler option enabled
This is the thing that actually makes your code works without explicit typecasting because @ operator returns typed pointer instead of untyped one.

yogo1212

  • New Member
  • *
  • Posts: 22
Re: Get Address of overloaded function
« Reply #10 on: February 15, 2014, 07:25:29 pm »
typed @ operator compiler option should be enabled to make this work

Thanks for the hint, will consider!

I recommend one always keep this compiler option enabled
Why isn't this the default anyway?

Still, this doesn't work for me (with the option on):

Code: [Select]
program Project1;

{$mode objfpc}{$H+}

{$T+}

uses sysutils;

type
  TIntProc = procedure (bla: Integer);
  TPIntProc = procedure (bla: PInteger);

procedure a(bla: Integer);
begin

end;

procedure a(bla: PInteger); overload;
begin

end;

procedure printAddr(proc: TIntProc);
begin
  writeln(hexStr(@proc));
end;

procedure printAddr(proc: TPIntProc); overload;
begin
  writeln(hexStr(@proc));
end;

begin
  printAddr(TIntProc(@a));
  printAddr(TPIntProc(@a));
end.

I am suprised to see this many people discussing this topic

kind regards

hinst

  • Sr. Member
  • ****
  • Posts: 303
Re: Get Address of overloaded function
« Reply #11 on: February 15, 2014, 08:24:50 pm »
@yogo1212 first of all:
Code: [Select]
procedure printAddr(proc: TIntProc);
begin
  writeln(hexStr(@proc)); // wrong: you get pointer to pointer
  writeln(hexStr(Pointer(proc))); // right: you get pointer to procedure

And:
Code: [Select]
program Project1;

{$mode objfpc}{$H+}

{$T+}

uses sysutils;

type
  TIntProc = procedure (bla: Integer);
  TPIntProc = procedure (bla: PInteger);

procedure a(bla: Integer);
begin
  WriteLN(bla);
end;

procedure a(bla: PInteger); overload;
begin
  WriteLN(bla^);
end;

procedure printAddr(proc: TIntProc);
begin
  writeln(hexStr(Pointer(proc)));
end;

procedure printAddr(proc: TPIntProc); overload;
begin
  writeln(hexStr(Pointer(proc)));
end;

var
  ai: TIntProc;
  api: TPIntProc;

begin
  ai := @a;
  api := @a;
  printAddr(ai);
  printAddr(api);
end.
Seems like FPC 2.6.2 only does this "trick" when you assign pointer to variable, not just use it as an argument:

Works:
Code: [Select]
begin
  ai := @a;
  api := @a;
  printAddr(ai);
  printAddr(api);
end.

Does NOT work
Code: [Select]
begin
  printAddr(TIntProc(@a));
  printAddr(TPIntProc(@a));
end.

Hmmmm you cast the pointer so... yeah, perhaps everything works like expected here... IDK
When you cast it explicitly, overloaded procedure type inference does not work; it only works when you either assign it or pass as an argument but don't cast it
« Last Edit: February 15, 2014, 08:31:34 pm by hinst »
Too late to escape fate

yogo1212

  • New Member
  • *
  • Posts: 22
Re: Get Address of overloaded function
« Reply #12 on: February 16, 2014, 12:14:14 am »
@yogo1212 first of all:
Code: [Select]
procedure printAddr(proc: TIntProc);
begin
  writeln(hexStr(@proc)); // wrong: you get pointer to pointer
  writeln(hexStr(Pointer(proc))); // right: you get pointer to procedure

Great, thank you! I didn't pay too much attention there.

Seems like FPC 2.6.2 only does this "trick" when you assign pointer to variable, not just use it as an argument:
That was my initial suspicion (see third post). Thank you for clarifying, i had hoped for a way to explicitely
reference an overloaded method and i really dont have the background information.

Here is a complete working example, in case anyone might need it:
Code: [Select]
program project1;

{$mode objfpc}{$H+}

{$T+}

uses
SysUtils;

type
TIntProc = procedure(bla: integer);
TStringProc = procedure(bla: string);
TPIntProc = procedure(bla: PInteger);

procedure ding(bla: integer);
begin
Sleep(bla);
end;

procedure ding(bla: PInteger); overload;
begin
bla^ := 1;
end;

procedure ding(bla: string); overload;
begin
bla := 'meow';
end;

procedure printAddr(proc: TIntProc);
begin
writeln('first:' + #9 + hexStr(proc));
end;

procedure printAddr(proc: TPIntProc); overload;
begin
writeln('second:'+ #9 + hexStr(proc));
end;

procedure printAddr(proc: TStringProc); overload;
begin
writeln('third:'+ #9 + hexStr(proc));
end;

var a: TIntProc;
  b: TPIntProc;
  c: TStringProc;

begin
  a := @ding;
  b := @ding;
  c := @ding;
// works:
printAddr(a);
printAddr(b);
printAddr(c);
// yields incorrect results:
printAddr(TIntProc(@ding));
printAddr(TPIntProc(@ding));
printAddr(TStringProc(@ding));   
end.

I hope this shows both the beauty and the ugliness.

Is this worth a feature request? Being able to name a particular overloaded  method?
Like: @dong[String]

While it looks bad, it avoids having to allocate memory for a variable in order to pass the reference to a method.

thank you for sticking around and good night

Leledumbo

  • Hero Member
  • *****
  • Posts: 8819
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Get Address of overloaded function
« Reply #13 on: February 16, 2014, 06:29:43 am »
Quote
Why isn't this the default anyway?
No idea, the documentation doesn't say the reason, but perhaps for compatibility with other compilers. Anyway, FPC and Lazarus itself uses pointer conversion/manipulation in several places (well, a few years ago, don't know if some has changed), which requires @ to be untyped.
Quote
Is this worth a feature request? Being able to name a particular overloaded  method?
Like: @dong[String]
Nope IMHO, don't know with others.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: Get Address of overloaded function
« Reply #14 on: February 16, 2014, 10:34:32 am »
Quote
Is this worth a feature request? Being able to name a particular overloaded  method?
Like: @dong[String]

I can't see the point of this. If you want uniquely named methods, don't overload them. If you want different methods to share the same name, use overloading. The choice is yours at the class/method design stage. You can't really have both features at once.

 

TinyPortal © 2005-2018