Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2016-04

A better way?

34 messages · 8 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Ryan Joseph

The most annoying problem I have with Pascal currently is with circular unit dependanices and “global” classes that need to be referenced from many units. In other languages I would make a forward declaration of the class in one file then implement it in another file so that all files could reference the class. It’s maybe a symptom of a “bad" design but sometimes it’s just faster and easier so it’s a problem I have to fight Pascal to make it work.

When I moved to FPC the solution I started using was this pattern below where I make an abstract class then override the methods I need in the global namespace within in the “main unit”. This is a bad hack to workaround a feature of the language but I wonder if there’s a better way. Does anyone have any ideas I could try?

========

"global” unit shared by many other units:

type
  TSomeClassAbstract = class (TObject)
    procedure DoSomething; virtual; abstract;
  end;

“main” unit which implements the class:

type
  TSomeClass = class (TSomeClassAbstract)
    procedure DoSomething; override;
  end;

Regards,
Ryan Joseph

Reply by email

Graeme Geldenhuys
to Ryan Joseph
Ryan Joseph wrote
The most annoying problem I have with Pascal currently is with circular unit dependanices and “global”

If you can give an actual example we can help. I've used TP then Delphi and now Free Pascal for more than 20+ years. I can probably count on one hand how many circular reference issues I had. So I dont' think it is such a big problem as you make out.

Often moving uses clause references from the Interface section to the Implementation section solve the problem. Sometimes using a base class in the interface works. Sometimes using Interfaces (the language feature) is a much better approach.

So again, if you can give an actual example of the various units, and how they relate (use each other), then we might be able to help you further.

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp

Reply by email

Tony Whyman
to Ryan Joseph: The most annoying problem I have with Pascal currently is with circular unit dependanices…

Ryan,

Reading through your post, I hope that you are aware that most circular dependencies can be avoided by referencing other units from the "uses" clause in the "implementation" section and keeping the "uses" clauses in "interfaces" down to those references strictly necessary for the unit's interface.

Having probably told you something you already know, i do sometimes get circular reference problems and usually because a program has evolved rather than having been designed properly. Sometimes it's better just to take as a hint to structure your program better but, otherwise, the techniques available are:

1. Abstract classes (as you suggest)

2. Interfaces

3. Dynamic casts.

Of the three, dynamic casts are often the quick and dirty way of fixing the problem as they can allow you to move a problematic unit reference from the "interface uses" to the "implementation uses", replacing the class reference in the unit interface to something generic like "TObject" and them coercing it to the required class when you actually use it.

I hope this helps.

Regards

Tony Whyman
MWA

Ryan Joseph wrote 28 lines
The most annoying problem I have with Pascal currently is with circular unit dependanices and “global” classes that need to be referenced from many units. In other languages I would make a forward declaration of the class in one file then implement it in another file so that all files could reference the class. It’s maybe a symptom of a “bad" design but sometimes it’s just faster and easier so it’s a problem I have to fight Pascal to make it work. When I moved to FPC the solution I started using was this pattern below where I make an abstract class then override the methods I need in the global namespace within in the “main unit”. This is a bad hack to workaround a feature of the language but I wonder if there’s a better way. Does anyone have any ideas I could try? ======== "global” unit shared by many other units: type TSomeClassAbstract = class (TObject) procedure DoSomething; virtual; abstract; end; “main” unit which implements the class: type TSomeClass = class (TSomeClassAbstract) procedure DoSomething; override; end; Regards, Ryan Joseph _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply by email

Ryan Joseph
to Tony Whyman
On Apr 14, 2016, at 3:02 PM, Tony Whyman wrote: Reading through your post, I hope that you are aware that most circular dependencies can be avoided by referencing other units from the "uses" clause in the "implementation" section and keeping the "uses" clauses in "interfaces" down to those references strictly necessary for the unit's interface.

I had no idea you could declare uses in the implementation! That could probably fix 90% of the problems I’m having which I’ve used abstract classes, dynamic method invocation and more recently interfaces to workaround. Don’t tell me this has been around for 20 years now I just never knew about it. ;) I should have asked sooner that’s for sure.

Thanks!

Regards,
Ryan Joseph

Reply by email

Sven Barth
to Ryan Joseph
"Ryan Joseph" wrote
> On Apr 14, 2016, at 3:02 PM, Tony Whyman
> > Reading through your post, I hope that you are aware that most circular

dependencies can be avoided by referencing other units from the "uses" clause in the "implementation" section and keeping the "uses" clauses in "interfaces" down to those references strictly necessary for the unit's interface.

I had no idea you could declare uses in the implementation! That could

