[fpc-pascal] with in classes/records
Ryan Joseph
ryan at thealchemistguild.com
Thu Sep 6 11:31:51 CEST 2018
> On Sep 4, 2018, at 12:35 PM, Sven Barth via fpc-pascal <fpc-pascal at lists.freepascal.org> wrote:
>
> I think you need to be clearer what you want to achieve in the end. The
> default property as intended by Maciej has the idea that it hoists the
> operators of the default property type to the record it is contained in.
Here’s my usage example which I encountered and got stuck with.
The problem is I have a base class that has some commonly used functionality across many subclasses but at a later time I wanted to remove the fields from the class to save memory. Now I’m stuck with an OOP problem where I have tons of syntax like obj.RegisterMethod which are used in dozens of subclasses.
The solutions currently are:
1) Put the two manager concepts into separate classes which can be subclassed. This may work in some instances but in others it breaks class hierarchies. If I want to include both I’m in trouble also because there’s no multiple inheritance in Pascal.
2) Put the two manager concepts into separate delegate classes which are included manually (delphi has the “implements” property but this doesn’t really do anything to help). This is more flexible but now I need to replace all calls as obj.methodManager.RegisterMethod which is 1) messy/long 2) introduces more names to remember and 3) will require lots of copy/paste. If I ever want to change it back I have hours of copy/pasting to do.
type
TBaseObject = class
private
methods: array of TMethod;
objects: array of TObject;
public
{ method manager }
procedure RegisterMethod(name: string; proc: pointer);
procedure InvokeMethod(name: string);
{ object manager }
procedure ManageObject(obj: TObject);
end;
// if we need either of the 2 managers we need to inherit from TBaseObject
// and take on the baggage. Typical OOP problems with deep class hierarchies.
type
TMyObject = class (TBaseObject)
end;
var
obj: TMyObject;
begin
obj := TMyObject.Create;
obj.RegisterMethod('foo', @method);
obj.InvokeMethod('foo');
obj.ManageObject(otherObj);
end.
========================================================================
Using what I’ll call the “alias property” for now (using “with” is not proper grammar Sven mentioned, and “default” implies just one, so...) we can pull out the two manager concepts into separate records and include them manually into any class we want. Thanks to having management operators now we can get automatic init/finalize to manage the lifecycle but I didn’t show that here.
The alias property acts like the default property we’ve been discussing by calling into the fields namespace without the property name. This is the preferred solution for breaking up the class because once I add the manager record and the alias I don’t need to change any other code, everything works like before.
What I propose is that the default property be allowed to support multiple default properties. The default property already basically does this so it’s just a matter of performing some searches before determining which property to use for the given call. If we made this safe it would be a good alternative to subclassing and allow for some really nice delegation patterns we can’t do in Pascal now.
Clearly there are problems with name conflicts and precedence but surely they can be solved. There’s already a precedent of multiple interfaces in classes and redefining methods in subclasses so I don’t see why this can’t be made safe also. Please note the default property already introduces these problems so in my opinion it’s not that crazy to add more levels of search than just one.
Here’s what the class looks like broken up into delegates and using aliases so they calling conventions don’t change from the original.
type
TMethodManager = record
private
methods: array of TMethod;
public
procedure RegisterMethod(name: string; proc: pointer);
procedure InvokeMethod(name: string);
end;
type
TObjectManager = record
private
objects: array of TObject;
public
procedure ManageObject(obj: TObject);
end;
type
TMyObjectA = class
private
m_methodManager: TMethodManager;
property methodManager: TMethodManager alias m_methodManager;
end;
type
TMyObjectB = class
private
m_objectManager: TObjectManager;
property objectManager: TObjectManager alias m_objectManager;
end;
var
objA: TMyObjectA;
objB: TMyObjectB;
begin
objA := TMyObjectA.Create;
objA.RegisterMethod('foo', @method);
objA.InvokeMethod('foo’);
objB := TMyObjectB.Create;
objB.ManageObject(otherObj);
end.
Regards,
Ryan Joseph
More information about the fpc-pascal
mailing list