Sign in Join

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

Mailing lists / fpc-devel / 2009-08

Circular references or Forward Declarations

3 messages · 3 people ·

Reply by email Subscribe to this list Your subscription

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

Graeme Geldenhuys

Lance posted this in the Lazarus mailing list, and I think it is more appropriate here...

Lance Collins wrote 36 lines
> How can you avoid circular references with two modules that > interact with each other? Basically it should be possible with a minor language change and not too much effort from the compiler team. Something like: unit ParentObject; interface TChild = forward class; // My made-up syntax // or TChild = implementation class; or some other syntax! TParent = Class public property Child :TChild; end; implementation uses ChildObject; // Nothing special here, only need the love one-way // but you could do it both ways if you want unit ChildObject; interface uses ParentObject; TChild = Class public property Parent :TParent; end; "TChild = forward class" tells the compiler that TChild is a class, and the complete type information is known in the implementation section (which the compiler can validate). This feature is often needed and the "if you need to do that then something is wrong with your design" argument doesn't fly with me.

I can't agree more. Sometimes there is a valid design for something like that. The compilers already handle forward declarations in the same unit, so why can that not be applied for multiple units as well. And if the forward declaration is not fully resolved by the time the whole project or unit is compiled - raise a compiler error.

The current usage of forward declaration is exactly the same principle we are trying to use, but this time just across units. Why can't the FPC compiler maybe in ObjFPC mode cater for this?

Regards,
- Graeme -

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://opensoft.homeip.net/fpgui/

Reply by email

Jonas Maebe
to Graeme Geldenhuys
Graeme Geldenhuys wrote
I can't agree more. Sometimes there is a valid design for something like that. The compilers already handle forward declarations in the same unit,

Actually, only in the same "type" block.

so why can that not be applied for multiple units as well.

Because types with the same name in different units are different types in Pascal.

Suchfunctionality will probably be added for Objective-Pascal classes (in the form of external anonymous objcclass types, because otherwise it's very hard to make sensible automated translations of Objective-C framework headers to Pascal; single giant umbrella units don't work very well there), but there it can work because the type system of Objective-C works differently. It has one namespace for class names, which is global for the entire application because all classes have to be registered with the run time system based on their name. So in Objective-C, a class name by itself does refer to a unique type (and the compiler will give an error if you try to define a class with the same name twice in different units).

In regular Object-Pascal, class names do not have to be unique over the entire application. Letting the compiler guess which type is the external one you meant and which isn't is a recipe for disaster. It would probably also require a significant rewrite of the type handling and symbol table (even more so than what will be required for objcpas).

Jonas

Reply by email

Martin
to Graeme Geldenhuys: Lance posted this in the Lazarus mailing list, and I think it is more appropriate here...…
Graeme Geldenhuys wrote 48 lines
Lance posted this in the Lazarus mailing list, and I think it is more appropriate here... Lance Collins wrote: >> How can you avoid circular references with two modules that >> interact with each other? > Basically it should be possible with a minor language change and not too > much effort from the compiler team. Something like: > > unit ParentObject; > interface > TChild = forward class; // My made-up syntax > // or TChild = implementation class; or some other syntax! > TParent = Class > public > property Child :TChild; > end; > implementation > uses ChildObject; > > > // Nothing special here, only need the love one-way > // but you could do it both ways if you want > unit ChildObject; > interface > uses ParentObject; > TChild = Class > public > property Parent :TParent; > end; > > "TChild = forward class" tells the compiler that TChild is a class, > and the > complete type information is known in the implementation section > (which the > compiler can validate). > This feature is often needed and the "if you need to do that then > something is wrong with your design" argument doesn't fly with me. I can't agree more. Sometimes there is a valid design for something like that. The compilers already handle forward declarations in the same unit, so why can that not be applied for multiple units as well. And if the forward declaration is not fully resolved by the time the whole project or unit is compiled - raise a compiler error. The current usage of forward declaration is exactly the same principle we are trying to use, but this time just across units. Why can't the FPC compiler maybe in ObjFPC mode cater for this?

I posted a similar (probable) explanation/problem resulting from this, but it is better explained on the example above.

unit Foo;
uses ParentObject;
....
implementation
procedure Bar(parent: TParent );
begin
  parent.Child := TListBox.Create(nil);
end;

because unit ParentObject does not say in it's interface what exactly Child can be (except it must be some instance of some class (in other words any object) , the compiler can not decide if the code about is correct or not.

You either loose the strong typed structure of pascal, or the compiler has to read the implementation of ParentObject in advance, which looses the benefit of differing between interface and implementation. Because now circular references in the implementation can no longer be resolved by knowing the interfaces (as we just discovered, that interfaces became useless without implementation). This means that it will ultimately mean multiply passes in order to compile......

Martin

Reply by email