probably fix 90% of the problems I’m having which I’ve used abstract classes, dynamic method invocation and more recently interfaces to workaround. Don’t tell me this has been around for 20 years now I just never knew about it. ;) I should have asked sooner that’s for sure.

When was Delphi 3 released? Before '96? In that case it would indeed be more than 20 years ;)

Regards,
Sven

Reply by email

Ryan Joseph
to Graeme Geldenhuys: If you can give an actual example we can help. I've used TP then Delphi and now Free…
Show quoted text 17 lines
On Apr 14, 2016, at 2:56 PM, Graeme Geldenhuys wrote: If you can give an actual example we can help. I've used TP then Delphi and now Free Pascal for more than 20+ years. I can probably count on one hand how many circular reference issues I had. So I dont' think it is such a big problem as you make out. Often moving uses clause references from the Interface section to the Implementation section solve the problem. Sometimes using a base class in the interface works. Sometimes using Interfaces (the language feature) is a much better approach. So again, if you can give an actual example of the various units, and how they relate (use each other), then we might be able to help you further. Regards, Graeme

I’ve just browsed over some code and found moving uses to the implementation did in fact help. That’s really helpful thank you both. However I’m still seeing some common patterns which just don’t seem Pascal friendly. I started using these more often after using Objective-C on Mac frequently and I really like it but it requires me to hack around the compiler in Pascal.

In that example below the “main” class has children it talks to using an interface and returning a reference to itself for introspection. They are interdependent but Pascal doesn’t offer a way to expose a global namespace for both the units as far as I know. In other languages I would make another “globals” unit and keep forward references to TClassA, TClassB and IClassA.

==============================

ClassB.pas:

uses
  ClassA;  // <----- circular reference but I need to know about ClassA
type
  TClassB = class (IClassA)
    // when we implement this method we may need to know some things
    // about the parent (TClassA) so it must be included
    procedure ClassDidThis (parent: TClassA; action: integer);
  end;

ClassA.pas:

uses
  ClassB;
type
  TClassA = class
    child: TClassB;
  end;
  // TClassA uses this interface to talk with it's “children” (TClassB)
  // and always returns a reference to itself because the children
  // often need to know about the parent also
  IClassA = interface
    procedure ClassDidThis (parent: TClassA; action: integer);
  end;

Regards,
Ryan Joseph

Reply by email

Ryan Joseph
to Sven Barth: dependencies can be avoided by referencing other units from the "uses" clause in the…
On Apr 14, 2016, at 4:14 PM, Sven Barth wrote: When was Delphi 3 released? Before '96? In that case it would indeed be more than 20 years ;)

I was using CodeWarrior back then (coming from a Mac background) and I didn’t switch to FPC until 2004 maybe. It would have been smart to learn about Delphi and new features but I just went ahead as normal missing lots of things that would help me in future years, like today for example. :)

Regards,
Ryan Joseph

Reply by email

Michael Van Canneyt
to Ryan Joseph: I’ve just browsed over some code and found moving uses to the implementation did in…
Ryan Joseph wrote 47 lines
> On Apr 14, 2016, at 2:56 PM, Graeme Geldenhuys wrote: > > If you can give an actual example we can help. I've used TP then Delphi > and now Free Pascal for more than 20+ years. I can probably count on one > hand how many circular reference issues I had. So I dont' think it is > such a big problem as you make out. > > Often moving uses clause references from the Interface section to the > Implementation section solve the problem. Sometimes using a base class > in the interface works. Sometimes using Interfaces (the language > feature) is a much better approach. > > So again, if you can give an actual example of the various units, and > how they relate (use each other), then we might be able to help you further. > > Regards, > Graeme I’ve just browsed over some code and found moving uses to the implementation did in fact help. That’s really helpful thank you both. However I’m still seeing some common patterns which just don’t seem Pascal friendly. I started using these more often after using Objective-C on Mac frequently and I really like it but it requires me to hack around the compiler in Pascal. In that example below the “main” class has children it talks to using an interface and returning a reference to itself for introspection. They are interdependent but Pascal doesn’t offer a way to expose a global namespace for both the units as far as I know. In other languages I would make another “globals” unit and keep forward references to TClassA, TClassB and IClassA. ============================== ClassB.pas: uses ClassA; // <----- circular reference but I need to know about ClassA type TClassB = class (IClassA) // when we implement this method we may need to know some things // about the parent (TClassA) so it must be included procedure ClassDidThis (parent: TClassA; action: integer); end; ClassA.pas: uses ClassB; type TClassA = class child: TClassB; end;

