Open alpinistbg opened 1 year ago
Thank you for this information!
It seems that FPC since 3.2.0 (at least partially) removed this limitation.
I tested on the attached test_method_callbacks.dpr, that deliberately does @TMyMethod.Add
instead in @TMyMethod(nil).Add
.
In the ObjFpc mode it definitely fails with older FPC versions. Tested now on FPC 2.6.4, 3.0.0, 3.0.2, 3.0.4 -- all fail like this:
$ fpc test_method_callbacks.dpr
Free Pascal Compiler version 2.6.4 [2014/03/03] for x86_64
Copyright (c) 1993-2014 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling test_method_callbacks.dpr
test_method_callbacks.dpr(24,8) Error: Incompatible types: got "<class method type of function(const LongInt,const LongInt):LongInt of object;Register>" expected "<procedure variable type of function(const LongInt,const LongInt):LongInt of object;Register>"
test_method_callbacks.dpr(25,8) Error: Incompatible types: got "<class method type of function(const LongInt,const LongInt):LongInt of object;Register>" expected "<procedure variable type of function(const LongInt,const LongInt):LongInt of object;Register>"
test_method_callbacks.dpr(27) Fatal: There were 2 errors compiling module, stopping
Fatal: Compilation aborted
Error: /home/michalis/installed/fpclazarus/2.6.4-sf/bin/ppcx64 returned an error exitcode (normal if you did not specify a source file to be compiled)
However, since FPC 3.2.0, including 3.2.2, it just works :)
Strangely, I didn't find a mention about this change in https://wiki.freepascal.org/FPC_New_Features_3.2.0 or https://wiki.freepascal.org/User_Changes_3.2.0 . Well, nevermind, the problem is gone.
Unfortunately, it seems it works at simple assignment, but not when method is a parameter. I.e. this still fails to compile, even with FPC 3.2.0 and 3.2.2:
function ActOnCallback(const A, B: Integer; const M: TMyMethod): Integer;
begin
Result := M(A, B);
end;
...
Writeln(ActOnCallback(10, 20, @TMyClass.Add));
So it is inconsistent now. One has to use the hack @TMyClass(nil).Add
when passing a parameter.
I'll update the page shortly, and also submit FPC bugreport about it. It should fail consistently, or be accepted consistently (also in parameters).
I'm still confused that it is possible to assign Self-less method to a procedural variable of object.
This possibility (using class method for of object
callback type) is because class methods actually also have a Self
pointer, but it points to the class and not the instance.
This pointer allows to use virtual class methods from the class function/procedure
implementation, because at runtime you know the exact class. The class methods that are passes are also allowed to be virtual.
See attached test_class_method_self.dpr
for what is enables.
As a side-effect, it also makes class methods and regular (instance) methods binary compatible. They both get extra pointer, in one case it is just a pointer to a class, in another a pointer to instance. From what I understand, when calling, the pointer to instance is also effectively a valid pointer to a class (though I couldn't find anymore exact spec how it is internally done, though I can imagine some ways).
This is an extra gain, because it means one can use class method as a value of of object
, and it just works.
This is different in static class methods, described on https://castle-engine.io/modern_pascal#_static_class_methods , that indeed have no Self
and thus their call is incompatible from calling of object
, i.e. they expect one less internal pointer. The static class methods are similar to regular (non-class) routines in this sense, i.e. calling them is just like calling a non-class routine.
See also
Again thanks for reporting this, I'll follow up with FPC report (to make it consistent) and updates to the "Modern Pascal Introduction" text.
Attaching the tests I mentioned above.
test_class_method_self.dpr.txt test_method_callbacks.dpr.txt
Rereading, I also see you found that Self
is indeed present in class methods. I hope that my explanation is still helpful :)
As a side-effect, it also makes class methods and regular (instance) methods binary compatible. They both get extra pointer, in one case it is just a pointer to a class, in another a pointer to instance. From what I understand, when calling, the pointer to instance is also effectively a valid pointer to a class (though I couldn't find anymore exact spec how it is internally done, though I can imagine some ways).
AFAIK the first entry into the instance memory is a pointer to VMT. Generalizing, classes can be thought of as a single instances of their meta-class, so that is a nice uniformity.
I'm working on a translation of your book, currently somewhat about 40-50% done. I've got some small (linguistic) troubles but that's quite normal. As a LT Pascal practitioner, back to TP3.0, I am nicely surprised to be able to discover some new things in that text.
Regards,
In 8.2 Callbacks (aka events, aka pointers to functions, aka procedural variables) at the end of page 70 (in .pdf):
That's not true.
It didn't occur to me to use class methods for procedural variable
of object
namely because I know there isno hiddenSelf in it (procedure of object
means it is a <object_reference, method_address> pair).To my surprise, your example works! But IMO it shouldn't. May be it works because one can invoke a class method using an instance only to specify indirectly it's class
and the actual reference is just ignored?Back to the statement:
I've tried:
And it works (thus your statement is not true).
Only when
Add
is a normal method, not aclass
one, then it won't work and it must be written as@TMyClass(nil).Add
, i.e. a reference must be specified.I'm still confused that it is possible to assign
Self-lessclass method to a procedural variable of object.Lazarus 1.9.0 r64660M FPC 3.1.1 x86_64-linux-gtk2
Edit: Corrections. There is a Self pointer in class methods according to: https://www.freepascal.org/docs-html/ref/refsu28.html and it points to the VMT. But what I've tried still works.