I kinda feel I should know this. How can I get the address of a virtual class method, given an instance? I want to pass it as a regular function pointer.
I kinda feel I should know this. How can I get the address of a virtual class method, given an instance? I want to pass it as a regular function pointer.
Any relatively nice ways without using an asm block?
Any relatively nice ways without using an asm block?
I don't know the answer to your question. I would like to say it is probably a bad design idea. Could you explain why you need such construction? What is the problem it solves for you?
ReplyDelete--
http://francois-piette.blogspot.be
IIRC class methods have a reference to the class metadata as the first parameter, so they are not well suitable as regular callback functions.
ReplyDeleteFrançois Piette Just playing a bit, trying to make a routine go faster. I'm passing a class to a function and calling a virtual class method in a loop, and Delphi doesn't store the class pointer in a register so becomes an unnecessary memory load per call. This is mostly just for fun :-)
ReplyDeleteEugene Mayevski I'll double check but I'm pretty certain I saw the compiler only push the parameters on stack. Since I will be the sole consumer of the function pointer it's not an issue though.
Asbjørn Heid You may just declare a data type for the method and assign it. Something like this:
ReplyDeletetype
TMyClass = class
class function Demo(N : Integer) : Integer; virtual;
end;
TMyFct = function (N : Integer) : Integer of object;
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TMyClass }
class function TMyClass.Demo(N: Integer): Integer;
begin
Result := N + N;
end;
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
var
F : TMyFct;
I : Integer;
R : Integer;
begin
F := TMyClass.Demo;
R := 1;
for I := 0 to 4 do
R := F(R);
Memo1.Lines.Add(IntToStr(R)); // Should display 32
end;
--
http://francois-piette.blogspot.be
François Piette Hm, I'll try that thanks. Reason why I'm using virtual class methods is that I'm using generics, so quite limited by that. While better than nothing, Delphi generics are the worst of both worlds.
ReplyDeleteAsbjørn Heid The more we discuss, the more I understand that you have incompletely stated your real problem. If you want good help, please completely describe what you need. I mean the problem you have to solve, not the way you think you can solve it.
ReplyDeleteFrançois Piette I'm using a generic base class to define some abstract class methods, which I use to constrain a type T in another generic function Foo. In Foo I call these class methods in a loop.
ReplyDeleteAs mentioned I was just curious if there was a way to directly call the class methods without the extra dereference.
This is of course to get around the limitations caused by the compiler instantiating the generic methods when it parses them, rather then on use.
ReplyDeleteAsbjørn Heid You're welcome to show your code.
ReplyDeleteFrançois Piette http://nopaste.dk/p64795
ReplyDeleteEugene Mayevski If you look at the code I pasted above, here's the disassembly:
ReplyDeleteProject1.dpr.51: result := NumberImpl.Add(result, A[i]);
0041C027 push dword ptr [esp]
0041C02A push dword ptr [ebx]
0041C02C mov eax,[$0041a220]
0041C031 call dword ptr [eax+$04]
0041C034 fstp dword ptr [esp]
So does indeed just push the two parameters.
Asbjørn Heid
ReplyDeleteDepends on Delphi version. In C++Builder 2007 there's a vmt parameter for class methods, while in C++Builder XE5 there's no such parameter declared.
Sounds like an attempt at premature micro-optimization... :). BTDT.
ReplyDeleteWhile it can be fun (and frustrating) to try and find ways to get the compiler to generate better code, most of the time the results for real-world usage will be unmeasurable.
Well, it's mostly frustrating when you so often end up against a silly limitation or internal compiler errors. Fun when you don't :)
ReplyDeleteEugene Mayevski Ah ok. I'm using XE3.
ReplyDeleteHallvard Vassbotn Just doing a plain sum is of course just an example. While it's likely a micro-op like you say, unfortunately I can't test how much it would bring to the table, since I can't get any alternatives working. François Piette's method of declaring a method pointer generates an ICE.
ReplyDeleteThe internal compiler error is due to Algorithms being declared as a record and not class. Once I changed it it compiles fine, but generates the wrong code. The method "adder" points to is Numbers.Add, not the concrete implementation, causing an abstract error. Here's what I'm using now:
ReplyDeletetype
BinaryOperator = function(const A, B: T): T of object;
var
adder: BinaryOperator;
begin
adder := NumberImpl.Add;
...
Oh well, worth a shot.
Ok, finally got it. http://nopaste.dk/p64815
ReplyDeleteHad to go via an instance variable.
For reference: doing Kahan summation using the function pointers is 2-3% faster than the "plain" approach when using either single or double data type. Kahan summation performs 4 operations per loop iteration.
ReplyDeleteNot to mention the summation code looks a lot cleaner.
Try passing your doubles back & forth through var params, on an Inc() prototype ie. something like procedure(var A, B: T)
ReplyDeleteThe call overhead in your case isn't the issue, the stack juggling is.
Eric Grange I know it's not the primary factor by far, and yes passing by var is unfortunately much more effective than not. Too bad the optimizer sucks as it really makes the code hard to read.
ReplyDeleteAnyway, I can now finally do something almost C++-like:
sum := Algorithms.Sum(myArrayOfSingles);
vsum := Algorithms.Sum(myArrayOfVector3);
Asbjørn Heid For simple primitive types like these you're better of using dedicated code IMHO.
ReplyDeleteNot just for performance (which won't be C++ class), but also for clarity (the generics syntax looks terrible) and debug-ability: your call stacks will be hideous, stepping through them will be painful, and maintaining the code will be even worse.
Eric Grange The idea is was to support more than simple primitive types, and more specifically user-defined types. Anyway, just playing around :)
ReplyDelete