You should not need TClassB here. You defeat the point of using an interface.

   Child : IClassA;

should be sufficient.

Michael.

Reply by email

Ryan Joseph
to Michael Van Canneyt
On Apr 14, 2016, at 5:00 PM, Michael Van Canneyt wrote: You should not need TClassB here. You defeat the point of using an interface.

I’m using the interface for specific communication I want denoted in the interface but I’m still typically using properties of the child class in addition to the interface. Offloading all properties to the interface would work but it would be making accessing them very cumbersome because it requires using Support instead of just accessing them directly.

The interface was probably over complicating the example actually because the true problem is having this pattern of a parent->child relationship where both classes need to know about each other to some extent but putting them in the same unit causes clutter and pollution in other units namespaces. In this example it’s likely that many other units use TClassB and it’s not exclusive to TClassA so putting them in the same unit doesn’t make sense.

Maybe I’m doing something stupid but other languages have forward declarations so I wonder why Pascal isn’t doing this also since it seems like the obvious solution.

Regards,
Ryan Joseph

Reply by email

Tony Whyman
to Ryan Joseph

Ryan,

Maybe I’m doing something stupid but other languages have forward declarations so I wonder why Pascal isn’t doing this also since it seems like the obvious solution.

Pascal does have forward declarations e.g.

class TClassB;
class TClassA
private
   FClassBObject: TClassB;
end;

class TClassB
...
end;

Otherwise, if you really want to avoid defining interdependent classes in the same unit (not sure why you want to avoid this) then try this:

unit unitA;
interface
type
class TClassA
private
   FClassBObject: TObject;
public
   procedure SomeProc;
end;
implementation
uses unitB;

Maybe I’m doing something stupid but other languages have forward declarations so I wonder why Pascal isn’t doing this also since it seems like the obvious solution.

procedure TClassA.SomeProc;
begin
   TClassB(FClassBObject).OtherProc;
end;
end.

unitB is pretty similar.

As long as you make sure that FClassBObject really is a TClassB object when it is assigned, the above should all work. The only extra effort is with the TClassB(...) wrapper for each reference to FClassBObject.

Ryan Joseph wrote 17 lines
> On Apr 14, 2016, at 5:00 PM, Michael Van Canneyt wrote: > > You should not need TClassB here. You defeat the point of using an > interface. I’m using the interface for specific communication I want denoted in the interface but I’m still typically using properties of the child class in addition to the interface. Offloading all properties to the interface would work but it would be making accessing them very cumbersome because it requires using Support instead of just accessing them directly. The interface was probably over complicating the example actually because the true problem is having this pattern of a parent->child relationship where both classes need to know about each other to some extent but putting them in the same unit causes clutter and pollution in other units namespaces. In this example it’s likely that many other units use TClassB and it’s not exclusive to TClassA so putting them in the same unit doesn’t make sense. Maybe I’m doing something stupid but other languages have forward declarations so I wonder why Pascal isn’t doing this also since it seems like the obvious solution. Regards, Ryan Joseph _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply by email

Michael Van Canneyt
to Ryan Joseph: I’m using the interface for specific communication I want denoted in the interface but…
Ryan Joseph wrote 11 lines
> On Apr 14, 2016, at 5:00 PM, Michael Van Canneyt wrote: > > You should not need TClassB here. You defeat the point of using an > interface. I’m using the interface for specific communication I want denoted in the interface but I’m still typically using properties of the child class in addition to the interface. Offloading all properties to the interface would work but it would be making accessing them very cumbersome because it requires using Support instead of just accessing them directly.

So, put bluntly, you are unwilling to do things properly, and then complain that the language does not allow you to do this easily enough ?

No, you need to do things properly.

Show quoted text 8 lines
The interface was probably over complicating the example actually because the true problem is having this pattern of a parent->child relationship where both classes need to know about each other to some extent but putting them in the same unit causes clutter and pollution in other units namespaces. In this example it’s likely that many other units use TClassB and it’s not exclusive to TClassA so putting them in the same unit doesn’t make sense.

It certainly does make sense, if they are so intertwined.

This is not Java where you must put all classes in a single separate file.

If classes are so intertwined that they need detailed knowledge of each other's property, put them in 1 unit.

It is as simple as that.

Maybe I’m doing something stupid but other languages have forward declarations so I wonder why Pascal isn’t doing this also since it seems like the obvious solution.

Pascal has forward declarations. But only inside 1 unit.

Michael.

Reply by email

