Forwarded from marcov@stack.nl (Marco van de Voort):
In gmane.comp.compilers.gpc, you wrote:
Frank Heckenbach wrote:
begin bla:=xx.methodname; end;
So IIUC, the difference to Markus' way is that here the procedural variable also contains the object, while there it only points to the method, and can be applied to any object (of matching type), right?
Yes, though object-instance might be a better phrasing. It works though with uninitialised objects too as long as you don't actually "use" (dereference) self:
{$mode delphi}
type tmyobject = class function bla ( x: integer):integer; end;
tmethodvar = function (x:integer):Integer of object;
function tmyobject.bla(x:integer):Integer; begin writeln(x, ' ', PtrInt(self)); // ptrint is guaranteed pointer size compat end;
var x : TMyobject; y : tMethodVar;
begin x:=nil; // if you can detect uninitialised vars, this is not necessary. y:=x.bla; y(5); end.
will write
5 0
I'm a bit worried that this syntax is a bit focused on object types that are already (implicitely) a pointer, like Delphi.
If this is possible with (one of ) your object models, better warn if you try to assign a methodvar to a method of an object that is on the stack instead of on the heap.
Forwarded from marcov@stack.nl (Marco van de Voort):
In gmane.comp.compilers.gpc, you wrote:
Frank Heckenbach wrote:
begin bla:=xx.methodname; end;
So IIUC, the difference to Markus' way is that here the procedural variable also contains the object, while there it only points to the method, and can be applied to any object (of matching type), right?
Yes, though object-instance might be a better phrasing.
Yes, I mean an instance.
It works though with uninitialised objects too as long as you don't actually "use" (dereference) self:
I see. But I suppose Markus also wants to call the method via the procedural variable later and apply it to a certain object instance (selected at call time), something like (depending on the syntax chosen):
someotherx.y (5);
or:
y (someotherx, 5);
I'm a bit worried that this syntax is a bit focused on object types that are already (implicitely) a pointer, like Delphi.
For the syntax, adding a @ or ^ here and there should be all that's required.
If this is possible with (one of ) your object models, better warn if you try to assign a methodvar to a method of an object that is on the stack instead of on the heap.
Of course, that can be a problem -- as always if not carefully used. BTW, do you warn if taking the address (@) of a normal (non-object) variable on the stack?
Frank
Frank Heckenbach wrote:
I see. But I suppose Markus also wants to call the method via the procedural variable later and apply it to a certain object instance (selected at call time), something like (depending on the syntax chosen):
someotherx.y (5);
or:
y (someotherx, 5);
Exactly.
Markus Gerwinski wrote:
Peter N Lewis wrote:
OTOH, I would, of course, even prefer to have real, "syntax-approved" method variables that also take into account polymorphy and the fact that methods might be overridden.
I'm afraid I can't understand the point of this, why not just use an indirecting procedure:
Remove the attribute names, and define
Procedure testObjFoo ( obj: pointer ); begin pTestObj(obj)^.foo; end;
That's basically what I've been doing for a couple of years now, and as far as only my own code is affected, I find it a bit annoying, but acceptable. However... well, see below.
No need to rely on the compiler implementation of anything.
Sure there is a negligible drop in efficiency, and you have to write a duplicate for any method you want to use a a procedural variable, but these hardly seem like big problems unless you have very specific requirements that make them big problems...?
The point is: I'm writing an API where I'd like to give the application developer the possibility to pass a method as an initialization parameter to the constructors of some objects. For example to an intermediate wrapper that turns rows of an SQL result set into objects and vice versa.
Can you expand a bit on this example? I'm not sure I can really imagine why this is really necessary? Are the methods that you pass defined in the library and/or the application? Do you need to pass arbitrarily many different methods per object type (so virtual methods won't do)? Do you need it for static and/or virtual methods?
Frank
Frank Heckenbach wrote:
Markus Gerwinski wrote:
The point is: I'm writing an API where I'd like to give the application developer the possibility to pass a method as an initialization parameter to the constructors of some objects. For example to an intermediate wrapper that turns rows of an SQL result set into objects and vice versa.
Can you expand a bit on this example? I'm not sure I can really imagine why this is really necessary? Are the methods that you pass defined in the library and/or the application? Do you need to pass arbitrarily many different methods per object type (so virtual methods won't do)? Do you need it for static and/or virtual methods?
The methods will be defined in the application. The API will provide a "method variable" parameter to pass them. I don't quite understand the question with the many different methods per object. As for static/virtual, I believe in 99% of all cases static methods will do; however, I'm not sure if the case might arise some day I need to pass a virtual (or even abstract) method.
Imagine something like this:
(* Code of the API: *)
type
tGetIntegerAttr = Function: integer; method; tSetIntegerAttr = Procedure ( aValue: integer ); method;
tDBReader = object ... Procedure addAttribute ( const aFieldName: string; aGetter: tGetIntegerAttr; aSetter: tSetIntegerAttr ); ... end (* tDBReader *);
tDBReader is, in this case, a generic object to read/write database tables and transform them into (different types of) objects.
(* Code of the application: *)
type
tPerson = object ... Function getYearOfBirth: integer; Procedure setYearOfBirth ( aValue: integer ); end (* tPerson *);
var PersonReader: pDBReader;
(* And inside some DB access initialization: *) ... PersonReader^ .addAttribute ( 'YearOfBirth', tPerson.getYearOfBirth, tPerson.setYearOfBirth ); ...
Calling addAttribute like this should, in my example, tell the PersonReader instance to take, for any given tPerson instance, the year of birth out of the 'YearOfBirth' field in the according database table and pass it to the tPerson using the setYearOfBirth method; when writing it back to the database, the PersonReader shall use the getYearOfBirth method to get the value out of the object and write it back into the field 'YearOfBirth' in the DB.
I assume getYearOfBirth and setYearOfBirth will almost always be static methods. However, as I said above, I cannot say for sure I won't some day have a situation where I need a virtual or abstract one.