[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