Ryan Joseph
to Tony Whyman: Pascal does have forward declarations e.g. class TClassB; class TClassA private…
Show quoted text 32 lines
On Apr 14, 2016, at 5:51 PM, Tony Whyman wrote: unit unitA; interface type class TClassA private FClassBObject: TObject; public procedure SomeProc; end; implementation uses unitB; Maybe I’m doing something stupid but other languages have forward declarations so I wonder why Pascal isn’t doing this also since it seems like the obvious solution. procedure TClassA.SomeProc; begin TClassB(FClassBObject).OtherProc; end; end. unitB is pretty similar. As long as you make sure that FClassBObject really is a TClassB object when it is assigned, the above should all work. The only extra effort is with the TClassB(...) wrapper for each reference to FClassBObject.

I use forward declarations sometimes but of course they need to be in the same block so they don’t solve this problem.

Yeah type casting. :) Like interfaces they work but they add a significant overhead and clutter (especially if they need to be used with dozens of methods in a unit) so that’s why I’m seeing if there’s a better way. However I think I like your idea of using them in conjunction with the uses in the implementation instead of my "abstract class" solution. Thanks for the tip.

Regards,
Ryan Joseph

Reply by email

Ryan Joseph
to Michael Van Canneyt: So, put bluntly, you are unwilling to do things properly, and then complain that the…
Show quoted text 6 lines
On Apr 14, 2016, at 6:09 PM, Michael Van Canneyt wrote: So, put bluntly, you are unwilling to do things properly, and then complain that the language does not allow you to do this easily enough ? No, you need to do things properly.

I thought that was probably the answer I’d get but I wanted to ask anyways. I get there’s a Pascal way but it’s making the job more difficult then it should be so I opt for the quick and easy hack. Maybe it’s bad form but at the end of the day I just want to make things work and get the job done instead of getting held up on small details.

Show quoted text 18 lines
> > The interface was probably over complicating the example actually because > the true problem is having this pattern of a parent->child relationship > where both classes need to know about each other to some extent but > putting them in the same unit causes clutter and pollution in other units > namespaces. In this example it’s likely that many other units use TClassB > and it’s not exclusive to TClassA so putting them in the same unit doesn’t > make sense. It certainly does make sense, if they are so intertwined. This is not Java where you must put all classes in a single separate file. If classes are so intertwined that they need detailed knowledge of each other's property, put them in 1 unit. It is as simple as that.

That would be a solution but it’s not very pretty. Having 2 classes in the same unit that need knowledge of each other but are still mutually exclusive is also bad design in my opinion.

Why is it so terrible that classes could have forward declarations outside of a single block anyways? It seems like a practical and efficient solution that requires lots of extra work in design or typecasting/interfaces.

Regards,
Ryan Joseph

Reply by email

Michael Van Canneyt
to Ryan Joseph
Ryan Joseph wrote 32 lines
> On Apr 14, 2016, at 6:09 PM, Michael Van Canneyt wrote: > > So, put bluntly, you are unwilling to do things properly, and then complain that the > language does not allow you to do this easily enough ? > > No, you need to do things properly. I thought that was probably the answer I’d get but I wanted to ask anyways. I get there’s a Pascal way but it’s making the job more difficult then it should be so I opt for the quick and easy hack. Maybe it’s bad form but at the end of the day I just want to make things work and get the job done instead of getting held up on small details. > >> >> The interface was probably over complicating the example actually because >> the true problem is having this pattern of a parent->child relationship >> where both classes need to know about each other to some extent but >> putting them in the same unit causes clutter and pollution in other units >> namespaces. In this example it’s likely that many other units use TClassB >> and it’s not exclusive to TClassA so putting them in the same unit doesn’t >> make sense. > > It certainly does make sense, if they are so intertwined. > > This is not Java where you must put all classes in a single separate file. > > If classes are so intertwined that they need detailed knowledge of each > other's property, put them in 1 unit. > > It is as simple as that. That would be a solution but it’s not very pretty. Having 2 classes in the same unit that need knowledge of each other but are still mutually exclusive is also bad design in my opinion.

Each is entitled to his opinion.

Practical people use the tools at their disposal how they are meant to use.

In this case the tool tells you that the 2 classes should be in 1 unit. So, you can waste your time looking for a way out, or just use the tool as it is meant.

Michael.

Reply by email

Ryan Joseph
to Michael Van Canneyt
Show quoted text 5 lines
On Apr 14, 2016, at 9:25 PM, Michael Van Canneyt wrote: In this case the tool tells you that the 2 classes should be in 1 unit. So, you can waste your time looking for a way out, or just use the tool as it is meant.

I think I prefer using type casting or abstract classes more than mixing 2 mutually exclusive classes into one unit even though they may have dependancies on each other. Editing and navigating the files would be confusing for one since there’s no obvious name to use for the file now.

Regards,
Ryan Joseph

Reply by email

Florian Klämpfl
to Michael Van Canneyt: Each is entitled to his opinion. Practical people use the tools at their disposal how…
Michael Van Canneyt wrote 13 lines
> > That would be a solution but it’s not very pretty. Having 2 classes in the > same unit that need knowledge of each other but are still mutually > exclusive is also bad design in my opinion. Each is entitled to his opinion. Practical people use the tools at their disposal how they are meant to use. In this case the tool tells you that the 2 classes should be in 1 unit. So, you can waste your time looking for a way out, or just use the tool as it is meant.

Or do it in two separate include files and add include both in the unit. FPC can do the C++ way :))))

Reply by email

Ewald
to Ryan Joseph: I think I prefer using type casting or abstract classes more than mixing 2 mutually…
Ryan Joseph wrote
I think I prefer using type casting or abstract classes more than mixing 2 mutually exclusive classes into one unit even though they may have dependancies on each other. Editing and navigating the files would be confusing for one since there’s no obvious name to use for the file now.

Mutually exclusive classes are mutually exclusive to classes which have dependencies on one another ;-)

Or am I missing something?

Ewald

Reply by email

Ryan Joseph
to Ewald
Show quoted text 6 lines
On Apr 15, 2016, at 1:23 AM, Ewald wrote: Mutually exclusive classes are mutually exclusive to classes which have dependencies on one another ;-) Or am I missing something?

In the example I gave TClassB may be used by many other units but TClassA is only used with one other unit. TClassB is communicating with TClassA via interfaces but it’s still useful standalone and distinct in nature.

This happens to me all the time actually where I need to expose some type information or an interface to both classes because I don’t want them merged.

Regards,
Ryan Joseph

Reply by email

Ryan Joseph
to Florian Klämpfl: Or do it in two separate include files and add include both in the unit. FPC can do the…

I’ve cleaned up some code by declaring uses in the implementation and using generic untyped variables like TObject for parameters. This compiles but I’m left with lots of ugly type casting in the implementation because the parameters for methods are untyped. Yes it works but I feel like the compiler could be helping more.

Just as an idea to float out here’s a suggestion for a “hint” syntax which would be used to solve the type casting issue by doing some dynamic typing.

Sure you could put those all in file for this example but in the real world there may be dozens of TClassB subclasses which would make the unit 10,000’s of lines long and impossible to navigate. The other solution as mentioned would be to use $includes and still use the single unit approach (how reliable is this? I’ve never tried).

Does this look like more work than using $includes? Maybe I need to seriously considering reorganizing my projects to work like this but it just feels wrong for some reason.

=====================

unit Globals;
interface
type
	HClassB = 'TClassB';	// declare a hint to the class name
implementation
end.

=====================

unit ClassB;
interface
uses
	ClassA;
type
	TClassB = class
		procedure DoSomething (sender: TClassA);
	end;
implementation
procedure TClassB.DoSomething (sender: TClassA);
begin
	// full access to TClassA
end;
end.

=====================

unit ClassA;
interface
uses
	Globals;
type
	TClassA = class
		// The interface section can't know about TClassB so we declare a
		// class name hint from Globals.pas (HClassB) and assign the paramter with it
		// using the "hint" syntax
		procedure SetValue (value: TObject[HClassB]);
	end;
implementation
// use ClassB now so hints evaluate
uses
	ClassB;
procedure TClassA.SetValue (value: TObject[HClassB]);
begin
	// behind the scenes the compiler (or preparser if FPC has one) replaces the text "TObject[HClassB]" to “TClassB" or throws an error if the identifier TClassB is not found
	value.DoSomething(self);
end;
end.

Regards,
Ryan Joseph

Reply by email

Graeme Geldenhuys
to Ryan Joseph
Ryan Joseph wrote
Sure you could put those all in file for this example but in the real world there may be dozens of TClassB subclasses which would make the unit 10,000’s of lines long and impossible to navigate.

Big units have never been a problem to navigate for me. Years ago I've implemented the "procedure list" IDE add-on for Lazarus and Maximus. I can find locations in code in one or two seconds (however fast I can type). Procedure List (Alt+G) is now installed as standard with Lazarus IDE.

Use bookmarks to jump between often used locations.

Lazarus IDE includes a feature called Code Explorer, but I find Procedure List much faster.

The other solution as mentioned would be to use $includes and still use the single unit approach (how reliable is this? I’ve never tried).

Yes, include files work very well (much better than in Delphi). Lazarus IDE knows how to handle include files very well to. So too does MSEide, but Lazarus has a few more tricks.

The Free Pascal code (compiler and RTL) uses include files extensively.

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp

Reply by email

Tony Whyman
to Ryan Joseph: I’ve cleaned up some code by declaring uses in the implementation and using generic…

Ryan,

If you want to get rid of (ugly) typecasts then maybe you should investigate the "absolute" keyword. You get a lot of examples in the LCL. For example, here's one I chose at random:

function TGtk2WidgetSet.RawImage_CreateBitmaps(const ARawImage:
TRawImage; out
   ABitmap, AMask: HBitmap; ASkipMask: boolean): boolean;
var
   GdiObject: PGDIObject absolute ABitmap;
   GdiMaskObject: PGDIObject absolute AMask;
   Desc: TRawImageDescription absolute ARawImage.Description;
....

You could describe it as typecast done in the var clause of a method. The right hand identifier is not restricted to function parameters.

Regards

Tony Whyman
MWA

Ryan Joseph wrote
I’ve cleaned up some code by declaring uses in the implementation and using generic untyped variables like TObject for parameters. This compiles but I’m left with lots of ugly type casting in the implementation because the parameters for methods are untyped. Yes it works but I feel like the compiler could be helping more.

Reply by email

Sven Barth
to Tony Whyman
"Tony Whyman" wrote
Ryan, If you want to get rid of (ugly) typecasts then maybe you should

investigate the "absolute" keyword. You get a lot of examples in the LCL. For example, here's one I chose at random:

function TGtk2WidgetSet.RawImage_CreateBitmaps(const ARawImage:

TRawImage; out

Show quoted text 9 lines
ABitmap, AMask: HBitmap; ASkipMask: boolean): boolean; var GdiObject: PGDIObject absolute ABitmap; GdiMaskObject: PGDIObject absolute AMask; Desc: TRawImageDescription absolute ARawImage.Description; .... You could describe it as typecast done in the var clause of a method. The

right hand identifier is not restricted to function parameters.

While it works using "absolute" for a public API is rather unsafe (I'd only use that in private methods). In those cases the "as" operator should he used (or at least "is" plus returning an error).

Regards,
Sven

Reply by email

Sven Barth
to Ryan Joseph: I’ve cleaned up some code by declaring uses in the implementation and using generic…
"Ryan Joseph" wrote
Does this look like more work than using $includes? Maybe I need to

seriously considering reorganizing my projects to work like this but it just feels wrong for some reason.

Show quoted text 43 lines
===================== unit Globals; interface type HClassB = 'TClassB'; // declare a hint to the class name implementation end. ===================== unit ClassB; interface uses ClassA; type TClassB = class procedure DoSomething (sender: TClassA); end; implementation procedure TClassB.DoSomething (sender: TClassA); begin // full access to TClassA end; end. ===================== unit ClassA; interface uses Globals; type TClassA = class // The interface section can't know about TClassB so we

declare a

// class name hint from Globals.pas (HClassB) and assign

the paramter with it

Show quoted text 13 lines
// using the "hint" syntax procedure SetValue (value: TObject[HClassB]); end; implementation // use ClassB now so hints evaluate uses ClassB; procedure TClassA.SetValue (value: TObject[HClassB]); begin // behind the scenes the compiler (or preparser if FPC has one)

replaces the text "TObject[HClassB]" to “TClassB" or throws an error if the identifier TClassB is not found

value.DoSomething(self); end; end.

*shudders* Before we introduce such syntax I'd prefer to get the formal class types that were added for Objective Pascal working with Object Pascal as well (which would be exactly what you want just with a more sane syntax).

But again: you're currently fighting against the language. Use interfaces and abstract classes and overthink your dependencies and design.

Regards,
Sven

Reply by email

Ryan Joseph
to Tony Whyman: If you want to get rid of (ugly) typecasts then maybe you should investigate the…
On Apr 15, 2016, at 3:46 PM, Tony Whyman wrote: You could describe it as typecast done in the var clause of a method. The right hand identifier is not restricted to function parameters.

I’m not understanding how this would work. I’ve read that the keyword (I’ve never heard about until now) makes a variable share memory so it’s like an alias I guess.

So, a common scenario is a member variable being declared as TObject then having to typecast it every time I need to access one of it’s methods. Declaring another variable in each function instead of typecasting is perhaps more work but maybe I’m not getting it.

Thanks.

Regards,
Ryan Joseph

Reply by email

Ryan Joseph
to Sven Barth: seriously considering reorganizing my projects to work like this but it just feels wrong…
On Apr 15, 2016, at 3:56 PM, Sven Barth wrote: *shudders* Before we introduce such syntax I'd prefer to get the formal class types that were added for Objective Pascal working with Object Pascal as well (which would be exactly what you want just with a more sane syntax).

I agree 100%. Putting a class definition into the namespace of another unit and importing like Objective Pascal(c) does is preferable. There doesn’t seem to be much interest in the problem though.

But again: you're currently fighting against the language. Use interfaces and abstract classes and overthink your dependencies and design.

Thanks to better usage of “uses” in the implementation I’ve learned now how to replace the abstract classes with weakly typed parameters and typecasting which is perhaps a small improvement because I don’t need to worry about extra classes and overriding etc… I’m happy with that but I’m still doing something the compiler could be helping with so I thought I’d mention it in the spirit of efficiency. :)

The only question now is if using $ifdef’s and $includes to make large single units is preferable to typecasting ugliness.

Thanks.

Regards,
Ryan Joseph

Reply by email

Dennis
to Sven Barth: investigate the "absolute" keyword. You get a lot of examples in the LCL. For example,…
Show quoted text 12 lines
> You could describe it as typecast done in the var clause of a method. The right hand identifier is not restricted to function parameters. While it works using "absolute" for a public API is rather unsafe (I'd only use that in private methods). In those cases the "as" operator should he used (or at least "is" plus returning an error). Regards, Sven

May I know why it is unsafe to use absolute on public method's parameters? Also, will there be any compiler options in which the parameters are passed through registers? In that case, will the absolute of private method's parameters still work?

Dennis

Reply by email

Tony Whyman
to Ryan Joseph: I’m not understanding how this would work. I’ve read that the keyword (I’ve never…
Ryan Joseph wrote
So, a common scenario is a member variable being declared as TObject then having to typecast it every time I need to access one of it’s methods. Declaring another variable in each function instead of typecasting is perhaps more work but maybe I’m not getting it. Thanks.

Here's an update of my previous example. In this example, "absolute" is just a way of tidying up the code when you need to make multiple references to a variable that always has to have its type coerced.

In the end though this is just a "what's in a name? A rose by any other name would smell as sweet" debate. No matter how you approach the problem, you have to have an identifier for the other class when you reference its methods and properties. If you don't want to declare two mutually referential classes in the same unit, then one of them (but only one) has to use some technique to avoid circular references. Type casting is a simple technique and you either have to coerce each use (think of TClassB(FObject) as just being a longer name for the same thing) or declare an alias of the correct type - a "With" statement can also be your friend here.

It's not that big an overhead and when it comes to type casts a 'C' programmer would wonder what all the fuss was about.

unit unitA;
interface
type
class TClassA
private
   FClassBObject: TObject;
public
   procedure SomeProc;
end;
implementation
uses unitB;
procedure TClassA.SomeProc;
var aClassBObject: TClassB absolute FClassBObject;
begin
   aClassBObject.OtherProc;
end;
end.

Reply by email

Tony Whyman
to Tony Whyman

Just remembered, FPC also supports macros - see
http://www.freepascal.org/docs-html/prog/progse5.html

I haven't tested it, but you should be able to do

{$MACRO On}
{$DEFINE aClassBObject:=TClassB(FClassBObject) }

and then use aClassBObject instead of the coercion.

Tony Whyman wrote 10 lines
In the end though this is just a "what's in a name? A rose by any other name would smell as sweet" debate. No matter how you approach the problem, you have to have an identifier for the other class when you reference its methods and properties. If you don't want to declare two mutually referential classes in the same unit, then one of them (but only one) has to use some technique to avoid circular references. Type casting is a simple technique and you either have to coerce each use (think of TClassB(FObject) as just being a longer name for the same thing) or declare an alias of the correct type - a "With" statement can also be your friend here.

Reply by email

Ryan Joseph
to Tony Whyman
Show quoted text 11 lines
On Apr 15, 2016, at 5:34 PM, Tony Whyman wrote: Just remembered, FPC also supports macros - see http://www.freepascal.org/docs-html/prog/progse5.html I haven't tested it, but you should be able to do {$MACRO On} {$DEFINE aClassBObject:=TClassB(FClassBObject) } and then use aClassBObject instead of the coercion.

I remember trying to doing pre-parser type things using macros but the think the conclusion is they don’t work by just replace text like in C.

I’m trying an example like this but getting errors and even crashing the compiler (seg faults).

Regards,
Ryan Joseph

Reply by email

Ryan Joseph
to Tony Whyman: Here's an update of my previous example. In this example, "absolute" is just a way of…
Show quoted text 26 lines
On Apr 15, 2016, at 5:13 PM, Tony Whyman wrote: unit unitA; interface type class TClassA private FClassBObject: TObject; public procedure SomeProc; end; implementation uses unitB; procedure TClassA.SomeProc; var aClassBObject: TClassB absolute FClassBObject; begin aClassBObject.OtherProc; end; end.

I see now. That’s possibly preferable so I’ll keep it in mind but like you said just sucking it up and type casting is not so bad. :) Sure enough but it would even better if the compiler was more friendly here. If there was a good pre parser in FPC I’d just make some custom syntax but that’s not an option either as far as I know.

Regards,
Ryan Joseph

Reply by email

Ryan Joseph
to Ryan Joseph: I remember trying to doing pre-parser type things using macros but the think the…
Show quoted text 5 lines
On Apr 15, 2016, at 6:26 PM, Ryan Joseph wrote: I remember trying to doing pre-parser type things using macros but the think the conclusion is they don’t work by just replace text like in C. I’m trying an example like this but getting errors and even crashing the compiler (seg faults).

Never mind! I was getting recursion causing the problems. It looks like they do work like the c preprocessor. Thanks, I’ll see if I can do something with this.

Regards,
Ryan Joseph

Reply by email

Sven Barth
to Dennis: May I know why it is unsafe to use absolute on public method's parameters? Also, will…
"Dennis" wrote
> > You could describe it as typecast done in the var clause of a method.

The right hand identifier is not restricted to function parameters.

> > While it works using "absolute" for a public API is rather unsafe (I'd

only use that in private methods). In those cases the "as" operator should he used (or at least "is" plus returning an error).

Show quoted text 6 lines
> > Regards, > Sven > > May I know why it is unsafe to use absolute on public method's parameters?

I didn't mean that in the context of the "public" section of classes, but if your API is available to third party users (that can also include other members of the same development team) then you need to make sure that the instance that is passed in is indeed a correct one as otherwise you'd access the wrong fields or methods. For this kind of mistake public and protected methods are of course the most affected ones as are routines that are declared in the interface section of a unit.

Also, will there be any compiler options in which the parameters are

passed through registers? In that case, will the absolute of private method's parameters still work?

Parameters are already often passed in registers (of course this depends on the platform). However parameters also often have a spilling locations in the stack to store the parameter if register pressure gets too high, so absolute shouldn't be a problem normally. It might also be that the usage of absolute prohibits some optimizations the compiler would otherwise do with that parameter inside the function.

Regards,
Sven

Reply by email

Sven Barth
to Ryan Joseph: I see now. That’s possibly preferable so I’ll keep it in mind but like you said just…
"Ryan Joseph" wrote
> On Apr 15, 2016, at 5:13 PM, Tony Whyman
Show quoted text 27 lines
> > unit unitA; > > interface > > type > > class TClassA > private > FClassBObject: TObject; > public > procedure SomeProc; > end; > > implementation > > uses unitB; > > procedure TClassA.SomeProc; > var aClassBObject: TClassB absolute FClassBObject; > begin > aClassBObject.OtherProc; > end; > > end. I see now. That’s possibly preferable so I’ll keep it in mind but like

you said just sucking it up and type casting is not so bad. :) Sure enough but it would even better if the compiler was more friendly here. If there was a good pre parser in FPC I’d just make some custom syntax but that’s not an option either as far as I know.

Again, you're fighting against design principles of the language. It's just as if you'd want to have virtual class methods in C++...

Regards,
Sven

Reply by email

Ryan Joseph
to Sven Barth
On Apr 15, 2016, at 7:03 PM, Sven Barth wrote: Again, you're fighting against design principles of the language. It's just as if you'd want to have virtual class methods in C++…

Agreed. Everyone has their own list of things they want their language to do I guess. Maybe I got spoiled with the typeless “id” type from Objective-C that let me have little work arounds for things like this. Not having a dictionary type after using PHP hurts also. ;)

Here’s another option I thought of. If the type casting was of a serious quantity doing this could save time and improve readability of code.

unit unitA;
interface
type
class TClassA
private
 FClassBObject: TObject;
public
 procedure SomeProc;
end;
implementation
uses unitB;
type
 TClassAHelper = class helper for TClassA
  function aClassBObject: TClassB;
 end;
function TClassAHelper.aClassBObject: TClassB;
begin
 result := TClassB(FClassBObject)
end;
procedure TClassA.SomeProc;
begin
 aClassBObject.OtherProc;
end;
end.

Regards,
Ryan Joseph

Reply by email