This is an *updated* version of my previous message, which I mistaken sent to gpc-doc (my apologies). (See the bits 'Addendum' for the new stuff I've added.)
Excuse my being frustrated. I am trying damn hard to get GPC (2.1 (20020510), based on gcc-2.95.3 20010315 (release)) to compile a Pascal program I wrote some time ago but keep running it things I can't make sense of. Maybe I'm being unfair, but I'll give you the whole story just in case its something new. The latest issue I'm having is a run of type vs. identifier conflict errors of the form:
./GHJ_AARegions.pas:49: type name expected, identifier `Fnames' given ./GHJ_AARegions.pas:49: warning: missing type in declaration of `Infname' ./GHJ_AARegions.pas:54: warning: parameter has incomplete type
My trouble was fnames *is* a type. It is *only* defined as a type. So how the heck does the compiler come away thinking that its an identifier?????
I eventually resolved this by directly 'using' modules, rather than rely on the hierarchy (read on).
When this error occurred the GHJ_AARegions unit uses GHJFnames. GHJFnames in turn uses GHJFiles, which is where the type fnames is defined. (Really these two levels need to be the other way around, but this is a legacy program I am porting, so I have to deal with that after the port.)
Since these uses statements were within the interface section, I presumed the definitions would propagate upwards so that they form a hierarchy. Or, put another way, I assumed the interface of a unit is extended by any units it uses in its interface section.
Originally, I had:
unit GHJ_AARegions ;
interface
uses GHJStdDefs, GHJStdMaths { for inrange() }, GHJttInterface { for press_return() }, GHJStrings { for get_string8() }, {=>} GHJFnames { for get_fname() }, GHJ_AAErrors, GHJ_AAUtils, GHJ_AAGlobalUnit ;
By getting GHJ_AARegions to explicitly, directly, use GHJFiles, the compiler stopped complaining. ie. I changed the above code to:
unit GHJ_AARegions ;
interface
uses GHJStdDefs, GHJStdMaths { for inrange() }, GHJttInterface { for press_return() }, GHJStrings { for get_string8() }, {=>} GHJFiles, GHJFnames { for get_fname() }, GHJ_AAErrors, GHJ_AAUtils, GHJ_AAGlobalUnit ;
{=>} points out the offending line of source. If this is truly the problem, then I presume there is no way of building a hierarchy of units in GPC at present? (In this case my safest strategy is to have every unit uses *all* the lower units directly, "just in case". Or put, another way, I might just as well use includes to make everything one big flat program. Sorry, but I *am* frustrated!)
Three thoughts:
1. This is the correct behaviour?: surely I should be able to use a higher-level module and have all the lower-level modules definitions within that higher-level module "gotten" at the same time without have to explicitly duck behind the scenes and drag out each module??? Excuse me for asking this, but its not terribly clear from the docs, at least on my reading of it. Addendum: I've since seen section 6.1.8.2. The source structure of UCSD/Borland units of the docs say "A units exports everything declared in the interface section". This it doesn't appear to do if we consider things in the units imported via uses.
2. Why the heck does the compiler report the message it does? "Missing type fnames" perhaps, but since fnames is only ever defined as a type and never as an identifier, it shouldn't be able to report the message it did...
3. Is there any chance you can tell the user where the "identifier" (see the error message) is defined so that in future users might have chance of trying to second-guess just what the compiler is "thinking"?
Sorry I can't provide a full code example: I am porting a largish program (approx. 60,000 lines of source in a dozen or so modules) and in any event I can't understand the problem well enough to create an example - catch-22...
Looking at the known bugs for ver. 2.1 there are a couple that might be relevant, esp."Types declared in a module interface are not visible in the implementation" (but does this refer to the same module's interface section and how are units and modules related with reference to this?)
While I'm writing, is there any chance you can make the compiler keep trying after one unit has failed, rather than stopping at that point? It seems that if there is even one error in a unit, the compilation is abandoned at the end of that unit.
Now to try figure out how GPC claims a type-mismatch on something that is definitely the same type and the type is only defined in one place... probably the same issue, we'll see... Addendum: Nope. It proves to be a unit issue, but a different one. It seems that --automake doesn't "drill down". If you modify a lower level unit, then recompile a higher-level one that uses the lower-level unit, GPC doesn't re-compile the lower-level unit first and instead comes out with (apparently) misleading error messages. --automake in my hands isn't so auto ;-) !
Grant
I've been a long time Pascal programmer on the Mac, and I'm just starting to look at ways to get the Mac OS API interfaces working with gpc to allow porting of Mac Pascal programs to gpc/Mac OS X and writing of new programs. The latter requires only hacking the Mac API files sufficiently to work with gpc, the former requires easing the burden of moving code from the traditional THINK/CodeWarrior Pascal compilers to gpc.
Some tasks are best done by changing the source files, but other tasks would be much easier done with modifications to gpc to support some of the Mac constructs (eg univ untyped pointers, external names that match exactly the procedure names, some minor things like, and some major things like binary compatible "short" strings (this is a big one), closer object modal (this is a huge amount of work, either in gpc or in porting)). I understand that there are god ideological reasons for not wanting some of these things, it's just that a lot of existing Mac Pascal code has good practical reasons for wanting these changes (personally, I have an active project with 300 Pascal files and 100,000 lines, and it would nice to be able to switch from CodeWarrior's unsupported Pascal compiler to gpc, but it's going to be a hard slog!).
So before I go to far with this, my question is, if we were to manage to modify gpc (in safe and sensible ways), and if the modifications were hidden unless some sort of compiler switch was enabled (perhaps --traditional-mac-compatible or whatever, possibly on by default when compiled on Mac OS X), would those changes be added back in to the gpc code base? Or is the policy more along the lines of "no, that's not X Y or Z standard, so it has to be patched in afterwards"?
Thanks for some meta advice on how to proceed in getting gpc accessible on Mac OS X, Peter.
Peter N Lewis wrote:
I've been a long time Pascal programmer on the Mac, and I'm just starting to look at ways to get the Mac OS API interfaces working with gpc to allow porting of Mac Pascal programs to gpc/Mac OS X and writing of new programs. The latter requires only hacking the Mac API files sufficiently to work with gpc, the former requires easing the burden of moving code from the traditional THINK/CodeWarrior Pascal compilers to gpc.
Some tasks are best done by changing the source files, but other tasks would be much easier done with modifications to gpc to support some of the Mac constructs (eg univ untyped pointers, external names that match exactly the procedure names, some minor things like, and some major things like binary compatible "short" strings (this is a big one), closer object modal (this is a huge amount of work, either in gpc or in porting)). I understand that there are god ideological reasons for not wanting some of these things, it's just that a lot of existing Mac Pascal code has good practical reasons for wanting these changes (personally, I have an active project with 300 Pascal files and 100,000 lines, and it would nice to be able to switch from CodeWarrior's unsupported Pascal compiler to gpc, but it's going to be a hard slog!).
So before I go to far with this, my question is, if we were to manage to modify gpc (in safe and sensible ways), and if the modifications were hidden unless some sort of compiler switch was enabled (perhaps --traditional-mac-compatible or whatever, possibly on by default when compiled on Mac OS X), would those changes be added back in to the gpc code base? Or is the policy more along the lines of "no, that's not X Y or Z standard, so it has to be patched in afterwards"?
Thanks for some meta advice on how to proceed in getting gpc accessible on Mac OS X, Peter.
I don't agree with Mr. Lewis views and I think the majority of Macintosh programmers will have flexible enough minds to shift to a Pascal world that is indeed a bit different but even more attractive than good-old THINK/CodeWarrior.
Regards,
Adriaan van Os
Peter N Lewis wrote:
I've been a long time Pascal programmer on the Mac, and I'm just starting to look at ways to get the Mac OS API interfaces working with gpc to allow porting of Mac Pascal programs to gpc/Mac OS X and writing of new programs. The latter requires only hacking the Mac API files sufficiently to work with gpc, the former requires easing the burden of moving code from the traditional THINK/CodeWarrior Pascal compilers to gpc.
Some tasks are best done by changing the source files, but other tasks would be much easier done with modifications to gpc to support some of the Mac constructs (eg univ untyped pointers, external names that match exactly the procedure names, some minor things like, and some major things like binary compatible "short" strings (this is a big one), closer object modal (this is a huge amount of work, either in gpc or in porting)). I understand that there are god ideological reasons for not wanting some of these things, it's just that a lot of existing Mac Pascal code has good practical reasons for wanting these changes (personally, I have an active project with 300 Pascal files and 100,000 lines, and it would nice to be able to switch from CodeWarrior's unsupported Pascal compiler to gpc, but it's going to be a hard slog!).
So before I go to far with this, my question is, if we were to manage to modify gpc (in safe and sensible ways), and if the modifications were hidden unless some sort of compiler switch was enabled (perhaps --traditional-mac-compatible or whatever, possibly on by default when compiled on Mac OS X), would those changes be added back in to the gpc code base? Or is the policy more along the lines of "no, that's not X Y or Z standard, so it has to be patched in afterwards"?
GPC does incorporate many features from non-standard dialects (mostly BP), so that's not a problem per se ...
The principle in GPC WRT dialects is that the default mode should accept all features of all dialects, and dialect options only turn off everything that doesn't belong to the given dialect. (In a few cases, there are warnings in the default mode about strange features, such as BP non-constant "typed constants" which are turned off by the dialect option.) So what you propose about the switch would be against this "policy".
Up to now, mixing the dialects has caused surpringly few problems, but there's no guarantee that this will remain so with new dialects added. Of course, any new dialect will increase the chances of conflicts ... and the complexity of the compiler. So we have to look at the features in detail.
Some tasks are best done by changing the source files, but other tasks would be much easier done with modifications to gpc to support some of the Mac constructs (eg univ untyped pointers,
I've commented on them (quite negatively ;-) recently. If I understood Adriaan's example right, they're mostly used with pointers and behave mostly like `Pointer' does in GPC/BP. (There are some subtle differences, such as Adriaan's example #3, but this is a really dangerous case, and it might be good if this doesn't work -- i.e., the programmer will be forced to check such cases and perhaps rewrite them more cleanly ...).
If that's all, the perhaps just "defining away" the `univ' will do the trick (`--cidefine=univ=' on the command line if you don't want to modify the sources). I don't like macros in general, but here perhaps they're more harmless than the alternatives ...
external names that match exactly the procedure names,
That's possible now with `asmname'. If you mean that, say, `procedure foobar; external;' and `procedure FooBar; external;' should behave differently -- well that's in direct contradiction to the Pascal standards (and other supported dialects such as BP), so I'd have severe issues with this one.
some minor things like, and some major things like binary compatible "short" strings (this is a big one),
We plan to do this for BP short strings. I don't know if these are the same (first byte: Length (limited to 255) accessible as a char `StringVar[0]', then the characters, capacity not stored). If so, yes, implementing them is wanted (but indeed quite involved).
closer object modal (this is a huge amount of work, either in gpc or in porting)).
Again, I don't know what the differences are. The OOE and Delphi models (rather close, but quite different from the existing BP model) are planned (and also much work) ...
Frank
The principle in GPC WRT dialects is that the default mode should accept all features of all dialects, and dialect options only turn off everything that doesn't belong to the given dialect. (In a few cases, there are warnings in the default mode about strange features, such as BP non-constant "typed constants" which are turned off by the dialect option.) So what you propose about the switch would be against this "policy".
Up to now, mixing the dialects has caused surpringly few problems, but there's no guarantee that this will remain so with new dialects added.
Ok, so if I understand that correctly, if there are features in CodeWarrior Pascal that can be implemented without conflicting with other dialects, that would be the sort of thing you'd want to see added. These might include:
univ (doesn't conflict as long as it is overridden by any user defined variable/type/etc)
making the "implementation begin" optional in interface only files.
What do you suggest for features that do conflict, but that are used widely in existing code?
Some tasks are best done by changing the source files, but other tasks would be much easier done with modifications to gpc to support some of the Mac constructs (eg univ untyped pointers,
I've commented on them (quite negatively ;-) recently. If I understood Adriaan's example right, they're mostly used with pointers and behave mostly like `Pointer' does in GPC/BP. (There are some subtle differences, such as Adriaan's example #3, but this is a really dangerous case, and it might be good if this doesn't work -- i.e., the programmer will be forced to check such cases and perhaps rewrite them more cleanly ...).
One of the useful places I think is for writing functions that take opaque record pointers without having to muck with casting everywhere. Eg, the interface might be:
type UserDataPtr = Pointer;
type OurData = record blah blah blah end; OurDataPtr = ^OurData
function CreateOurData( var data: univ OurDataPtr );
Then the users of the unit can use UserDataPtr and never have to worry about accidently accessing the internal records and the local unit can use the data without having to cast every input parameter.
Where it is typically used in the Mac world is for Handles (which is just a pointer to a pointer, type Handle = ^Ptr, but it's a useful type in that you can resize a Handle without breaking any references to it). So you might have:
type OurDataHandle = ^OurDataPtr;
and be able to pass it to a standard function like:
function ResizeHandle( data: univ Handle; newsize: Size ): OSError;
ResizeHandle could just have data: Pointer, but that gives less information and requires casting in ResizeHandle to access as a generic system handle. Using data: Pointer is no more or less safer than using data: univ Handle.
It is possible some of the benefits can be got using restricted records, but I don't think that works for cases where you want to write generic routines where Handle is treated something like a super class (excuse the misuse of terminology) of all Handle types.
If that's all, the perhaps just "defining away" the `univ' will do the trick (`--cidefine=univ=' on the command line if you don't want to modify the sources). I don't like macros in general, but here perhaps they're more harmless than the alternatives ...
Unfortunately, if I do this to this, then the existing code wont work since it will expect to be able to pass in arbitrary pointers. Fortunately, all uses of univ in the system interfaces are exactly "univ Ptr", and so Pointer will indeed work fine for that.
external names that match exactly the procedure names,
That's possible now with `asmname'. If you mean that, say, `procedure foobar; external;' and `procedure FooBar; external;' should behave differently -- well that's in direct contradiction to the Pascal standards (and other supported dialects such as BP), so I'd have severe issues with this one.
Yeah, I agree with the qualms on this. It would make the interfaces a heck of a lot cleaner than adding asmname for every single function. This is the sort of thing where I would use a compiler directive {$asmname-asgiven} {$asmname-lowercase} {$asmname-underscorelowercase}. It's not generally a good idea, but under certain circumstances it would simplify life, which to me sounds like a good type for a compiler directive.
some minor things like, and some major things like binary compatible "short" strings (this is a big one),
We plan to do this for BP short strings. I don't know if these are the same (first byte: Length (limited to 255) accessible as a char `StringVar[0]', then the characters, capacity not stored). If so, yes, implementing them is wanted (but indeed quite involved).
Yes, that sounds the same. I figured that would come sooner or later and was wondering whether to have a look at implementing it in gpc, but that seemed a little bit like jumping in at the deep end!
closer object modal (this is a huge amount of work, either in gpc or in porting)).
Again, I don't know what the differences are. The OOE and Delphi models (rather close, but quite different from the existing BP model) are planned (and also much work) ...
I don't know the details of how exactly they differ, but for starters in THINK/CodeWarrior:
All methods are virtual
Overriding a method requires the use of the override keyword (ie procedure method1; override;) I see GPC's docs have override listed but not what it does.
All objects are pointers, and have to be newed and are implicitly dereferenced (actually, they are either pointers or handles, but since they are implicitly dereferenced, the user doesn't need to know which). eg:
var o: MyObject; begin new(o); o.Method1( 1 );
In terms of converting existing Mac code across to GPC's object model, the best I can think of is to add "virtual" to every method, deal with the object/pointer difference by making a pointer type for each object, and then adding an explicit indirection for each use of the object. However automating that would be rather challenging I think. And alternative might be a GPC switch to emulate the Mac style objects. It's a toss up which would be more doable. Either way it needs more thought and isn't necessary to get to stage 2 (stage 1 being gpc working under Mac OS X which Adriaan already has largely done, stage 2 being to be able to use the Mac interfaces to write arbitrary Mac code and stage 3 being to enable porting of existing Mac code to gpc).
Anyway, this is probably longer than anyone wants to read, I'll go back to trying to parse the Mac Universal Interfaces into something GPC is happy with so we can at least reasonably get to stage 2 and avoid having to generate any more CodeWarrior code that is incompatible with the generally much nicer GPC language.
Thanks, Peter.
Peter N Lewis wrote:
The principle in GPC WRT dialects is that the default mode should accept all features of all dialects, and dialect options only turn off everything that doesn't belong to the given dialect. (In a few cases, there are warnings in the default mode about strange features, such as BP non-constant "typed constants" which are turned off by the dialect option.) So what you propose about the switch would be against this "policy".
Up to now, mixing the dialects has caused surpringly few problems, but there's no guarantee that this will remain so with new dialects added.
Ok, so if I understand that correctly, if there are features in CodeWarrior Pascal that can be implemented without conflicting with other dialects, that would be the sort of thing you'd want to see added.
Well, unless there are other problems with them (such as the case-sensitivity issue below).
These might include:
univ (doesn't conflict as long as it is overridden by any user defined variable/type/etc)
Overridden? I thought the syntax was `univ TypeName'. This would be a syntax extension (which I generally take with some scepticism) and couldn't be overridden. At best, `univ' could become a conditional keyword (as I suggested recently), so a user-defined identifier `Univ' would make the syntax extension unavailable.
making the "implementation begin" optional in interface only files.
You mean omitting the word `implementation'?
Should be ok, AFAICS (does anyone else see a problem?).
What do you suggest for features that do conflict, but that are used widely in existing code?
We must discuss them in detail, I can't give a general solution.
Some tasks are best done by changing the source files, but other tasks would be much easier done with modifications to gpc to support some of the Mac constructs (eg univ untyped pointers,
I've commented on them (quite negatively ;-) recently. If I understood Adriaan's example right, they're mostly used with pointers and behave mostly like `Pointer' does in GPC/BP. (There are some subtle differences, such as Adriaan's example #3, but this is a really dangerous case, and it might be good if this doesn't work -- i.e., the programmer will be forced to check such cases and perhaps rewrite them more cleanly ...).
One of the useful places I think is for writing functions that take opaque record pointers without having to muck with casting everywhere. Eg, the interface might be:
type UserDataPtr = Pointer;
type OurData = record blah blah blah end; OurDataPtr = ^OurData
function CreateOurData( var data: univ OurDataPtr );
Then the users of the unit can use UserDataPtr and never have to worry about accidently accessing the internal records and the local unit can use the data without having to cast every input parameter.
This sounds more like `restricted' in EP -- and quite unlike what Adriaan explained about `univ'. According to his explanation, one could pass any data type to CreateOurData whose size happens to match that of OurData.
Where it is typically used in the Mac world is for Handles (which is just a pointer to a pointer, type Handle = ^Ptr, but it's a useful type in that you can resize a Handle without breaking any references to it). So you might have:
type OurDataHandle = ^OurDataPtr;
and be able to pass it to a standard function like:
function ResizeHandle( data: univ Handle; newsize: Size ): OSError;
ResizeHandle could just have data: Pointer, but that gives less information and requires casting in ResizeHandle to access as a generic system handle. Using data: Pointer is no more or less safer than using data: univ Handle.
This in turn looks a bit like EP schemata, yet another completely different thing.
It is possible some of the benefits can be got using restricted records, but I don't think that works for cases where you want to write generic routines where Handle is treated something like a super class (excuse the misuse of terminology) of all Handle types.
And now we have OOP!? ;-)
I'm confused. If `univ' really does lump together all these usages (untyped pointers; restricted; schemata; kind of OOP), I'm even more sceptical, I'm afraid ...
external names that match exactly the procedure names,
That's possible now with `asmname'. If you mean that, say, `procedure foobar; external;' and `procedure FooBar; external;' should behave differently -- well that's in direct contradiction to the Pascal standards (and other supported dialects such as BP), so I'd have severe issues with this one.
Yeah, I agree with the qualms on this. It would make the interfaces a heck of a lot cleaner than adding asmname for every single function. This is the sort of thing where I would use a compiler directive {$asmname-asgiven} {$asmname-lowercase} {$asmname-underscorelowercase}. It's not generally a good idea, but under certain circumstances it would simplify life, which to me sounds like a good type for a compiler directive.
Well, if it's only (or mostly) needed in the system interfaces, converting them would be a one-time change (mostly automatic, with a script like the one I posted) whereas a compiler change would stick around, have to be maintained etc.
We plan to do this for BP short strings. I don't know if these are the same (first byte: Length (limited to 255) accessible as a char `StringVar[0]', then the characters, capacity not stored). If so, yes, implementing them is wanted (but indeed quite involved).
Yes, that sounds the same. I figured that would come sooner or later and was wondering whether to have a look at implementing it in gpc, but that seemed a little bit like jumping in at the deep end!
Yeah, not exactly the easiest thing to start with (I'm still a bit afraid of taking it on myself) ...
Frank
At 5:32 PM +0100 1/3/03, Frank Heckenbach wrote:
Peter N Lewis wrote:
univ (doesn't conflict as long as it is overridden by any user defined variable/type/etc)
Overridden? I thought the syntax was `univ TypeName'. This would be a syntax extension (which I generally take with some scepticism) and couldn't be overridden. At best, `univ' could become a conditional keyword (as I suggested recently), so a user-defined identifier `Univ' would make the syntax extension unavailable.
Yes, that's what I mean - ie, a user could have code to use the identifier univ and this extension would not harm them, and a user could have code from the mac world which uses univ and it would work for them. To me, this fits well with your desire to support all the features of all the languages in so much as they don't conflict.
making the "implementation begin" optional in interface only files.
You mean omitting the word `implementation'?
Correct. Essentially, interface only units are typically used for system interfaces (or can also be used for library interfaces where you ship binary builds of the library), and these have no implementation in the Pascal unit file, so that section is ommitted and the "end." comes at the end of the interface section. No big deal, it's easy to work around, but again, it seems to fit well with the desire.
function CreateOurData( var data: univ OurDataPtr );
This sounds more like `restricted' in EP -- and quite unlike what Adriaan explained about `univ'. According to his explanation, one could pass any data type to CreateOurData whose size happens to match that of OurData.
No, any data whose size matches OurDataPtr. Yes, I believe the definition of univ simply limits the type checking to anything that matches the binary size.
Here's a possible compromise suggestion that would possibly be safer while still providing a high degree of compatibility. Support parameters with syntax "param: univ MyTypePtr", but only where MyTypePtr is a pointer. This would then be equivalent for the external user as "param: Pointer", but for the procedure, param would be typed as if it was MyTypePtr. This would handle the bulk of the uses of univ - the only other ones I can see in code I have access to is where parameter is SInt16 or SInt32, where you want to be able to ignore the differences between signed and unsigned numbers for purposes like reading/writing from a binary file or network transfer or such. So perhaps supporting it for fixed with types like Pointers and Integers? But even just Pointer compatible types would be the bulk of them.
I'm confused. If `univ' really does lump together all these usages (untyped pointers; restricted; schemata; kind of OOP), I'm even more sceptical, I'm afraid ...
No, it is just a simply statement to the compiler "anything matching the size of this type is ok", I'm just illustrating some of the ways it is used. GPC may well have better ways for every possible use of univ (although it seems to me that specifying a particular type of pointer but allowing any type of pointer to be passed could be cleaner than just using Pointer for at least some problems), but regardless, it's a porting issue for Mac users. Probably not a stopper, just one more thing between them and getting the benefits of CW.
Yes, that sounds the same. I figured that would come sooner or later and was wondering whether to have a look at implementing it in gpc, but that seemed a little bit like jumping in at the deep end!
Yeah, not exactly the easiest thing to start with (I'm still a bit afraid of taking it on myself) ...
Yep. But at least then I'd have some credibility. For now I'll just have to see what I can manage with parsing the interfaces and then look to see what I can do to help with GPC if I can get far enough to make most things work. Peter.
Peter N Lewis wrote:
At 5:32 PM +0100 1/3/03, Frank Heckenbach wrote:
Peter N Lewis wrote:
univ (doesn't conflict as long as it is overridden by any user defined variable/type/etc)
Overridden? I thought the syntax was `univ TypeName'. This would be a syntax extension (which I generally take with some scepticism) and couldn't be overridden. At best, `univ' could become a conditional keyword (as I suggested recently), so a user-defined identifier `Univ' would make the syntax extension unavailable.
Yes, that's what I mean - ie, a user could have code to use the identifier univ and this extension would not harm them, and a user could have code from the mac world which uses univ and it would work for them. To me, this fits well with your desire to support all the features of all the languages in so much as they don't conflict.
The only use of 'univ' of which I am aware is in Per Brinch Hansens Concurrent Pascal and the associated Sequential Pascal. Here it was used to modify the parameter type in function/procedure headers, so that other things occupying the identical parameter space could be passed. The treatment was then up to the receiver. IIRC.
Peter N Lewis wrote:
At 5:32 PM +0100 1/3/03, Frank Heckenbach wrote:
Peter N Lewis wrote:
univ (doesn't conflict as long as it is overridden by any user defined variable/type/etc)
Overridden? I thought the syntax was `univ TypeName'. This would be a syntax extension (which I generally take with some scepticism) and couldn't be overridden. At best, `univ' could become a conditional keyword (as I suggested recently), so a user-defined identifier `Univ' would make the syntax extension unavailable.
Yes, that's what I mean - ie, a user could have code to use the identifier univ and this extension would not harm them, and a user could have code from the mac world which uses univ and it would work for them. To me, this fits well with your desire to support all the features of all the languages in so much as they don't conflict.
OK, syntactically it would fit.
making the "implementation begin" optional in interface only files.
You mean omitting the word `implementation'?
Correct. Essentially, interface only units are typically used for system interfaces (or can also be used for library interfaces where you ship binary builds of the library), and these have no implementation in the Pascal unit file, so that section is ommitted and the "end." comes at the end of the interface section.
I know. gpc.pas is (almost) such a case (except that it's no unit, but a module ;-).
No big deal, it's easy to work around, but again, it seems to fit well with the desire.
OK. It will be warned about in `--borland-pascal'.
I'm adding a new option (is `--mac-pascal' ok?). Now, of course, all existing non-standard features which are part of Mac Pascal (such as units) have to be marked as such in the compiler (mostly a matter of changing some constants like B_D_PASCAL in many places, a rather tedious job). I'll leave this to you (if possible, please do it shortly after the next release, because I've made many changes and probably will again after the next release, and such a "spreaded" change will likely cause conflicts if applied to diverging versions) ...
function CreateOurData( var data: univ OurDataPtr );
This sounds more like `restricted' in EP -- and quite unlike what Adriaan explained about `univ'. According to his explanation, one could pass any data type to CreateOurData whose size happens to match that of OurData.
No, any data whose size matches OurDataPtr.
Yes, that's what I meant. (I had read to quickly and thought it was `var data: univ OurData'.)
Yes, I believe the definition of univ simply limits the type checking to anything that matches the binary size.
Here's a possible compromise suggestion that would possibly be safer while still providing a high degree of compatibility. Support parameters with syntax "param: univ MyTypePtr", but only where MyTypePtr is a pointer. This would then be equivalent for the external user as "param: Pointer", but for the procedure, param would be typed as if it was MyTypePtr. This would handle the bulk of the uses of univ - the only other ones I can see in code I have access to is where parameter is SInt16 or SInt32, where you want to be able to ignore the differences between signed and unsigned numbers for purposes like reading/writing from a binary file or network transfer or such.
Well, signed and unsigned integers would be relatively harmless in many circumstances, but many other types also have the same size, such as pointers (on some platforms), short reals (on some), strings/char arrays of a certain length, some small records and sets, etc.
So perhaps supporting it for fixed with types like Pointers and Integers? But even just Pointer compatible types would be the bulk of them.
OK, I think I could rather live with that.
The implementation might be a little more effort then -- just `univ Pointer' which is mapped to (BP's) `Pointer' would be rather easy, but this way, the real pointer type has to be kept. So each pointer type needs to have a flag whether it's `univ'. When the routine is called, this flag must be checked to "relax" type checking, but not when the parameter is used within the routine, IIRC.
So the task would be (if I'm not missing something -- that's roughly the order I would do things):
- Write some test programs - Define the flag (gpc.h and utils/gpidump.pas) - Extend the syntax (this will involve defining `univ' as a conditional keyword, or if you're lucky, only a special identifier) - Check the dialect - Set the flag - Check the flag on parameter passing - Test, debug ...
I think that's feasible, and you might be able to do it if you get familiar with some of GPC's internals ...
Frank
Grant Jacobs wrote:
... snip ...
Since these uses statements were within the interface section, I presumed the definitions would propagate upwards so that they form a hierarchy. Or, put another way, I assumed the interface of a unit is extended by any units it uses in its interface section.
That would be very poor practice, since it would expose routines, types, variables in areas where one wishes to keep them inaccessible. Uses does not, and should not, mean export.
On Fri, 28 Feb 2003, CBFalconer wrote:
Grant Jacobs wrote:
.. snip ...
Since these uses statements were within the interface section, I presumed the definitions would propagate upwards so that they form a hierarchy. Or, put another way, I assumed the interface of a unit is extended by any units it uses in its interface section.
That would be very poor practice, since it would expose routines, types, variables in areas where one wishes to keep them inaccessible. Uses does not, and should not, mean export.
Not quite. given:
Unit foo; interface uses bar; implementation uses nobar; begin end.
In any user of foo, anything in the interface section of bar should be visable; unit nobar remains hidden.
Russ
Russell Whitaker wrote:
On Fri, 28 Feb 2003, CBFalconer wrote:
Grant Jacobs wrote:
.. snip ...
Since these uses statements were within the interface section, I presumed the definitions would propagate upwards so that they form a hierarchy. Or, put another way, I assumed the interface of a unit is extended by any units it uses in its interface section.
That would be very poor practice, since it would expose routines, types, variables in areas where one wishes to keep them inaccessible. Uses does not, and should not, mean export.
Not quite. given:
Unit foo; interface uses bar; implementation uses nobar; begin end.
In any user of foo, anything in the interface section of bar should be visable; unit nobar remains hidden.
Nope. Using `bar' in the interface only means its declarations are available when processing the interface declarations of `foo', not that they're re-exported.
As I said, that's quite clear in BP (which is the "standard" as far as units are concerned). With EP modules, the problem actually doesn't arise, since everything exported must be named explicitly, but GPC's `all' behaves like BP with units.
Frank
At 10:42 PM +0100 28/2/03, Frank Heckenbach wrote:
Not quite. given:
Unit foo; interface uses bar; implementation uses nobar; begin end.
In any user of foo, anything in the interface section of bar should be visable; unit nobar remains hidden.
Nope. Using `bar' in the interface only means its declarations are available when processing the interface declarations of `foo', not that they're re-exported.
In the Mac world, this is generally called "Uses Propagation" and was always on in THINK Pascal and is a compiler switch in CodeWarrior.
The best use of it is for writing a unit MacOS.p that includes all the MacOS API interface files for you in one go, rather than have to specify them each explicitly as you use them. Is there any way to get an equivalent to this in GPC?
For your own units in a normal program, it's probably not particularly useful.
Enjoy, Peter.
On 1 Mar 2003 at 16:38, Peter N Lewis wrote:
[...]
In the Mac world, this is generally called "Uses Propagation" and was always on in THINK Pascal and is a compiler switch in CodeWarrior.
The best use of it is for writing a unit MacOS.p that includes all the MacOS API interface files for you in one go, rather than have to specify them each explicitly as you use them. Is there any way to get an equivalent to this in GPC?
gpc foo.pas --automake --uses=MacOS
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~african_chief/
At 10:48 AM +0000 1/3/03, Prof A Olowofoyeku (The African Chief) wrote:
On 1 Mar 2003 at 16:38, Peter N Lewis wrote:
[...]
In the Mac world, this is generally called "Uses Propagation" and was always on in THINK Pascal and is a compiler switch in CodeWarrior.
The best use of it is for writing a unit MacOS.p that includes all the MacOS API interface files for you in one go, rather than have to specify them each explicitly as you use them. Is there any way to get an equivalent to this in GPC?
gpc foo.pas --automake --uses=MacOS
Perhaps I was unclear, either that or I'm confused. MacOS.p is just:
unit MacOS;
interface
uses MacTypes,MacMemory,Sound,Controls,and,everything,under,the,sun;
end.
So if I understand correctly, with GPC which does not have Uses Propagation, "uses MacOS" either with or without the command line would never do anything, correct?
Thanks, Peter.
On 1 Mar 2003 at 19:08, Peter N Lewis wrote:
At 10:48 AM +0000 1/3/03, Prof A Olowofoyeku (The African Chief) wrote:
On 1 Mar 2003 at 16:38, Peter N Lewis wrote: > >[...] >> In the Mac
world, this is generally called "Uses Propagation" and was >> always on in THINK Pascal and is a compiler switch in CodeWarrior. >> >> The best use of it is for writing a unit MacOS.p that includes all the >> MacOS API interface files for you in one go, rather than have to specify >> them each explicitly as you use them. Is there any way to get an >> equivalent to this in GPC? > >gpc foo.pas --automake --uses=MacOS
Perhaps I was unclear, either that or I'm confused.
You were unclear ;)
MacOS.p is just:
unit MacOS;
interface
uses MacTypes,MacMemory,Sound,Controls,and,everything,under,the,sun;
end.
So if I understand correctly, with GPC which does not have Uses Propagation, "uses MacOS" either with or without the command line would never do anything, correct?
Correct. Of course you could do "--uses" for everything used in MacOS. It would make for a long command line - but you can have that in a batch file or a make file or a script. It only needs doing once, and would take only a few seconds. Seems far easier than any alternative being proposed (including amending the compiler).
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~african_chief/
At 10:42 PM +0100 28/2/03, Frank Heckenbach wrote:
Russell Whitaker wrote:
On Fri, 28 Feb 2003, CBFalconer wrote:
Grant Jacobs wrote:
.. snip ...
Since these uses statements were within the interface section, I presumed the definitions would propagate upwards so that they form a hierarchy. Or, put another way, I assumed the interface of a unit is extended by any units it uses in its interface section.
That would be very poor practice, since it would expose routines, types, variables in areas where one wishes to keep them inaccessible. Uses does not, and should not, mean export.
Not quite. given:
Unit foo; interface uses bar; implementation uses nobar; begin end.
In any user of foo, anything in the interface section of bar should be visable; unit nobar remains hidden.
Nope. Using `bar' in the interface only means its declarations are available when processing the interface declarations of `foo', not that they're re-exported.
I'm getting this from a lot of different directions :-)
As I said, that's quite clear in BP (which is the "standard" as far as units are concerned).
With EP modules, the problem actually doesn't arise, since everything exported must be named explicitly, but GPC's `all' behaves like BP with units.
Looks I might have to swop from units to modules using export to achieve the effect I want. After putting units into everything... sigh.
Cheers,
Grant
At 10:10 AM -0800 28/2/03, Russell Whitaker wrote:
On Fri, 28 Feb 2003, CBFalconer wrote:
Grant Jacobs wrote:
.. snip ...
Since these uses statements were within the interface section, I presumed the definitions would propagate upwards so that they form a hierarchy. Or, put another way, I assumed the interface of a unit is extended by any units it uses in its interface section.
That would be very poor practice, since it would expose routines, types, variables in areas where one wishes to keep them inaccessible. Uses does not, and should not, mean export.
Not quite. given:
Unit foo; interface uses bar; implementation uses nobar; begin end.
In any user of foo, anything in the interface section of bar should be visable; unit nobar remains hidden.
Russ
Thanks, this is exactly what I meant. I need to do a little testing: it may be that --automake not "drilling down" the units or some other silly thing is confusing me. I'll get back once I'm done.
Note that its not the uses that does the export (I never meant to suggest or imply that!), but the interface. Uses only does an import. If the uses is placed in the interface, the interface of that unit gets inherited/propagated/call-it-what-you-will.
From vague memory this is how its work in MW Pascal too. Delphi/Kylix also has uses, so I presume it does the same -- ? (Delphi in a Nutshell isn't helpful here, sigh)
Grant
Grant Jacobs wrote:
At 10:10 AM -0800 28/2/03, Russell Whitaker wrote:
On Fri, 28 Feb 2003, CBFalconer wrote:
Grant Jacobs wrote:
.. snip ...
Since these uses statements were within the interface section, I presumed the definitions would propagate upwards so that they form a hierarchy. Or, put another way, I assumed the interface of a unit is extended by any units it uses in its interface section.
That would be very poor practice, since it would expose routines, types, variables in areas where one wishes to keep them inaccessible. Uses does not, and should not, mean export.
Not quite. given:
Unit foo; interface uses bar; implementation uses nobar; begin end.
In any user of foo, anything in the interface section of bar should be visable; unit nobar remains hidden.
Thanks, this is exactly what I meant. I need to do a little testing: it may be that --automake not "drilling down" the units or some other silly thing is confusing me. I'll get back once I'm done.
Note that its not the uses that does the export (I never meant to suggest or imply that!), but the interface. Uses only does an import. If the uses is placed in the interface, the interface of that unit gets inherited/propagated/call-it-what-you-will.
Let's try a little analogy:
PROCEDURE foo;
PROCEDURE bar;
PROCEDURE baz; BEGIN END; (* baz *)
BEGIN END; (* bar *)
BEGIN (* baz is totally invisible here *) END; (* foo *)
(* If the earlier bar were visibile, we couldn't *) PROCEDURE bar; BEGIN END;
BEGIN (* baz is invisible here *) (* bar is locally defined, nothing to do with foo *) END.
Now think about the name conflicts that can arise if everything is visible everywhere. Consider foo ensconced in a separate module, which in turn uses a module holding bar.
The point is to enable break up into separate compilations without causing inexplicable errors.
At 5:43 PM -0500 28/2/03, CBFalconer wrote:
Grant Jacobs wrote:
At 10:10 AM -0800 28/2/03, Russell Whitaker wrote:
On Fri, 28 Feb 2003, CBFalconer wrote:
Grant Jacobs wrote:
.. snip ...
Since these uses statements were within the interface section, I presumed the definitions would propagate upwards so that they form a hierarchy. Or, put another way, I assumed the interface of a unit is extended by any units it uses in its interface section.
That would be very poor practice, since it would expose routines, types, variables in areas where one wishes to keep them inaccessible. Uses does not, and should not, mean export.
Not quite. given:
Unit foo; interface uses bar; implementation uses nobar; begin end.
In any user of foo, anything in the interface section of bar should be visable; unit nobar remains hidden.
Thanks, this is exactly what I meant. I need to do a little testing: it may be that --automake not "drilling down" the units or some other silly thing is confusing me. I'll get back once I'm done.
Note that its not the uses that does the export (I never meant to suggest or imply that!), but the interface. Uses only does an import. If the uses is placed in the interface, the interface of that unit gets inherited/propagated/call-it-what-you-will.
Let's try a little analogy:
PROCEDURE foo;
PROCEDURE bar; PROCEDURE baz; BEGIN END; (* baz *) BEGIN END; (* bar *) BEGIN (* baz is totally invisible here *) END; (* foo *)
(* If the earlier bar were visibile, we couldn't *) PROCEDURE bar; BEGIN END;
BEGIN (* baz is invisible here *) (* bar is locally defined, nothing to do with foo *) END.
Now think about the name conflicts that can arise if everything is visible everywhere. Consider foo ensconced in a separate module, which in turn uses a module holding bar.
The point is to enable break up into separate compilations without causing inexplicable errors.
Firstly, you're "cheating" in that you're using nested procedures and I'm sure you realise units and modules aren't designed to be tied to procedures and functions so this e.g. is purely academic from my point of view. You can't create the equivalent of nested procedures with units or modules, as that would require putting the uses inside the outer procedure (i.e. tying the unit to the procedure), e.g. you can't do:
procedure foo;
uses barthingy;
begin end; { foo }
Units/modules are tied to other units/modules, not to procedures or functions, at least in the implementations I know of! (And even if you could, you'd still have the issues I discuss below).
So... while this example is interesting as its impossible to directly implement in units/modules, its not a practical problem to me. It *is* an interesting limitation of units/modules, though.
Secondly, I never meant to imply everything is visible everywhere nor that it ought to be. Obviously, what you expose depends on the application at hand. On the note of hiding/exposing, a little philosophy FYI (at the risk teaching grandmother suck eggs, so to speak):
If a unit doesn't want something to be visible to code that imports the unit, the programmer simply doesn't place that thing in the interface section of the unit. Done. To hide procs./fns. from other procs./fns. in the same implementation unit, used nesting.
The importing units can choose to import all, some or none of the things offered up for import by any unit it uses, assuming there is a mechanism to selectively import items (obviously importing nothing of something you use doesn't make sense, but it *is* conceptually an option!).
This places more control in the hands of the code using units. In the procedure-based code, higher levels have no option but to accept what is defined at lower levels. If two different units offered bar(), higher-level code could choose which unit to import bar() from. There is no way to achieve this with procedures/functions; only one bar() could be offered.
So it really is a difference in philosophy. Should the power be with the lower levels to dictate the higher levels or vice versa? Should lower levels insist only one bar() is available, or can several alternatives be offered and one chosen by the importing code? You can argue for both in different cases.
What you seem to want is for units/modules to be able to choose to dictate what units/modules can use them (this is in effect what nesting does). We could come up with a construct like 'export bar only to foothingies, specialthingies;'. [This isn't a suggestion, Frank!! :-)] Thought of in a larger scale, this blocks re-use of lower level code as you'd be forever editing import restrictions in the lower-level units. That said, there probably is the odd case where it'd be useful.
The only way to make your example work under current schemes (as I understand them) is to move the inner bar outside foo; as I was saying units/modules are not designed for nested procedures. But if you accept this you can make it work (an equally academic example back to you! :-) ):
unit innerbarthingy;
interface
procedure bar;
implementation
procedure bar;
procedure baz; begin end; { baz }
begin end; { bar }
end.
unit foothingy;
interface
{ export only foo } procedure foo; begin end;
implementation
{ get the "inner" bar } uses innerbarthingy;
procedure foo;
begin end; { foo }
end.
unit barthingy;
interface
procedure bar;
implementation
procedure bar; begin end; { bar }
end.
unit getthingies;
interface
implementation
{ get foo() from foothingy and bar() from barthingy } uses foothingy, barthingy;
end.
The only "danger" is that the importing code needs to take responsibility for importing the right things (e.g. that we don't import bar() from innerbarthingy). Which comes back to the philosophy question again. With power comes responsibility...
There are a long list of other issues tied with this, but I'd rather get back to solving my practical problems...! If you want to ruminate on your own, you could consider (a) that most modern OOP languages share this issue in various ways (b) how hierarchial naming schemes (e.g. getthingies.bar vs. getthingies.foo.bar) figure in the mix, (c) polymorphism... and, err, I've got work to do...! Its fun mulling over all this, though.
BTW, my original questions weren't "how to program" but rather how does this particular implementation (GPC) work, esp. as the docs are are a tad thin.
Cheers,
Grant
Grant Jacobs wrote:
At 5:43 PM -0500 28/2/03, CBFalconer wrote:
Let's try a little analogy:
PROCEDURE foo;
PROCEDURE bar; PROCEDURE baz; BEGIN END; (* baz *) BEGIN END; (* bar *) BEGIN (* baz is totally invisible here *) END; (* foo *)
(* If the earlier bar were visibile, we couldn't *) PROCEDURE bar; BEGIN END;
BEGIN (* baz is invisible here *) (* bar is locally defined, nothing to do with foo *) END.
Now think about the name conflicts that can arise if everything is visible everywhere. Consider foo ensconced in a separate module, which in turn uses a module holding bar.
The point is to enable break up into separate compilations without causing inexplicable errors.
Firstly, you're "cheating" in that you're using nested procedures and I'm sure you realise units and modules aren't designed to be tied to procedures and functions so this e.g. is purely academic from my point of view.
I'd say the analogy is just as (in)valid as yours with classes. ;-)
Units/modules are tied to other units/modules, not to procedures or functions, at least in the implementations I know of!
Not in those I know. They can refer to each other, but they're not tied to each other. (Might be nitpicking, but I think that's just the question at hand.)
Secondly, I never meant to imply everything is visible everywhere nor that it ought to be. Obviously, what you expose depends on the application at hand. On the note of hiding/exposing, a little philosophy FYI (at the risk teaching grandmother suck eggs, so to speak):
If a unit doesn't want something to be visible to code that imports the unit, the programmer simply doesn't place that thing in the interface section of the unit. Done.
Nope. If, say, one type from the sub-unit is needed for an interface declaration, the sub-unit must be used in the interface. This generally doesn't mean that everything from it should be re-exported.
The importing units can choose to import all, some or none of the things offered up for import by any unit it uses, assuming there is a mechanism to selectively import items (obviously importing nothing of something you use doesn't make sense, but it *is* conceptually an option!).
This places more control in the hands of the code using units.
While this is true (and possible with GPC), it's not exactly in line with your original argumentation. You complained that you have to list several units (instead of one) in the `uses' clause, now you suggest a way where you have to list many identifiers from each unit, i.e. an order of magnitude more.
In the procedure-based code, higher levels have no option but to accept what is defined at lower levels.
I think that's true *with* automatic re-exporting, not otherwise.
What you seem to want is for units/modules to be able to choose to dictate what units/modules can use them
Not at all (unless you take his analogy a little to far).
(this is in effect what nesting does). We could come up with a construct like 'export bar only to foothingies, specialthingies;'. [This isn't a suggestion, Frank!! :-)]
I hope not -- I surely wouldn't do that. ;-)
The only way to make your example work under current schemes (as I understand them) is to move the inner bar outside foo; [...]
You really seem to have misunderstood his analogy. He wasn't trying to implement nested routines as units or vice versa, just saying that the behaviour is (in some regard) similar.
Peter N Lewis wrote:
In the Mac world, this is generally called "Uses Propagation" and was always on in THINK Pascal and is a compiler switch in CodeWarrior.
The best use of it is for writing a unit MacOS.p that includes all the MacOS API interface files for you in one go, rather than have to specify them each explicitly as you use them. Is there any way to get an equivalent to this in GPC?
Not directly.
For your own units in a normal program, it's probably not particularly useful.
Well, if it's only for the interfaces, I wouldn't suggest amending the compiler for that (same as I think about the asmnames).
If the interface units are generally all used together, anyway, it would be easy to put all their contents into one unit (can be done automatically with a simple script).
Prof A Olowofoyeku (The African Chief) wrote:
A unit exports only things in it's own interface section. If you use another unit, it only allows you access to things in the interface section of that other unit - and that is all it does. Any program/unit that desire similar access has to use the other unit for itself. This behaviour is far safer IMHO to the "interface inheritance" that you propose. Like Frank, I think that interface inheritance will cause problems.
Well, I don't think that it will cause problems per se, but it might lead to confusion, especially if it's on by default. Implementing it would probably not be too hard, but I've yet to be convinced that it's really useful ...
It might of course be easy to add this type of inheritance if the unit mechanism were object oriented, but I don't know anything about the internal implementation of it.
It's not related.
Frank
At 7:25 PM +0100 1/3/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
[snip]
Not in those I know. They can refer to each other, but they're not tied to each other. (Might be nitpicking, but I think that's just the question at hand.)
Maybe the word 'tied' isn't the right choice, but you do get the *gist* of what I'm trying to say... And no I didn't mean to nit-pick (sorry if I'm coming across that way), I'm trying to be understood, which seems to be near impossible as people keep imposing their models on me instead of trying to understand my model! Grrr! But as I've said elsewhere, I think I might start giving up :-|
[snip]
If a unit doesn't want something to be visible to code that imports the unit, the programmer simply doesn't place that thing in the interface section of the unit. Done.
Nope. If, say, one type from the sub-unit is needed for an interface declaration, the sub-unit must be used in the interface. This generally doesn't mean that everything from it should be re-exported.
Sigh. I'm not sure if you're getting it. Whether not or not you use inheritance or make the higher level directly import dependencies (i.e. without inheritance), this same issue is present in both cases. Its not something that distinguishes the models. The uses at the lower level in inheritance and the uses at the higher level without inheritance faces the same issue of what to import (and in the case of inheritance, what to re-export). The difference is that the opportunity to have intermediate level units present what is needed for use of their unit is lost, so that without inheritance the higher levels are forced to maintain the dependencies of lower level units. Does that help?
By the way, to do this properly AFAIK, you need two places for uses per unit.
As I've said elsewhere and an earlier code example showed, there are two locations for uses under this approach. One in the interface section and one in the implementation section. The latter imports everything and the kitchen sink to implement its stuff, with no effect on other units. The former, if done properly, only imports (and hence propagates) just those things needed to satisfy the declaration part of the interface. Those are "coincidently" just the things that any importing unit must acquire to use the interface. By having the contents of this uses exported, importing units acquire them without have to maintain the actual location, etc., of these types, etc. But to do this you need (1) uses to work "in context" with the interface/implementation sections (2) two parallel uses, one in the interface section and one in the implementation section rather than having one uses work across the interface/implementation boundary (3) ideally a means of only importing a subset of an interface so that the users can choose not to propagate the whole of the lower-level interfaces.
By way of example, think about what happens if we shift fnames to another unit. Under what you've told me (assuming I'm reading you right), all units which need fnames are now going to have to be edited and re-compiled to import the new unit that now holds fnames. By contrast under the inherited model if fnames is exported by FileOps, units importing FileOps need not be changed; the inheritance looks after the dependencies. Only units immediately "above" where fnames is exported need to be updated. Higher levels will get the type regardless via inheritance. The higher levels are still getting exactly the same items, its just they no longer need to maintain the dependencies of the lower-level units.
If this is no clearer, lets call it quits. I could add two long-winded code examples of each model then compare them and what happens as you maintain the code, but it'd take too long for right now. And its getting a long way from my original intention of just trying to figure how units work in GPC and how I might address roughly the sort of scheme I had in mind.
The importing units can choose to import all, some or none of the things offered up for import by any unit it uses, assuming there is a mechanism to selectively import items (obviously importing nothing of something you use doesn't make sense, but it *is* conceptually an option!).
This places more control in the hands of the code using units.
While this is true (and possible with GPC), it's not exactly in line with your original argumentation. You complained that you have to list several units (instead of one) in the `uses' clause,
Meaning that I have to list all the dependent units. See above.
now you suggest a way where you have to list many identifiers from each unit, i.e. an order of magnitude more.
I take your point, but (largely!) only those that are present in the interface declarations. In principle another way of doing this would be to have interfaces export not just the procedure and function identifiers but also the types found in the procedure/function declarations. Trouble is there may be the odd constant, etc. that the unit logically wants exported for higher-level use and you'd still need a mechanism to export these. So you might not be much better off.
Bear in mind that exporting a subset is just an option and in many cases it'd be fine to export everything (ie. the lower level unit's interface doesn't have too much extraneous junk.)
Also, how is this any worse than export in modules?
[snip]
The only way to make your example work under current schemes (as I understand them) is to move the inner bar outside foo; [...]
You really seem to have misunderstood his analogy. He wasn't trying to implement nested routines as units or vice versa, just saying that the behaviour is (in some regard) similar.
I probably tried to have my cake an eat it too ;-) I did get what he was after, I believe, its just I also tried to test out his example literally "just for the hell of it" in the same post. The two are probably kind-of tangled up...
[snip]
Grant
At 3:08 PM +1300 2/3/03, Grant Jacobs wrote:
At 7:25 PM +0100 1/3/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
If a unit doesn't want something to be visible to code that imports the unit, the programmer simply doesn't place that thing in the interface section of the unit. Done.
Nope. If, say, one type from the sub-unit is needed for an interface declaration, the sub-unit must be used in the interface. This generally doesn't mean that everything from it should be re-exported.
Sigh. I'm not sure if you're getting it. Whether not or not you use inheritance or make the higher level directly import dependencies (i.e. without inheritance), this same issue is present in both cases. Its not something that distinguishes the models. The uses at the lower level in inheritance and the uses at the higher level without inheritance faces the same issue of what to import (and in the case of inheritance, what to re-export). The difference is that the opportunity to have intermediate level units present what is needed for use of their unit is lost, so that without inheritance the higher levels are forced to maintain the dependencies of lower level units. Does that help?
Perhaps this would be a useful thought experiment. Say I wrote a unit "WonderSound" to handle all sorts of Sound stuff, and it became wildly popular and was used by lots of folks, and so I decide to update it and add lots more functionality, and so I break the unit up in to more manageably parts, like WonderSoundInput and WonderSoundOutput and WonderSoundFilters and WonderSoundFiles etc. With Uses Propagation, I can include a unit like:
unit WonderSound;
interface
uses WonderSoundInput, WonderSoundOutput, WonderSoundFilters, WonderSoundFiles;
end.
and then all existing code works unmodified and future users can choose either to use only the unit they need, or just use WonderSound and get all the stuff.
For this to be useful, the units have to ensure they have non-conflicting names, as is generally easy to do if it is all an interface on to one conceptual library.
Please note I'm not advocating Uses Propagation as something that has to go in to GPC, just trying to clarify where it might be useful. There is certainly a good case to be made against uses propagation, and I think I'd rather see some sort of extension to allow this to be used where appropriate, perhaps something like a synonym for "uses" that also reexports the listed units, or perhaps something like "uses WonderSoundInput (reexport);" or perhaps in the style of "external", something like "uses WonderSoundInput; reexport;" although that would really require allowing multiple "uses" statements to allow for some that were reexported and some that were not. Just some ideas, it would require more thought to know what if anything was the best way of including this facility.
Enjoy, Peter.
Peter N Lewis wrote:
... snip ...
Perhaps this would be a useful thought experiment. Say I wrote a unit "WonderSound" to handle all sorts of Sound stuff, and it became wildly popular and was used by lots of folks, and so I decide to update it and add lots more functionality, and so I break the unit up in to more manageably parts, like WonderSoundInput and WonderSoundOutput and WonderSoundFilters and WonderSoundFiles etc. With Uses Propagation, I can include a unit like:
unit WonderSound;
interface
uses WonderSoundInput, WonderSoundOutput, WonderSoundFilters, WonderSoundFiles;
end.
and then all existing code works unmodified and future users can choose either to use only the unit they need, or just use WonderSound and get all the stuff.
Things might NOT be compatible. A provision of units, at least under Borland, is that they have (possibly empty) initialization code, which is automatically called at program initialization time. I don't think such would occur here.
If you do something like:
uses SomeUnit, AnotherUnit, ;
const int_width = 6 ;
You'll get an error of the form:
module/unit `int_width' could not be compiled
It seems the parser sees the last comma of the unit list and skips until the next identifier in searching for the next unit, instead of running into the semicolon and thinking "whoa, we're missing a unit name".
It manages to skip the const without complaining about that either (I have no unit named const, btw!)
This is kind-of low priority as I can't imagine too many people repeating this (!) and the 'module/unit' in the error message gives you enough of a hint of where to look.
Still, its "interesting" :-)
Grant
Grant Jacobs wrote:
If you do something like:
uses SomeUnit, AnotherUnit, ;
const int_width = 6 ;
You'll get an error of the form:
module/unit `int_width' could not be compiled
It seems the parser sees the last comma of the unit list and skips until the next identifier in searching for the next unit, instead of running into the semicolon and thinking "whoa, we're missing a unit name".
Yes, that's a known issue:
: the compiler does not always recover well after a parse error : <199911040915.KAA11168@@humulus.daimi.au.dk>
As I wrote the other day, a perfect solution is not possible, anyway (because at least in some cases the computer can't guess what was intended), but the heuristics can certainly be improved. If someone has any experience in parser error recovery strategies, feel free to improve it ...
Frank
I feel mean suggesting work for you, but another low priority warning/error message thing...
Any merit in a warning message (not an error) if a unit name differs from its filename? If you do something like:
Unit1:
uses GHJ_AAConsensus;
Unit2, named 'GHJ_AAConsensus.pas'
unit Consensus;
On compiling Unit1, you get a compile-time message like:
module/unit `ghj_aaconsensus2' could not be compiled
which is pretty broad!
I imagine many users would use the same name for the unit as to the file as if nothing else its easy to locate the file if they have the same name, so a warning if they differ might be useful?
It'd be a bit of a bonus really as its a "useability" thing not a bug fix.
Grant
Grant Jacobs wrote:
I feel mean suggesting work for you, but another low priority warning/error message thing...
Any merit in a warning message (not an error) if a unit name differs from its filename? If you do something like:
Unit1:
uses GHJ_AAConsensus;
Unit2, named 'GHJ_AAConsensus.pas'
unit Consensus;
On compiling Unit1, you get a compile-time message like:
module/unit `ghj_aaconsensus2' could not be compiled
which is pretty broad!
I imagine many users would use the same name for the unit as to the file as if nothing else its easy to locate the file if they have the same name, so a warning if they differ might be useful?
It'd be a bit of a bonus really as its a "useability" thing not a bug fix.
OK (`-W[no-]interface-file-name', not on by default or with `-Wall'). ("Interface" because for modules it will apply to the interface names which is where it matters, and which might be distinct from the module name. For units it's the same.)
Frank
Peter N Lewis wrote:
Perhaps this would be a useful thought experiment. Say I wrote a unit "WonderSound" to handle all sorts of Sound stuff, and it became wildly popular and was used by lots of folks, and so I decide to update it and add lots more functionality, and so I break the unit up in to more manageably parts, like WonderSoundInput and WonderSoundOutput and WonderSoundFilters and WonderSoundFiles etc. With Uses Propagation, I can include a unit like:
unit WonderSound;
interface
uses WonderSoundInput, WonderSoundOutput, WonderSoundFilters, WonderSoundFiles;
end.
and then all existing code works unmodified and future users can choose either to use only the unit they need, or just use WonderSound and get all the stuff.
This would be close to something I have in mind as "libraries" (not implemented yet, maybe BP or Delphi compatible, don't remember exactly), i.e. basically one unit/library as a "container" for several units. (Whether there may be additional declarations in a library, if it's Borland compatible, I don't know -- but at least in your example this wouldn't seem crucial.)
Please note I'm not advocating Uses Propagation as something that has to go in to GPC, just trying to clarify where it might be useful. There is certainly a good case to be made against uses propagation, and I think I'd rather see some sort of extension to allow this to be used where appropriate, perhaps something like a synonym for "uses" that also reexports the listed units, or perhaps something like "uses WonderSoundInput (reexport);" or perhaps in the style of "external", something like "uses WonderSoundInput; reexport;"
A new syntax would surely help to distinguish it from the rest. OTOH, new syntax rules always introduce complexity in the parser. If we're to design it on our own (no compatibility concerns), we should at leastt try to keep the complexity as low as possible:
- new keyword (synonym for `uses'): not if it can be avoided at all (see my other mails)
- `uses ...; reexport;': prone to parser difficulties. Reason: After the first ';', the uses statement might be finished -- or it might not be, with the new syntax. So the new syntax can potentially conflict with anything that's valid after a uses statement (which includes many possible future extensions).
- `uses ... (reexport);' seems more harmless (rather strict syntactic context, fewer chances for conflicts). It would still add a ("weak") keyword, though. ("Weak" meaning that it is well separated from identifiers, so it can in fact be parsed as an identifier, AFAICS.) Not too much of a problem, but it adds to the list of special identifiers ...
An alternative could be the use of some symbols instead of new keywords (but I don't have a good idea for this yet), or a combination of keywords, such as `uses external ...' or `uses export ...' or `export uses' etc. (just playing around with words ... ;-).
although that would really require allowing multiple "uses" statements to allow for some that were reexported and some that were not.
Which GPC does (non-standard, non-BP, but we're talking about extensions here, anyway ...)
CBFalconer wrote:
Things might NOT be compatible. A provision of units, at least under Borland, is that they have (possibly empty) initialization code, which is automatically called at program initialization time. I don't think such would occur here.
It would -- that's not the problem. Also indirectly used units are initialized (the initializers of one unit call those of the ones they use, and each identifier has a flag so it's not run twice).
Frank
On 3 Mar 2003 at 1:28, Frank Heckenbach wrote:
Peter N Lewis wrote:
Perhaps this would be a useful thought experiment. Say I wrote a unit "WonderSound" to handle all sorts of Sound stuff, and it became wildly popular and was used by lots of folks, and so I decide to update it and add lots more functionality, and so I break the unit up in to more manageably parts, like WonderSoundInput and WonderSoundOutput and WonderSoundFilters and WonderSoundFiles etc. With Uses Propagation, I can include a unit like:
unit WonderSound;
interface
uses WonderSoundInput, WonderSoundOutput, WonderSoundFilters, WonderSoundFiles;
end.
and then all existing code works unmodified and future users can choose either to use only the unit they need, or just use WonderSound and get all the stuff.
This would be close to something I have in mind as "libraries" (not implemented yet, maybe BP or Delphi compatible, don't remember exactly), i.e. basically one unit/library as a "container" for several units.
You can have that in BP/Delphi, with regard to "libraries" (i.e., DLLs). As you all know, a DLL is simply a self-contained executable that exports routines. So the analogy is not the same with units. If we are going to use the reserved word "library", I suggest that we keep it BP-compatible and restrict to dynamically linked libraries. You can still do what you suggest above of course, but I would suggest a different name for it (e.g., "propagate" or "library_unit" or whatever).
(Whether there may be additional declarations in a library, if it's Borland compatible, I don't know -- but at least in your example this wouldn't seem crucial.)
In BP/Delphi, I believe you can have additional declarations in a library (DLL).
[...]
An alternative could be the use of some symbols instead of new keywords (but I don't have a good idea for this yet), or a combination of keywords, such as `uses external ...' or `uses export ...' or `export uses' etc. (just playing around with words ... ;-).
"propagates"?
e.g.
unit foo; interface
propagates Bar, Baz, Fooz, Trook, Trog;
implementation end.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~african_chief/
Now I have gotten the legacy code to compile it won't link... I'm still poking around to see what I figure out but so far:
1. The compiler won't make .o files for units that are found in a directory other than the one you're compiling from.
I have three directories and I compile from within one of them. The units in the other two directories are located using --unit-path compiler option. Reading the docs, I'm under the impression its supposed to default to placing the .o files into the directory you compile from for all units, regardless where the units are located unless you play with the compiler options. The .gpi files *are* made correctly. (I'm using --autobuild, btw.)
Why won't it make .o files for the units in the other directories?
Doing
gpc -c ../otherdir/someunit.p
manually works fine and the resulting .o file resolves the references it should. So I can manually fix this, but the behaviour seem a little, well, odd :-)
Does this ring a bell with anyone?
2. (Less important: I suspect this will resolve itself once the above issue is dealt with.) The linker its looking for a lot of init_* references, which I presume are initialisation routines. The docs indicate that units don't need to supply an initialisation part. Does linker simply "do away" with these references, or are empty initialisation routines required?
Grant
Grant Jacobs wrote:
- The compiler won't make .o files for units that are found in a
directory other than the one you're compiling from.
I have three directories and I compile from within one of them. The units in the other two directories are located using --unit-path compiler option. Reading the docs, I'm under the impression its supposed to default to placing the .o files into the directory you compile from for all units,
Yes, I think that's how it should be (and seems to be in my test).
regardless where the units are located unless you play with the compiler options. The .gpi files *are* made correctly. (I'm using --autobuild, btw.)
Sorry, but your description is not really clear to me. Are no .o files created, or in which directories are they created, and what about the gpi files? Which compiler options did you use, and which files are located where?
- (Less important: I suspect this will resolve itself once the above
issue is dealt with.) The linker its looking for a lot of init_* references, which I presume are initialisation routines. The docs indicate that units don't need to supply an initialisation part. Does linker simply "do away" with these references, or are empty initialisation routines required?
The problem is that while compiling the interface, it is not known whether the unit has an initialisation part (or any variables that need initialization, or uses any units that have an initializer, ...). So if you wanted to convey the information whether there's one to the user of the unit, the GPI file would have to be updated later.
Even worse, with circular unit references (which I don't like, but we have to support them), things get even trickier. I could construct some example if you really care ...
AFAICS, improving it would cost much effort and gain very little (the costs of a redundant empty initalizer is almost always negligible).
Frank
At 1:22 PM +0100 3/3/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
- The compiler won't make .o files for units that are found in a
directory other than the one you're compiling from.
I have three directories and I compile from within one of them. The units in the other two directories are located using --unit-path compiler option. Reading the docs, I'm under the impression its supposed to default to placing the .o files into the directory you compile from for all units,
Yes, I think that's how it should be (and seems to be in my test).
regardless where the units are located unless you play with the compiler options. The .gpi files *are* made correctly. (I'm using --autobuild, btw.)
Sorry, but your description is not really clear to me. Are no .o files created, or in which directories are they created, and what about the gpi files? Which compiler options did you use, and which files are located where?
No .o files are made for nits which lie outside the current directory. You have to do them yourself manually, which in my case is simply a matter of 'gpc -c ../otherdir/*.p'. However, its not what I expected. I thought the compiler would locate them via --unit-path which I use on the command line, etc.
alias gpc="gpc --executable-file-name --nested-comments --unit-path='~sysadmin/work/code/LMBCodeArchive/GNUPascal/GHJLibraries:~sysadmin/work/code/LMBCodeArchive/GNUPascal/table_code_dir:.' --autobuild"
(Excuse the wraparound.) I compile from ~/sysadmin/work/code/LMBCodeArchive/GNUPascal/aa_prog_dir
The units in GHJLibraries and table_code_dir have their .gpi files made into aa_prog_dir as expected; but no .o files are made for these units. .o files are made for all the units in aa_prog_dir, but not the other two directories.
I can work around it, but it might be worth noting as something that might be tackled "one day".
- (Less important: I suspect this will resolve itself once the above
issue is dealt with.) The linker its looking for a lot of init_* references, which I presume are initialisation routines. The docs indicate that units don't need to supply an initialisation part. Does linker simply "do away" with these references, or are empty initialisation routines required?
The problem is that while compiling the interface, it is not known whether the unit has an initialisation part (or any variables that need initialization, or uses any units that have an initializer, ...). So if you wanted to convey the information whether there's one to the user of the unit, the GPI file would have to be updated later.
Even worse, with circular unit references (which I don't like, but we have to support them), things get even trickier. I could construct some example if you really care ...
AFAICS, improving it would cost much effort and gain very little (the costs of a redundant empty initalizer is almost always negligible).
Leave it. This looks more trouble than its worth.
Grant
Grant Jacobs wrote:
At 1:22 PM +0100 3/3/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
- The compiler won't make .o files for units that are found in a
directory other than the one you're compiling from.
I have three directories and I compile from within one of them. The units in the other two directories are located using --unit-path compiler option. Reading the docs, I'm under the impression its supposed to default to placing the .o files into the directory you compile from for all units,
Yes, I think that's how it should be (and seems to be in my test).
regardless where the units are located unless you play with the compiler options. The .gpi files *are* made correctly. (I'm using --autobuild, btw.)
Sorry, but your description is not really clear to me. Are no .o files created, or in which directories are they created, and what about the gpi files? Which compiler options did you use, and which files are located where?
No .o files are made for nits which lie outside the current directory. You have to do them yourself manually, which in my case is simply a matter of 'gpc -c ../otherdir/*.p'. However, its not what I expected. I thought the compiler would locate them via --unit-path which I use on the command line, etc.
alias gpc="gpc --executable-file-name --nested-comments --unit-path='~sysadmin/work/code/LMBCodeArchive/GNUPascal/GHJLibraries:~sysadmin/work/code/LMBCodeArchive/GNUPascal/table_code_dir:.' --autobuild"
(Excuse the wraparound.) I compile from ~/sysadmin/work/code/LMBCodeArchive/GNUPascal/aa_prog_dir
The units in GHJLibraries and table_code_dir have their .gpi files made into aa_prog_dir as expected; but no .o files are made for these units. .o files are made for all the units in aa_prog_dir, but not the other two directories.
I can work around it, but it might be worth noting as something that might be tackled "one day".
If really no object files are generated, this would be a rather serious bug.
Can you send me the output of a full (i.e., `--autobuild', or after removing all .gpi files) compilation with `-v' (verbose), with a preferably simple example that exhibits this bug?
BTW, if your shell behaves like Unix shells, then the above alias is probably wrong, because `~' within single quotes is not expanded, i.e. it would look for a directory actually called `~sysadmin' (rather than the home of user `sysadmin'). But I don't suppose that's the problem here since apparently the units are found.
Frank
At 12:26 AM +0100 4/3/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
At 1:22 PM +0100 3/3/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
- The compiler won't make .o files for units that are found in a
directory other than the one you're compiling from.
I have three directories and I compile from within one of them. The units in the other two directories are located using --unit-path compiler option. Reading the docs, I'm under the impression its supposed to default to placing the .o files into the directory you compile from for all units,
Yes, I think that's how it should be (and seems to be in my test).
regardless where the units are located unless you play with the compiler options. The .gpi files *are* made correctly. (I'm using --autobuild, btw.)
Sorry, but your description is not really clear to me. Are no .o files created, or in which directories are they created, and what about the gpi files? Which compiler options did you use, and which files are located where?
No .o files are made for nits which lie outside the current directory. You have to do them yourself manually, which in my case is simply a matter of 'gpc -c ../otherdir/*.p'. However, its not what I expected. I thought the compiler would locate them via --unit-path which I use on the command line, etc.
alias gpc="gpc --executable-file-name --nested-comments
--unit-path='~sysadmin/work/code/LMBCodeArchive/GNUPascal/GHJLibraries:~sysadmin/work/code/LMBCodeArchive/GNUPascal/table_code_dir:.' --autobuild"
(Excuse the wraparound.) I compile from ~/sysadmin/work/code/LMBCodeArchive/GNUPascal/aa_prog_dir
The units in GHJLibraries and table_code_dir have their .gpi files made into aa_prog_dir as expected; but no .o files are made for these units. .o files are made for all the units in aa_prog_dir, but not the other two directories.
I can work around it, but it might be worth noting as something that might be tackled "one day".
If really no object files are generated, this would be a rather serious bug.
No .o files for those units in other directories, even though their .gpi files are present. And the are not built into the other directories either. For the units in the current directory, all the .o files are made.
Can you send me the output of a full (i.e., `--autobuild', or after removing all .gpi files) compilation with `-v' (verbose), with a preferably simple example that exhibits this bug?
I'll try get onto this, but basically its just a list of linker errors; all of which are unresolved references to the routines the units in the current directory need from the units found in the other directories. Since their .o files are not present, that's not surprising! I'm not really sure what you'd learn from that.
As for removing all the .gpi files, I have to admit I'm reluctant! In a post I haven't sent to you I was going to point out that --automake (or --autobuild) in my hands doesn't work if the .gpi files aren't present. So the first time around I'm forced to compile everything by hand (or make up a make file which I haven't done yet).
This is particularly annoying on the odd occasion when I change a unit only to find that the compiler now complains about wanting to 'recompile unit XXX in the proper order' despite the fact I've only changed one unit. The only work-around I've found so far is to trash all the .o and .gpi files and start over.
Here's the contents of that post:
I was trying to fix my linking problems, when I ran into a possible reason why --automake or --autobuild isn't doing its thing for me. Its seems that if the .gpi file is not present for a unit, it won't compile that unit in, but rather stops saying that that unit could not be compiled, eg.
module/unit `ghj_aaclustergroups' could not be compiled
This means the first time around I'm force to compile the lot myself. I've sort-of ducked by this using gpc -c *.pas a few times.
Thinking about it, is this because the compiler can't guess the filename holding the unit, given that the unit name is assigned within the file?
If so, a possible extension would be to do one scan of all *.pas and *.p files at the onset and compile a table of unit/module names for each file and proceed from there? Or perhaps this is what it already does and something else is afoot?
I know you want to deal with automake later, separately from the compiler but I thought I'd mentions this while its at hand.
BTW, if your shell behaves like Unix shells, then the above alias is probably wrong, because `~' within single quotes is not expanded, i.e. it would look for a directory actually called `~sysadmin' (rather than the home of user `sysadmin'). But I don't suppose that's the problem here since apparently the units are found.
I use bash.
Grant
Grant Jacobs wrote:
If really no object files are generated, this would be a rather serious bug.
No .o files for those units in other directories, even though their .gpi files are present. And the are not built into the other directories either. For the units in the current directory, all the .o files are made.
Can you send me the output of a full (i.e., `--autobuild', or after removing all .gpi files) compilation with `-v' (verbose), with a preferably simple example that exhibits this bug?
I'll try get onto this, but basically its just a list of linker errors; all of which are unresolved references to the routines the units in the current directory need from the units found in the other directories. Since their .o files are not present, that's not surprising! I'm not really sure what you'd learn from that.
Linking is the last step. I'm interested in the messages from the compiler runs before that (which are output with `-v'). If you like, you can omit the linker messages -- the .o files should have been created by the time the linker is invoked, and if not, the error must be before that.
This is particularly annoying on the odd occasion when I change a unit only to find that the compiler now complains about wanting to 'recompile unit XXX in the proper order' despite the fact I've only changed one unit.
A message to compile *one* unit in the proper order would be confusing indeed. ;-) But I'm only aware of a message to "recompile interfaces in the right order". This is output when automake is either disabled or doesn't have enough information (see below) to figure out by itself what to compile.
Of course, when one unit's interface changed, every unit that depends on it (directly or indirectly) must be recompiled.
Here's the contents of that post:
I was trying to fix my linking problems, when I ran into a possible reason why --automake or --autobuild isn't doing its thing for me. Its seems that if the .gpi file is not present for a unit, it won't compile that unit in, but rather stops saying that that unit could not be compiled, eg.
module/unit `ghj_aaclustergroups' could not be compiled
This message appears if either the source is not found or compilation failed because of some error.
This means the first time around I'm force to compile the lot myself. I've sort-of ducked by this using gpc -c *.pas a few times.
Thinking about it, is this because the compiler can't guess the filename holding the unit, given that the unit name is assigned within the file?
If so, a possible extension would be to do one scan of all *.pas and *.p files at the onset and compile a table of unit/module names for each file and proceed from there? Or perhaps this is what it already does and something else is afoot?
Nope. What it does is to assume file name = interface name (+ suffix), unless you specify `uses ... in 'filename''. Well, this is documented (in the references under `uses', and in the `Internals' chapter).
This probably doesn't explain the problem above (it should't fail to create .o files, without giving any errors). For that I'd still need the output.
Frank
Even worse, with circular unit references (which I don't like, but we have to support them), things get even trickier. I could construct some example if you really care ...
Why do we have to support them? I thought units were already non-standard, and I'm not aware of any compiler that DOES support circular units; is there one? FreePascal does not, and Turbo Pascal does not.
===== ======= Frank D. Engel, Jr.
Modify the equilibrium of the vertically-oriented particle decelerator to result in the reestablishment of its resistance to counterproductive atmospheric penetration.
__________________________________________________ Do you Yahoo!? Yahoo! Tax Center - forms, calculators, tips, more http://taxes.yahoo.com/
Frank D. Engel, Jr. wrote:
Even worse, with circular unit references (which I don't like, but we have to support them), things get even trickier. I could construct some example if you really care ...
Why do we have to support them? I thought units were already non-standard, and I'm not aware of any compiler that DOES support circular units; is there one? FreePascal does not, and Turbo Pascal does not.
TP does, and I suppose FPC does as well. Provided that any (or all, don't remember exactly) of the relevant `uses' are in the implementation part.
Frank
On Tue, 4 Mar 2003, Frank D. Engel, Jr. wrote:
Even worse, with circular unit references (which I don't like, but we have to support them), things get even trickier. I could construct some example if you really care ...
Why do we have to support them? I thought units were already non-standard, and I'm not aware of any compiler that DOES support circular units; is there one? FreePascal does not, and Turbo Pascal does not.
User's Guide, Borland Pascal With Objects, version 7.0, page 131:
"Because units in Borland Pascal don't need to be strictly hierachi- cal, you can make circular unit references. ...see chapter 10 in the Language Guide."
Russ
At 9:00 AM -0800 4/3/03, Russell Whitaker wrote:
On Tue, 4 Mar 2003, Frank D. Engel, Jr. wrote:
Even worse, with circular unit references (which I don't like, but we have to support them), things get even trickier. I could construct some example if you really care ...
Why do we have to support them? I thought units were already non-standard, and I'm not aware of any compiler that DOES support circular units; is there one? FreePascal does not, and Turbo Pascal does not.
User's Guide, Borland Pascal With Objects, version 7.0, page 131:
"Because units in Borland Pascal don't need to be strictly hierachi- cal, you can make circular unit references. ...see chapter 10 in the Language Guide."
Somewhat ironically for someone who has been talking about hierarchial unit models, I've run into this circular reference thing a few times in the code I'm porting. I ended up creating separate sub-units to resolve the issue.
Grant
Grant Jacobs wrote:
At 9:00 AM -0800 4/3/03, Russell Whitaker wrote:
On Tue, 4 Mar 2003, Frank D. Engel, Jr. wrote:
Even worse, with circular unit references (which I don't like, but we have to support them), things get even trickier. I could construct some example if you really care ...
Why do we have to support them? I thought units were already non-standard, and I'm not aware of any compiler that DOES support circular units; is there one? FreePascal does not, and Turbo Pascal does not.
User's Guide, Borland Pascal With Objects, version 7.0, page 131:
"Because units in Borland Pascal don't need to be strictly hierachi- cal, you can make circular unit references. ...see chapter 10 in the Language Guide."
Somewhat ironically for someone who has been talking about hierarchial unit models, I've run into this circular reference thing a few times in the code I'm porting. I ended up creating separate sub-units to resolve the issue.
Sorry to be annoying again, but it was you who talked about hierarchical unit models:
: I eventually resolved this by directly 'using' modules, rather than : rely on the hierarchy
[the hierarchy which unit inheritance provides]
: Since these uses statements were within the interface section, I : presumed the definitions would propagate upwards so that they form a : hierarchy.
: {=>} points out the offending line of source. If this is truly the : problem, then I presume there is no way of building a hierarchy of : units in GPC at present?
So it doesn't appear ironic at all -- with unit inheritance you get a strict hierarchy indeed. With the BP model, not necessarily.
Frank
At 2:17 AM +0100 5/3/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
Somewhat ironically for someone who has been talking about hierarchial unit models, I've run into this circular reference thing a few times in the code I'm porting. I ended up creating separate sub-units to resolve the issue.
Sorry to be annoying again, but it was you who talked about hierarchical unit models:
I know that! - I talking about myself, not you!!
: Since these uses statements were within the interface section, I : presumed the definitions would propagate upwards so that they form a : hierarchy.
: {=>} points out the offending line of source. If this is truly the : problem, then I presume there is no way of building a hierarchy of : units in GPC at present?
So it doesn't appear ironic at all -- with unit inheritance you get a strict hierarchy indeed. With the BP model, not necessarily.
I realise, but this is not really where the issue comes from in the code I'm referring to. (Its a porting issue due in part due to mixing units and routines previously prefixed with [global] from Vax/DEC Pascal).
Grant
Grant Jacobs wrote:
At 2:17 AM +0100 5/3/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
Somewhat ironically for someone who has been talking about hierarchial unit models, I've run into this circular reference thing a few times in the code I'm porting. I ended up creating separate sub-units to resolve the issue.
Sorry to be annoying again, but it was you who talked about hierarchical unit models:
I know that! - I talking about myself, not you!!
I see, sorry.
BTW, though I don't like the "unit inheritance", I don't like circulare references either -- IMHO, if two units require each other, they actually form one "unit" (in the non-technical meaning of the word), and splitting them into two files seems quite pointless. It might be a way to have different users get different parts only, but for that, a module with several interfaces seems cleaner and more flexible (interfaces can overlap, e.g.) ...
Frank
Consider that it might be nice, though, to split those into separate units for a different reason: the file might be very large.
For example, what if I create an opaque type, say "Color", and another opaque type, say "ColorSystem". For simplified management, I may choose to place each of these in a different source file, even though they should obviously depend on resources in each other's domain. The possibilities for correcting this would be to take those "common" resources and place them in some third file (messy), to use circular references (undesirable), or the following:
1. Pick the most convenient unit (A) to be used by the other (B). 2. Place all variables that must be shared between these units into A. 3. For every function in A that must be accessable by B, create a function pointer type in A, along with a interface-level variable of that type. 4. In the initialization part of B, assign the functions' addresses to those variables.
A bit messy at first read, but I've become used to it...
--- Frank Heckenbach frank@g-n-u.de wrote:
Grant Jacobs wrote:
At 2:17 AM +0100 5/3/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
Somewhat ironically for someone who has been talking about hierarchial unit models, I've run into this circular reference
thing
a few times in the code I'm porting. I ended up creating
separate
sub-units to resolve the issue.
Sorry to be annoying again, but it was you who talked about hierarchical unit models:
I know that! - I talking about myself, not you!!
I see, sorry.
BTW, though I don't like the "unit inheritance", I don't like circulare references either -- IMHO, if two units require each other, they actually form one "unit" (in the non-technical meaning of the word), and splitting them into two files seems quite pointless. It might be a way to have different users get different parts only, but for that, a module with several interfaces seems cleaner and more flexible (interfaces can overlap, e.g.) ...
Frank
-- Frank Heckenbach, frank@g-n-u.de, http://fjf.gnu.de/, 7977168E GPC To-Do list, latest features, fixed bugs: http://www.gnu-pascal.de/todo.html GPC download signing key: 51FF C1F0 1A77 C6C2 4482 4DDC 117A 9773 7F88 1707
===== ======= Frank D. Engel, Jr.
Modify the equilibrium of the vertically-oriented particle decelerator to result in the reestablishment of its resistance to counterproductive atmospheric penetration.
__________________________________________________ Do you Yahoo!? Yahoo! Tax Center - forms, calculators, tips, more http://taxes.yahoo.com/
At 3:54 PM -0800 5/3/03, Frank D. Engel, Jr. wrote:
Consider that it might be nice, though, to split those into separate units for a different reason: the file might be very large.
Basically, this is the example Peter used earlier.
For example, what if I create an opaque type, say "Color", and another opaque type, say "ColorSystem". For simplified management, I may choose to place each of these in a different source file, even though they should obviously depend on resources in each other's domain. The possibilities for correcting this would be to take those "common" resources and place them in some third file (messy),
This is in broad essence what I've done (I'm in a bit of a hurry; I just want to get an initial port the *work* at all to start with!). Basically, for each module/unit XXX with circ. refs. I've made a XXXUtils module/unit which holds the stuff which can be used by other modules/units, although I've done without the pointers you mention below as I don't need them.
to use circular references (undesirable), or the following:
- Pick the most convenient unit (A) to be used by the other (B).
- Place all variables that must be shared between these units into A.
- For every function in A that must be accessable by B, create a
function pointer type in A, along with a interface-level variable of that type. 4. In the initialization part of B, assign the functions' addresses to those variables.
A bit messy at first read, but I've become used to it...
For the global error message system on the other hand I made an error function handler which receives each unit's error function as a parameter. But that's a bit of a unique case and its partly to handle porting code with more than one error function in different units.
Grant
Frank D. Engel, Jr. wrote:
Consider that it might be nice, though, to split those into separate units for a different reason: the file might be very large.
For example, what if I create an opaque type, say "Color", and another opaque type, say "ColorSystem". For simplified management, I may choose to place each of these in a different source file, even though they should obviously depend on resources in each other's domain.
I don't see why it simiplifies management, but if you think so, why not simply use include files within one unit?
Separate compilation can't be the issue, since with circular references, a change in one unit requires reocmpilation of all.
Frank
Prof A Olowofoyeku (The African Chief) wrote:
On 3 Mar 2003 at 1:28, Frank Heckenbach wrote:
This would be close to something I have in mind as "libraries" (not implemented yet, maybe BP or Delphi compatible, don't remember exactly), i.e. basically one unit/library as a "container" for several units.
You can have that in BP/Delphi, with regard to "libraries" (i.e., DLLs). As you all know, a DLL is simply a self-contained executable that exports routines. So the analogy is not the same with units. If we are going to use the reserved word "library", I suggest that we keep it BP-compatible and restrict to dynamically linked libraries.
As you know, in GCC, the difference between static and dynamic libraries is very small. You need some options to create a dynamic lib, and that's all. (When using them, you can force linking static ones with `-static', but otherwise the linker will find dynamic or static ones automatically.)
So if we implement a syntax for `libarary', it should not have any relevance to whether the object code is linked statically or dynamically (or perhaps even as a set of object files).
So I was talking more about the syntactical and semantical aspects of `library' in BP/Delphi, not the implementation details (which includes how to link them).
[...]
An alternative could be the use of some symbols instead of new keywords (but I don't have a good idea for this yet), or a combination of keywords, such as `uses external ...' or `uses export ...' or `export uses' etc. (just playing around with words ... ;-).
"propagates"?
"instead of new keywords"!?
Frank
On 3 Mar 2003 at 13:14, Frank Heckenbach wrote:
[...]
As you know, in GCC, the difference between static and dynamic libraries is very small. You need some options to create a dynamic lib, and that's all. (When using them, you can force linking static ones with `-static', but otherwise the linker will find dynamic or static ones automatically.)
So if we implement a syntax for `libarary', it should not have any relevance to whether the object code is linked statically or dynamically (or perhaps even as a set of object files).
No, it shouldn't. But it should mean "generate a DLL (Windows) or a .so (Unix), as opposed to a .o". In Windows, there would have to be linking as well, in order to produce the DLL. But I guess GPC can just assume that "library foo" means "GPC -shared foo ...." and let the system deal with the rest as appropriate. It is then up to the programmer (platform dependent) what to do with the generated library.
So I was talking more about the syntactical and semantical aspects of `library' in BP/Delphi, not the implementation details (which includes how to link them).
Ok.
[...]
An alternative could be the use of some symbols instead of new keywords (but I don't have a good idea for this yet), or a combination of keywords, such as `uses external ...' or `uses export ...' or `export uses' etc. (just playing around with words ... ;-).
"propagates"?
"instead of new keywords"!?
No.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~african_chief/
Prof A Olowofoyeku (The African Chief) wrote:
On 3 Mar 2003 at 13:14, Frank Heckenbach wrote:
[...]
As you know, in GCC, the difference between static and dynamic libraries is very small. You need some options to create a dynamic lib, and that's all. (When using them, you can force linking static ones with `-static', but otherwise the linker will find dynamic or static ones automatically.)
So if we implement a syntax for `libarary', it should not have any relevance to whether the object code is linked statically or dynamically (or perhaps even as a set of object files).
No, it shouldn't. But it should mean "generate a DLL (Windows) or a .so (Unix), as opposed to a .o".
Uhm, well, that's not exactly what I meant with "should *not*". Whether a static or a shared library is built should not affect the source code.
In fact, under Unix it's common for libraries to build both static and shared versions (and I think the same can be done under Windows), and of course, there should be no change required in the source code (just some different options used etc.).
In Windows, there would have to be linking as well, in order to produce the DLL. But I guess GPC can just assume that "library foo" means "GPC -shared foo ...." and let the system deal with the rest as appropriate.
It takes more. On many platforms (including IA32) code must be compiled with `-fPIC' to work reliably in shared libs. A `-Wl,-soname=...' option for linking is often useful, etc.
All this should not be the job of the compiler, but of the make system. I won't add it to automake anymore (since it's going to vanish, anyway), so it would be something for gp or another utility. That's in the future ...
Currently, we can discuss the syntax aspects, which might be close to what Peter wants to achieve with unit inheritance. Even if no linking provisions are made now, the "library" can be linked just as a set of object files. Later it can be made into a real libraray, without changing the Pascal syntax.
So I was talking more about the syntactical and semantical aspects of `library' in BP/Delphi, not the implementation details (which includes how to link them).
Ok.
So what are they like?
Frank
On 3 Mar 2003 at 23:14, Frank Heckenbach wrote:
[...]
No, it shouldn't. But it should mean "generate a DLL (Windows) or a .so (Unix), as opposed to a .o".
Uhm, well, that's not exactly what I meant with "should *not*". Whether a static or a shared library is built should not affect the source code.
I think we are on very different wave lengths here. But I won't press the issue.
In fact, under Unix it's common for libraries to build both static and shared versions (and I think the same can be done under Windows), and of course, there should be no change required in the source code (just some different options used etc.).
"library foo;" under BP/Delphi always means a DLL. But I am getting confused now about what we are really talking about ;(
So I was talking more about the syntactical and semantical aspects of `library' in BP/Delphi, not the implementation details (which includes how to link them).
Ok.
So what are they like?
I presume you are talking about the BP/Delphi syntax for "libraries". Here is an example:
library foobar; uses bar, baz;
function bar1 : integer; stdcall; begin { blah, blah } end;
function foo : integer; stdcall; begin { blah, blah } end;
exports bar1 index 1 name 'myBar'; {IIRC, "index n" and "name x" are optional} foo index 2 name 'foo';
begin { optional } [...] { optional } end.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~african_chief/
Prof A Olowofoyeku (The African Chief) wrote:
On 3 Mar 2003 at 23:14, Frank Heckenbach wrote:
[...]
No, it shouldn't. But it should mean "generate a DLL (Windows) or a .so (Unix), as opposed to a .o".
Uhm, well, that's not exactly what I meant with "should *not*". Whether a static or a shared library is built should not affect the source code.
I think we are on very different wave lengths here. But I won't press the issue.
In fact, under Unix it's common for libraries to build both static and shared versions (and I think the same can be done under Windows), and of course, there should be no change required in the source code (just some different options used etc.).
"library foo;" under BP/Delphi always means a DLL. But I am getting confused now about what we are really talking about ;(
And this confusion is probably caused by Borland because they don't properly separate the language definition and implementation details. (I had a look in my BP manuals about units the other day, and noticed the same ...)
AFAIK, under "plain" Windows (i.e. without Cygin/Mingw or similar) there are no static libs (.a), so DLLs are the only kind of libraries (correct me if I'm wrong), and a BP/Delphi library naturally creates a DLL.
But that's an implementation detail and should not be considered part of the language definition.
For an example about units -- the BP manuals state something like the compiled unit is placed in a file whose name consists of the first 8 letters of the unit name (maybe they even said, converted to upper case) plus the extension `.TPU' (in real mode). Surely, everyone who is familiar with the issues would see that the former is a property of the FAT file system, and would not expect a BP compatible compiler to truncate unit file names after 8 characters on systems that allow longer file names. (And even the extension `.TPU' is certainly implementation-specific; GPC and FPC (and probably also other more or less BP compatible compilers) use different names.)
Same here -- we do have two ways of building libraries (*), so we should support them, of course.
(*) On most systems. I'm not completely sure, but I think, e.g., DJGPP doesn't support shared libs (yet). That, of course, means that Pascal libraries can only create static libs (or a set of object files) there ...
So I was talking more about the syntactical and semantical aspects of `library' in BP/Delphi, not the implementation details (which includes how to link them).
Ok.
So what are they like?
I presume you are talking about the BP/Delphi syntax for "libraries". Here is an example:
library foobar; uses bar, baz;
function bar1 : integer; stdcall; begin { blah, blah } end;
function foo : integer; stdcall; begin { blah, blah } end;
exports bar1 index 1 name 'myBar'; {IIRC, "index n" and "name x" are optional} foo index 2 name 'foo';
begin { optional } [...] { optional } end.
Do you have to list all exports explicitly? I.e., they don't re-export everything from the units used automatically? Well then, it actually doesn't seem to have much to do with unit inheritance as I thought ...
Frank
On 5 Mar 2003 at 2:30, Frank Heckenbach wrote:
[...]
AFAIK, under "plain" Windows (i.e. without Cygin/Mingw or similar) there are no static libs (.a), so DLLs are the only kind of libraries (correct me if I'm wrong),
There are static libs - ".lib" (I believe in a standard Intel format). MS tools use this format, and so does Virtual Pascal for unit implementation sections (VP uses the .lib format in cases where BP/Delphi would use .tpu/.dcu and GPC would use .o). Someone on the Mingw list said that the Mingw ".a" files are in the same format as Microsoft's ".lib" - but I wouldn't know for sure.
I guess that, theoretically, you can link VP .libs with MS stuff and vice versa, and someone said you can (theoretically) link MS .libs into Mingw programs, and vice versa.
and a BP/Delphi library naturally creates a DLL.
Yes - and so does VP.
But that's an implementation detail and should not be considered part of the language definition.
For an example about units -- the BP manuals state something like the compiled unit is placed in a file whose name consists of the first 8 letters of the unit name (maybe they even said, converted to upper case) plus the extension `.TPU' (in real mode). Surely, everyone who is familiar with the issues would see that the former is a property of the FAT file system, and would not expect a BP compatible compiler to truncate unit file names after 8 characters on systems that allow longer file names.
True. There is no restriction as to the length of the names in Delphi or VP.
(And even the extension `.TPU' is certainly implementation-specific; GPC and FPC (and probably also other more or less BP compatible compilers) use different names.)
True again. Delphi uses .dcu.
[...]
I presume you are talking about the BP/Delphi syntax for "libraries". Here is an example:
library foobar; uses bar, baz;
function bar1 : integer; stdcall; begin { blah, blah } end;
function foo : integer; stdcall; begin { blah, blah } end;
exports bar1 index 1 name 'myBar'; {IIRC, "index n" and "name x" are optional} foo index 2 name 'foo';
begin { optional } [...] { optional } end.
Do you have to list all exports explicitly? I.e., they don't re-export everything from the units used automatically?
I believe so - these snippets are from the Delphi 7 help file:
"Libraries can be built from multiple units. In this case, the library source file is frequently reduced to a uses clause, an exports clause, and the initialization code."
"You can put exports clauses in the interface or implementation section of a unit. Any library that includes such a unit in its uses clause automatically exports the routines listed the unit's exports clauses-- without the need for an exports clause of its own."
"An exports clause can appear anywhere and any number of times in the declaration part of a program or library, or in the interface or implementation section of a unit. Programs seldom contain an exports clause."
BTW: the Delphi 7 help file also discourages the use of "index". This is a new one (at least, to me): "Use of index specifiers, which are supported for backward compatibility only, is discouraged and may cause problems for other development tools."
Well then, it actually doesn't seem to have much to do with unit inheritance as I thought ...
Well, it can - sort of - if you have exports clauses in the units themselves. I also came across something:
"Type Switch Syntax {$ObjExportAll On} or {$ObjExportAll Off} Default {$ObjExportAll Off} Scope Global The {$ObjExportAll On} directive exports all symbols in the unit file in which it occurs. This allows the C++ compiler to create packages containing Delphi-generated object files."
Might this be a way to avoid having to declare exports clauses in units in order to simplify unit inheritance in libraries? This might of course be very difficult to implement, but it seems a clear way out of all the problems we have been discussing;
* a library that tries to propagate other units would need to have an exports section listing each thing that it wants to propagate (meaning no accidental propagation)
* but, where you want to propagate everything from a particular unit (i.e., from its interface section), you either have an exports clause in that unit, or you use the (new) directive "{$ObjExportAll On}" in that unit. The latter is obviously the more convenient for the programmer. So any library that uses such unit does not need to have its own exports clause. But this also has the advantage that the programmer would have somewhere taken a deliberate decision as to what should be propagated from his/her units.
I have to say that, for me, none of this is a priority - but it seems that others want it.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~african_chief/
At 5:47 AM +0000 5/3/03, Prof A Olowofoyeku (The African Chief) wrote:
- but, where you want to propagate everything from a particular unit
(i.e., from its interface section), you either have an exports clause in that unit, or you use the (new) directive "{$ObjExportAll On}" in that unit. The latter is obviously the more convenient for the programmer. So any library that uses such unit does not need to have its own exports clause. But this also has the advantage that the programmer would have somewhere taken a deliberate decision as to what should be propagated from his/her units.
This is pretty much what I was going to suggestion in that post I've *still* yet to send. Lets hope I find time soon... there is a little more to it as how the interface sections behave in this scheme isn't quite the same as under the units we have at present.
Grant
Prof A Olowofoyeku (The African Chief) wrote:
I believe so - these snippets are from the Delphi 7 help file:
"Libraries can be built from multiple units. In this case, the library source file is frequently reduced to a uses clause, an exports clause, and the initialization code."
"You can put exports clauses in the interface or implementation section of a unit. Any library that includes such a unit in its uses clause automatically exports the routines listed the unit's exports clauses-- without the need for an exports clause of its own."
It gets even wierder ... :-(
BTW: the Delphi 7 help file also discourages the use of "index". This is a new one (at least, to me): "Use of index specifiers, which are supported for backward compatibility only, is discouraged and may cause problems for other development tools."
At least. :-)
Well, it can - sort of - if you have exports clauses in the units themselves. I also came across something:
"Type Switch Syntax {$ObjExportAll On} or {$ObjExportAll Off} Default {$ObjExportAll Off} Scope Global The {$ObjExportAll On} directive exports all symbols in the unit file in which it occurs. This allows the C++ compiler to create packages containing Delphi-generated object files."
Seems also very strange. At least in BP it's quite clear that interface declarations are exported and implementation declarations are not. I don't see any reason for deviating from this. If you want to export something, just write it in the interface.
Well, either I'm overlooking quite a few things, or they just like to make things unnecessarily complicated for no good reason.
I think, unless someone brings up new arguments to convince me otherwise, I'll forget about this and will implement none of it.
The most I could imagine doing would be an explicit re-export of (selected) imported interfaces (though we'd still have to find a syntax for it), or a "library" that automatically re-exports all its interface imports (leaving aside the linking issues for now).
Frank
On 12 Mar 2003 at 1:13, Frank Heckenbach wrote:
[...]
"Libraries can be built from multiple units. In this case, the library source file is frequently reduced to a uses clause, an exports clause, and the initialization code."
"You can put exports clauses in the interface or implementation section of a unit. Any library that includes such a unit in its uses clause automatically exports the routines listed the unit's exports clauses-- without the need for an exports clause of its own."
It gets even wierder ... :-(
This is because of the nature of Windows "libraries" (i.e., DLLs), which are nothing more than special executables that export routines. A "library" under unix is not an executable of any kind - as far as I am aware.
[...]
Well, it can - sort of - if you have exports clauses in the units themselves. I also came across something:
"Type Switch Syntax {$ObjExportAll On} or {$ObjExportAll Off} Default {$ObjExportAll Off} Scope Global The {$ObjExportAll On} directive exports all symbols in the unit file in which it occurs. This allows the C++ compiler to create packages containing Delphi-generated object files."
Seems also very strange. At least in BP it's quite clear that interface declarations are exported and implementation declarations are not. I don't see any reason for deviating from this. If you want to export something, just write it in the interface.
That is always the case for units. What we are talking about here is exporting routines from a unit via a "library". The problem is the fact that a "library" (i.e., DLL) needs to have an "exports" clause in order to allow the programmer to specify precisely what he wants the DLL to export. This is the standard method of doing it. The above attempts to provide ways of allowing programmers to not have that exports clause in the DLL source itself - which means they need to specify what to export somewhere else - so a DLL source can then be as simple as this;
library foo; uses bar1, bar2, bar3, bar4; begin end.
Without the above switch (or without using exports clauses) in the bar(n) units, we couldn't have the above - but we would rather need something like this;
library foo; uses bar1, bar2, bar3, bar4; exports bar01 name 'myBar1', bar02 name 'myBar2', bar03 name 'myBar3', bar04 name 'myBar4'; begin end.
Well, either I'm overlooking quite a few things, or they just like to make things unnecessarily complicated for no good reason.
I think that we've got the wires crossed somewhere ;-). What they are doing is trying to find ways round the principle that a library must have an exports clause that states what is to be exported from it. They could, of course, simply have revised that principle itself - but I am a subscriber to the view that the programmer must specify for himself (somewhere) what the library should export.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~african_chief/
Prof A Olowofoyeku (The African Chief) wrote:
On 12 Mar 2003 at 1:13, Frank Heckenbach wrote:
[...]
"Libraries can be built from multiple units. In this case, the library source file is frequently reduced to a uses clause, an exports clause, and the initialization code."
"You can put exports clauses in the interface or implementation section of a unit. Any library that includes such a unit in its uses clause automatically exports the routines listed the unit's exports clauses-- without the need for an exports clause of its own."
It gets even wierder ... :-(
This is because of the nature of Windows "libraries" (i.e., DLLs), which are nothing more than special executables that export routines. A "library" under unix is not an executable of any kind - as far as I am aware.
In fact, (some?) ELF shared libs are executabled which can be executed for special purposes (ldd etc.). But usually they're not executed on their own, but linked into other processes.
I don't know the technical details under Windows, but that's irrelevant. The end result is that they contain some routines etc. (which have names) and which are made available to programs that use them. The same applies to shared libs under Unix and even to static libs. The difference, whether they're copied (in the exectuables and/or in memory) or shared has no bearing on the program's behaviour (leaving aside obscure things like self-modifying code or code that relies on specific addresses of routines).
It's irrelevant because we're talking about program language syntax here, not low-level details such as how dynamic linking works. I know that many Dos/Windows compilers mix them up (also BP does), but please try to keep them apart, otherwise we'll get nowhere.
[...]
Well, it can - sort of - if you have exports clauses in the units themselves. I also came across something:
"Type Switch Syntax {$ObjExportAll On} or {$ObjExportAll Off} Default {$ObjExportAll Off} Scope Global The {$ObjExportAll On} directive exports all symbols in the unit file in which it occurs. This allows the C++ compiler to create packages containing Delphi-generated object files."
Seems also very strange. At least in BP it's quite clear that interface declarations are exported and implementation declarations are not. I don't see any reason for deviating from this. If you want to export something, just write it in the interface.
That is always the case for units. What we are talking about here is exporting routines from a unit via a "library". The problem is the fact that a "library" (i.e., DLL) needs to have an "exports" clause in order to allow the programmer to specify precisely what he wants the DLL to export. This is the standard method of doing it. The above attempts to provide ways of allowing programmers to not have that exports clause in the DLL source itself - which means they need to specify what to export somewhere else - so a DLL source can then be as simple as this;
library foo; uses bar1, bar2, bar3, bar4; begin end.
Without the above switch (or without using exports clauses) in the bar(n) units, we couldn't have the above - but we would rather need something like this;
library foo; uses bar1, bar2, bar3, bar4; exports bar01 name 'myBar1', bar02 name 'myBar2', bar03 name 'myBar3', bar04 name 'myBar4'; begin end.
Well, either I'm overlooking quite a few things, or they just like to make things unnecessarily complicated for no good reason.
I think that we've got the wires crossed somewhere ;-). What they are doing is trying to find ways round the principle that a library must have an exports clause that states what is to be exported from it. They could, of course, simply have revised that principle itself - but I am a subscriber to the view that the programmer must specify for himself (somewhere) what the library should export.
Yes, so either with explicit export statements as in the latter example, or by re-exporting everything imported via `uses' (in a library, this would seem a useful default to me -- which is why I got to libraries in this thread originally).
What seems strange is (a) that units decide on behalf of the library what to export (what if a unit authors write some export statement, thinking of a particular library, and someone else wants to use the unit in another library and not export the stuff), and (b) that via that switch, things in an implementation part can be exported.
But maybe there's another confusion going on here -- the confusionn between exporting and providing linker names ("non-static functions" in C). In GPC, implementation routines by default get no linker name, but it's possible to specify one (with `asmname', subject to the syntax changes) -- though I'm not really sure if that's a good feature ... But that doesn't mean they're exported.
Frank
As far as that goes, why not simply add on a modifier to the unit itself, as in:
SHARED UNIT name;
INTERFACE
[...]
IMPLEMENTATION
[...]
END.
That way, programs and other units would still work the same way, and would not need to specify this specially (you could still do something strange like 'USES SHARED name, STATIC name'; or for anti-adding-keyword people, a good idea really, 'USES INTERFACE name, USES IMPLEMENTATION name', or sim.)
that "library foo" means "GPC -shared foo ...." and let the system
An alternative could be the use of some symbols instead of new keywords (but I don't have a good idea for this yet), or a combination of keywords, such as `uses external ...' or `uses
===== ======= Frank D. Engel, Jr.
Modify the equilibrium of the vertically-oriented particle decelerator to result in the reestablishment of its resistance to counterproductive atmospheric penetration.
__________________________________________________ Do you Yahoo!? Yahoo! Tax Center - forms, calculators, tips, more http://taxes.yahoo.com/
Frank D. Engel, Jr. wrote:
As far as that goes, why not simply add on a modifier to the unit itself, as in:
SHARED UNIT name;
<crying desperately="100%"> Please, not another new keyword!
That way, programs and other units would still work the same way, and would not need to specify this specially (you could still do something strange like 'USES SHARED name, STATIC name';
That's already two new mechanisms. I'd prefer to keep it simple.
or for anti-adding-keyword people, a good idea really, 'USES INTERFACE name, USES IMPLEMENTATION name', or sim.)
Much better already (syntactically).
Though, actually, it's not qutie accurate. It would sound like "use the interface/implementation part". But, of course, always the interface part is used, while this is about re-exporting.
Frank
At 9:16 PM +0100 4/3/03, Frank Heckenbach wrote:
Frank D. Engel, Jr. wrote:
As far as that goes, why not simply add on a modifier to the unit itself, as in:
SHARED UNIT name;
<crying desperately="100%"> Please, not another new keyword!
That way, programs and other units would still work the same way, and would not need to specify this specially (you could still do something strange like 'USES SHARED name, STATIC name';
That's already two new mechanisms. I'd prefer to keep it simple.
or for anti-adding-keyword people, a good idea really, 'USES INTERFACE name, USES IMPLEMENTATION name', or sim.)
Much better already (syntactically).
Though, actually, it's not qutie accurate. It would sound like "use the interface/implementation part". But, of course, always the interface part is used, while this is about re-exporting.
I agree with this last point - when it I saw the original suggestion I paused thinking the same thing. I'll try get onto that inheritance model post by the end of the weekend. Kind-of flat out...
Grant
anti-adding-keyword people, a good idea really, 'USES INTERFACE
name,
USES IMPLEMENTATION name', or sim.)
Much better already (syntactically).
Though, actually, it's not qutie accurate. It would sound like "use the interface/implementation part". But, of course, always the interface part is used, while this is about re-exporting.
Actually, that was about static/shared units (at least I thought it was...):
If using a SHARED unit (library), only parts of the 'INTERFACE' are compiled into the currently-in-production target, whereas with a STATIC unit (library), the 'IMPLEMENTATION' is included as well.
That was my logic beind it, and in this way, you don't add any keywords, and it still looks clean, like the rest of Pascal does/should.
===== ======= Frank D. Engel, Jr.
Modify the equilibrium of the vertically-oriented particle decelerator to result in the reestablishment of its resistance to counterproductive atmospheric penetration.
__________________________________________________ Do you Yahoo!? Yahoo! Tax Center - forms, calculators, tips, more http://taxes.yahoo.com/
Frank D. Engel, Jr. wrote:
anti-adding-keyword people, a good idea really, 'USES INTERFACE
name,
USES IMPLEMENTATION name', or sim.)
Much better already (syntactically).
Though, actually, it's not qutie accurate. It would sound like "use the interface/implementation part". But, of course, always the interface part is used, while this is about re-exporting.
Actually, that was about static/shared units (at least I thought it was...):
If using a SHARED unit (library), only parts of the 'INTERFACE' are compiled into the currently-in-production target, whereas with a STATIC unit (library), the 'IMPLEMENTATION' is included as well.
That was my logic beind it, and in this way, you don't add any keywords, and it still looks clean, like the rest of Pascal does/should.
Sorry, I can't follow you. I was talking about static and shared (aka dynamic) libraries on the linker level. As I tried to explain, that's a purely linker issue, nothing to do with the Pascal source. It also doesn't have anything to do with interfaces and implementations -- both static and shared libs must contain all interfaces and implementations, because they are meant to be used "stand-alone" by other code.
And all of this is mostly independent of the "unit inheritance" issue (which I thought your suggestion was about) -- except that an extreme form of unit inheritance seems to be mostly equivalent to a (BPish) library. But for BP libraries, there is already a syntax, so we probably don't need to invent anything new. If we make up something new, it would be for selective unit inheritance ...
Frank
unit foo; interface
propagates Bar, Baz, Fooz, Trook, Trog;
implementation end.
FWIW, I have often wished for a feature like this, too; how about this:
UNIT foo WITH Bar, Baz, Fooz, Trook, Trog; INTERFACE IMPLEMENTATION END.
This eliminates the need for an extra keyword.
===== ======= Frank D. Engel, Jr.
Modify the equilibrium of the vertically-oriented particle decelerator to result in the reestablishment of its resistance to counterproductive atmospheric penetration.
__________________________________________________ Do you Yahoo!? Yahoo! Tax Center - forms, calculators, tips, more http://taxes.yahoo.com/
Frank D. Engel, Jr. wrote:
unit foo; interface
propagates Bar, Baz, Fooz, Trook, Trog;
implementation end.
FWIW, I have often wished for a feature like this, too; how about this:
UNIT foo WITH Bar, Baz, Fooz, Trook, Trog; INTERFACE IMPLEMENTATION END.
This eliminates the need for an extra keyword.
Seems OK syntactically.
BTW, what about EP modules? They can have several interfaces. I guess auto-re-exports should only apply to `export all' interfaces.
Frank
At 10:42 PM +0800 2/3/03, Peter N Lewis wrote:
Please note I'm not advocating Uses Propagation as something that has to go in to GPC, just trying to clarify where it might be useful. There is certainly a good case to be made against uses propagation, and I think I'd rather see some sort of extension to allow this to be used where appropriate, perhaps something like a synonym for "uses" that also reexports the listed units, or perhaps something like "uses WonderSoundInput (reexport);" or perhaps in the style of "external", something like "uses WonderSoundInput; reexport;" although that would really require allowing multiple "uses" statements to allow for some that were reexported and some that were not.
You're right (and I like your example) but having uses work "in context" w.r.t. interface or implementation avoids the need to do this make up extra syntax to handle exporting. I'll address this in a later post (to try answer as much as I can in one place).
While the two unit schemes use the same keywords, they work conceptually differently. Its like a dialect difference to me: something a compiler flag to switch modes could be made (and a compiler directive as I'm a fan of compiler directives to keep things documented within the code ;-) ). But give me a day or so to conjure up a proper code-example based explanation.
Grant
Grant Jacobs wrote:
While the two unit schemes use the same keywords, they work conceptually differently. Its like a dialect difference to me:
Unfortunately one that uses the same syntax for different things (unlike, e.g. units vs. EP modules which can be clearly distinguished).
Frank
Grant Jacobs wrote:
./GHJ_AARegions.pas:49: type name expected, identifier `Fnames' given ./GHJ_AARegions.pas:49: warning: missing type in declaration of `Infname' ./GHJ_AARegions.pas:54: warning: parameter has incomplete type
My trouble was fnames *is* a type. It is *only* defined as a type. So how the heck does the compiler come away thinking that its an identifier?????
Surely it is an identifier. In Pascal, an identifier is any sequence of letters, numbers and underscores (subject to the syntactic restrictions) which isn't a keyword. So any type name is also an identifier.
Since these uses statements were within the interface section, I presumed the definitions would propagate upwards so that they form a hierarchy. Or, put another way, I assumed the interface of a unit is extended by any units it uses in its interface section.
As you found out, this isn't the case.
{=>} points out the offending line of source. If this is truly the problem, then I presume there is no way of building a hierarchy of units in GPC at present? (In this case my safest strategy is to have every unit uses *all* the lower units directly, "just in case".
Yes, in a situation where each unit builds on all the previous ones, that's usually what one does. Then again, there are other situations. E.g., you might have some utility units (such as some of those distributed with GPC, say StringUtils, FileUtils, RegEx, ...) which are used where they're needed; you might have some general low-level units which are used in every unit of your project; you might have some "main functionality" units which build upon each other (i.e., a basic version of something, a refined or extended version etc., or some other functionality which uses the first one); and you might have some that are parallel (different functionalities which do not require each other) which are only put together in the main program or a highest-level unit ...
I think it depends on the situation what one needs. If the compiler would automatically re-export everything imported, you'd have less choices to structure your project.
Or put, another way, I might just as well use includes to make everything one big flat program. Sorry, but I *am* frustrated!)
This would have the disadvantage that separate compilation (automake) wouldn't be possible ...
- This is the correct behaviour?: surely I should be able to use a
higher-level module and have all the lower-level modules definitions within that higher-level module "gotten" at the same time without have to explicitly duck behind the scenes and drag out each module??? Excuse me for asking this, but its not terribly clear from the docs, at least on my reading of it.
I'm not sure if the GPC docs mention it (as usual, they're quite incomplete), but both BP (for units) and EP (for modules) are quite clean about it. EP allows for re-exporting imported identifiers, but they have to be mentioned explicitly in the `export' clause.
Long time ago, there was a short discussion about re-exporting everything from an imported unit -- not automatically, but with some explicit directive ("unit inheritance"), but AFAIK, this hasn't been pursued further yet ...
Addendum: I've since seen section 6.1.8.2. The source structure of UCSD/Borland units of the docs say "A units exports everything declared in the interface section". This it doesn't appear to do if we consider things in the units imported via uses.
Importing <> declaring. The things are declared in the interface section of the lower-level units, but not in the one of the present unit.
- Why the heck does the compiler report the message it does?
"Missing type fnames" perhaps, but since fnames is only ever defined as a type and never as an identifier, it shouldn't be able to report the message it did...
Is this clear now, with the explanation what identifier means? If not, we can, of course, change the wording of the error -- perhaps drop the "identifier" and just "type name expected, `Fnames' given".
I think it would also be easy to distinguish the cases of a unknown identifier (like here), and an identifier which is not a type, if that's what confused you.
- Is there any chance you can tell the user where the "identifier"
(see the error message) is defined so that in future users might have chance of trying to second-guess just what the compiler is "thinking"?
In your situation, as far as I understand it, it isn't declared anywhere (more precisely, in a unit which isn't imported, which is, as far as the current unit is concerned, "nowhere" ;-).
Sorry I can't provide a full code example: I am porting a largish program (approx. 60,000 lines of source in a dozen or so modules) and in any event I can't understand the problem well enough to create an example - catch-22...
Not necessary here, I think your description is quite clear.
Looking at the known bugs for ver. 2.1 there are a couple that might be relevant, esp."Types declared in a module interface are not visible in the implementation" (but does this refer to the same module's interface section and how are units and modules related with reference to this?)
Yes, it refers to the same module. It doesn't affect units, and it's not relevant to your problem.
While I'm writing, is there any chance you can make the compiler keep trying after one unit has failed, rather than stopping at that point? It seems that if there is even one error in a unit, the compilation is abandoned at the end of that unit.
I don't think so. After an error, the compiled unit would be more or less wrong to unusable. Continuing with it might lead to even more obscure follow-up errors. Furthermore, the compiled unit would habe to be marked as erroneous, lest someone uses it in another compilation and gets a seemingly successful compilation. This would add more overhead.
In fact, quite a bit of work in GPC is currently spent on continuing after an error within one source file. This is extra work in the parser (syntactic recovery, still far from perfect) and in the rest of the compiler (passing around error indicators (`error_mark_node') and trapping them in the right places to avoid crashing).
I'm wondering if it wouldn't be easier to drop this all. Today's compilation times are usually short enough that one can retry after each error. But then, the current behaviour is GCC compatible, so I guess we'll have to keep it ...
Now to try figure out how GPC claims a type-mismatch on something that is definitely the same type and the type is only defined in one place...
I don't see it claiming a type-mismatch. It speaks of a "missing type" etc. That's one of those follow-up errors I mentioned (not exactly wrong, but somewhat redundant). Certainly, we could add some more code to avoid this particular message (and dozens of other similar situations). Usually I'm happy if it doesn't crash in such situations ...
As a general rule, everything after the first error message may or may not be accurate. This will always remain true, since no compiler can guess what you really mean when there is an error.
Addendum: Nope. It proves to be a unit issue, but a different one. It seems that --automake doesn't "drill down". If you modify a lower level unit, then recompile a higher-level one that uses the lower-level unit, GPC doesn't re-compile the lower-level unit first and instead comes out with (apparently) misleading error messages.
Which error messages? If you mean the above, they're not due to any automake problem.
--automake in my hands isn't so auto ;-) !
It should work, but there may be problems. It is known that the current automake is not a perfect situation, and the solution will be to replace it by a utility which has a "higher viewpoint". I'm working on this (when I get to it), so I don't think there's much point in debugging this automake problem now (if there's one) ...
Frank
While we've crossed wires a bit, I'll reply all the same :-)
At 10:27 PM +0100 28/2/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
./GHJ_AARegions.pas:49: type name expected, identifier `Fnames' given ./GHJ_AARegions.pas:49: warning: missing type in declaration of `Infname' ./GHJ_AARegions.pas:54: warning: parameter has incomplete type
My trouble was fnames *is* a type. It is *only* defined as a type. So how the heck does the compiler come away thinking that its an identifier?????
Surely it is an identifier. In Pascal, an identifier is any sequence of letters, numbers and underscores (subject to the syntactic restrictions) which isn't a keyword. So any type name is also an identifier.
Ah, I get it. Slow on the uptake... This ought to be re-worded (if you can stand me!), e.g.
type name expected: unknown identifier 'Fnames' given ^^^^^^^
Sounds silly, but I read the original message to mean "the identifier fnames was given but it doesn't fit here" (but it *does* fit there, so I got confused naturally enough!), rather than "I'm expecting a type identifier; I have no idea what fnames is". I should know by now to read all error messages as "you goofed; I have no idea what you're up to, here's my next-to-useless guess" ;-) Sorry, couldn't resist.
You want to distinguish known and unknown identifiers in the error messages to make things clearer (?)
Since these uses statements were within the interface section, I presumed the definitions would propagate upwards so that they form a hierarchy. Or, put another way, I assumed the interface of a unit is extended by any units it uses in its interface section.
As you found out, this isn't the case.
This is not how MW Pascal does it if my memory is right (and it has been a while since I played with that...). And left like this I can't see how you can properly inherit interfaces. (I don't have time to dredge around the standards, sorry, so I'm going on common-sense.)
{=>} points out the offending line of source. If this is truly the problem, then I presume there is no way of building a hierarchy of units in GPC at present? (In this case my safest strategy is to have every unit uses *all* the lower units directly, "just in case".
Yes, in a situation where each unit builds on all the previous ones, that's usually what one does.
Not necessarily: its what you do if you have no other choice!! :-) It forces all higher units to know the dependencies of the lower ones because you can't inherit the dependencies.
Then again, there are other situations. E.g., you might have some utility units (such as some of those distributed with GPC, say StringUtils, FileUtils, RegEx, ...) which are used where they're needed; you might have some general low-level units which are used in every unit of your project; you might have some "main functionality" units which build upon each other (i.e., a basic version of something, a refined or extended version etc., or some other functionality which uses the first one); and you might have some that are parallel (different functionalities which do not require each other) which are only put together in the main program or a highest-level unit ...
I think it depends on the situation what one needs. If the compiler would automatically re-export everything imported, you'd have less choices to structure your project.
Unless you compare units/modules and just consider that they just work from opposite views about how to restrict what is exported:
In units:
When uses is placed into an interface, all of the interface of used unit is propagated (inherited, etc.) unless the programmer restricts what is imported in the uses. [This is what I was hoping for, that is.]
In modules:
Nothing from a uses placed in an interface is exported (propagated, etc.) unless it is explicitly exported.
And in either case interfaces imported into implementation sections are hidden.
Modules are more explicit (and more pendantic!), but you also have to effectively state everything twice, once in a export statement and once just in the interface. If you have a lot to export, this must get to be a handful.
Units are more permissive unless explicitly restricted by just importing specific parts of an interface.
Once you have a means of obtaining just a specified portion of an interface, the module and unit approaches presented here would be equivalent (AFAICS), just coming from different angles. If you also added an 'export all' of a sub module (which I think you were mentioning the other day), they'd pretty much be exact alternatives. (Export is still in the docs as 'not yet implemented!)
Think of the unit mechanism presented here as being akin to inheritance in class libraries. Code that uses a class doesn't have to explicitly import all dependencies: they just import the class and get all the dependencies "free". (That said, unless they use introspection in some way, they still need to know the names of the subclasses, etc., but we're (or rather, I'm) getting off topic...!)
Yes, I could convert them all to classes, but not on the initial port... first time around I want to keep things as close to how they are. Perhaps I'll just have to re-convert everything to modules.
And as I said, I thought this was how MW Pascal units did things. Anyone know how Delphi/Kylix goes about this? (My 'Deplhi in a Nutshell' isn't helpful here and I haven't time to test this just yet.)
Or put, another way, I might just as well use includes to make everything one big flat program. Sorry, but I *am* frustrated!)
This would have the disadvantage that separate compilation (automake) wouldn't be possible ...
I know! -- I'm getting frustrated enough that I might just have to do this... (I don't *want* to...) Modern computers are fast enough speed won't be an issue, but re-use of the units eventually will. I'm just trying to get the initial port going at all first!
- This is the correct behaviour?: surely I should be able to use a
higher-level module and have all the lower-level modules definitions within that higher-level module "gotten" at the same time without have to explicitly duck behind the scenes and drag out each module??? Excuse me for asking this, but its not terribly clear from the docs, at least on my reading of it.
I'm not sure if the GPC docs mention it (as usual, they're quite incomplete), but both BP (for units) and EP (for modules) are quite clean about it. EP allows for re-exporting imported identifiers, but they have to be mentioned explicitly in the `export' clause.
See above. So BP can't inherit subunits and you have to explicitly pick up dependent units?
Long time ago, there was a short discussion about re-exporting everything from an imported unit -- not automatically, but with some explicit directive ("unit inheritance"), but AFAIK, this hasn't been pursued further yet ...
See above.
Addendum: I've since seen section 6.1.8.2. The source structure of UCSD/Borland units of the docs say "A units exports everything declared in the interface section". This it doesn't appear to do if we consider things in the units imported via uses.
Importing <> declaring. The things are declared in the interface section of the lower-level units, but not in the one of the present unit.
This could be confusing and/or limiting: consider this case:
unit FileOps;
interface uses FnameUnit;
function WordCount( var fname: fnames ): integer ;
implementation { expected stuff... } end.
(Leave aside the fact most people would pass the opened file, not its name!) Assume fnames is defined in FnameUnit. Naturally, we want fnames exported for users of unit FileOps. Under the 'no export' scheme the other units need to "just know" that unit FnameUnit has the definition required and import FnameUnit when using FileOps. Under the alternative scheme other units can just 'uses FileOps' and get everything they need without have to know about any dependencies. The latter has its advantages... It'd be nicer if we had the option of only passing on just the things needed by FileOps should we want to, e.g. uses FnameUnit only ( fnames ); (in analogous fashion to how import specifies subsets.)
(I hope I'm not wasting your time with all this -- I like exploring these issues for that one day I get around to developing my own programming language... :-) )
[snip]
In fact, quite a bit of work in GPC is currently spent on continuing after an error within one source file. This is extra work in the parser (syntactic recovery, still far from perfect) and in the rest of the compiler (passing around error indicators (`error_mark_node') and trapping them in the right places to avoid crashing).
OK, it was only a thought. You've a point about extra overhead for error recovery/tracking across files.
I'm wondering if it wouldn't be easier to drop this all. Today's compilation times are usually short enough that one can retry after each error. But then, the current behaviour is GCC compatible, so I guess we'll have to keep it ...
One day, maybe... :-)
For a similar reason, having a brute force compile-all option appeals to me (do the equivalent of --automake, but simply recompile all units you run into regardless of what status the units have [except that you'll want to detect circular dependencies!]). I'm assuming this would avoid any issues in automake, but of course I could be wrong since I don't know how automake is implemented. I'd prefer the little time "wasted" by the computer re-compiling everything, to me having to manually do things.
Now to try figure out how GPC claims a type-mismatch on something that is definitely the same type and the type is only defined in one place...
I don't see it claiming a type-mismatch. It speaks of a "missing type" etc. That's one of those follow-up errors I mentioned (not exactly wrong, but somewhat redundant). Certainly, we could add some more code to avoid this particular message (and dozens of other similar situations). Usually I'm happy if it doesn't crash in such situations ...
:-)
Same issue as before, it seems. If something is unknown, state so explicitly...?
As a general rule, everything after the first error message may or may not be accurate. This will always remain true, since no compiler can guess what you really mean when there is an error.
Like most people, I've been through that particular mill for a long time...! (But I'm still capable of confusing myself over an error message!) We'd all love a compiler that didn't do this, but "that'll be the day..." :-) (Maybe in my own programming language...;-) )
[snip]
Grant
Grant Jacobs wrote:
Sounds silly, but I read the original message to mean "the identifier fnames was given but it doesn't fit here" (but it *does* fit there, so I got confused naturally enough!), rather than "I'm expecting a type identifier; I have no idea what fnames is". I should know by now to read all error messages as "you goofed; I have no idea what you're up to, here's my next-to-useless guess" ;-) Sorry, couldn't resist.
You want to distinguish known and unknown identifiers in the error messages to make things clearer (?)
unknown identifier `%s'
type name expected, `%s' given
?
{=>} points out the offending line of source. If this is truly the problem, then I presume there is no way of building a hierarchy of units in GPC at present? (In this case my safest strategy is to have every unit uses *all* the lower units directly, "just in case".
Yes, in a situation where each unit builds on all the previous ones, that's usually what one does.
Not necessarily: its what you do if you have no other choice!! :-) It forces all higher units to know the dependencies of the lower ones because you can't inherit the dependencies.
No, it doesn't. You only have to use the lower ones if you use some declaration from them. And if you do, you generally know that you do. (Well, I'm used to this, so it may appear more natural to me. But I've never had a big problem due to it ...)
Unless you compare units/modules and just consider that they just work from opposite views about how to restrict what is exported:
In units:
When uses is placed into an interface, all of the interface of used unit is propagated (inherited, etc.) unless the programmer restricts what is imported in the uses. [This is what I was hoping for, that is.]
In modules:
Nothing from a uses placed in an interface is exported (propagated, etc.) unless it is explicitly exported.
And in either case interfaces imported into implementation sections are hidden.
Modules are more explicit (and more pendantic!), but you also have to effectively state everything twice, once in a export statement and once just in the interface. If you have a lot to export, this must get to be a handful.
Yes, that's why GPC has `export Foo = all' as an extension (but referring only to the current module's interface, not to re-exporting). With the proposed extension `export Foo = all (Bar, ...)', it would be possible to export all from the current interface and some selected items from imported modules (such as some types used, as in your example) which I'd consider more reasonable than just re-exporting everything.
Once you have a means of obtaining just a specified portion of an interface,
This might be useful. Do you have any ideas for the syntax (in particular for units), other than an `export' clause (for modules)?
the module and unit approaches presented here would be equivalent (AFAICS), just coming from different angles. If you also added an 'export all' of a sub module (which I think you were mentioning the other day), they'd pretty much be exact alternatives. (Export is still in the docs as 'not yet implemented!)
Don't you mean `exports' (which is, AFIAK, for BP compatible "libraries", not to be confused with `export' for EP modules)?
This could be confusing and/or limiting: consider this case:
unit FileOps;
interface uses FnameUnit;
function WordCount( var fname: fnames ): integer ;
implementation { expected stuff... } end.
(Leave aside the fact most people would pass the opened file, not its name!) Assume fnames is defined in FnameUnit. Naturally, we want fnames exported for users of unit FileOps. Under the 'no export' scheme the other units need to "just know" that unit FnameUnit has the definition required and import FnameUnit when using FileOps. Under the alternative scheme other units can just 'uses FileOps' and get everything they need without have to know about any dependencies. The latter has its advantages... It'd be nicer if we had the option of only passing on just the things needed by FileOps should we want to, e.g. uses FnameUnit only ( fnames ); (in analogous fashion to how import specifies subsets.)
Doing the restriction on the import would be too ... well, restricting. ;-) You might need other stuff from FnameUnit within FileOps.
So, this case would fit the proposed `export Foo = all (Bar, ...)'.
For a similar reason, having a brute force compile-all option appeals to me (do the equivalent of --automake, but simply recompile all units you run into regardless of what status the units have [except that you'll want to detect circular dependencies!]).
You mean `--autobuild'?
As a general rule, everything after the first error message may or may not be accurate. This will always remain true, since no compiler can guess what you really mean when there is an error.
Like most people, I've been through that particular mill for a long time...! (But I'm still capable of confusing myself over an error message!) We'd all love a compiler that didn't do this, but "that'll be the day..." :-) (Maybe in my own programming language...;-) )
My suggestion: Implement a real AI first, then the rest will be trivial. ;-)
Frank
Frank Heckenbach wrote:
Grant Jacobs wrote:
... snip ...
This could be confusing and/or limiting: consider this case:
unit FileOps;
interface uses FnameUnit;
function WordCount( var fname: fnames ): integer ;
implementation { expected stuff... } end.
(Leave aside the fact most people would pass the opened file, not its name!) Assume fnames is defined in FnameUnit. Naturally, we want fnames exported for users of unit FileOps. Under the 'no export' scheme the other units need to "just know" that unit FnameUnit has the definition required and import FnameUnit when using FileOps. Under the alternative scheme other units can just 'uses FileOps' and get everything they need without have to know about any dependencies. The latter has its advantages... It'd be nicer if we had the option of only passing on just the things needed by FileOps should we want to, e.g. uses FnameUnit only ( fnames ); (in analogous fashion to how import specifies subsets.)
Doing the restriction on the import would be too ... well, restricting. ;-) You might need other stuff from FnameUnit within FileOps.
So, this case would fit the proposed `export Foo = all (Bar, ...)'.
I think this shows that the original code is misdesigned. As designed, the user of Wordcount has to know about the type fnames, with no clue whatsoever about what that type actually is or where its definition is to be found. This is a maintenance nightmare.
That user has probably performed other gyrations to prepare something that he considers a member of fnames ahead of time. Thus he should already be "using FnameUnit".
Forcing the specification of fnameunit in that section of code gives a place to search.
At 8:33 AM +0100 1/3/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
[snip]
You want to distinguish known and unknown identifiers in the error messages to make things clearer (?)
unknown identifier `%s'
type name expected, `%s' given
?
Yup. Such a major change, huh ;-) Being pedantic, I'd add 'unknown identifier' after the comma in the second line too:
type name expected, unknown identifier `%s' given
but apart from being a little redundant it makes the line a bit long.
An alternative one-liner might be:
unknown identifier '%s' found in place of type name
Which is a tad longer still... whatever, as long as 'unknown identifier' makes it into there... if it weren't for the length, I kind-of like the last one, FWIW.
[snip]
No, it doesn't. You only have to use the lower ones if you use some declaration from them. And if you do, you generally know that you do. (Well, I'm used to this, so it may appear more natural to me. But I've never had a big problem due to it ...)
I know you can to this; its what I'm using at present and I've used it before too. See my next post (which may be my last on this matter as people really are not "getting it" so I'm thinking of giving up... besides on a pragmatic front modules with 'export all' look like they might do enough to keep me happy for now).
Yes, that's why GPC has `export Foo = all' as an extension (but referring only to the current module's interface, not to re-exporting). With the proposed extension `export Foo = all (Bar, ...)', it would be possible to export all from the current interface and some selected items from imported modules (such as some types used, as in your example) which I'd consider more reasonable than just re-exporting everything.
I'd like to know more about this as it might let me do roughly what I wanted. There are a few other (minor) differences with using a unit approach, but lets just leave that for now...
Once you have a means of obtaining just a specified portion of an interface,
This might be useful. Do you have any ideas for the syntax (in particular for units), other than an `export' clause (for modules)?
I could give you a detailed account of what I am thinking if you *really* want... I'll touch on it in my next post but I'm not likely to post code examples - yet. And I imagine you'll want a code example, naturally enough :-)
the module and unit approaches presented here would be equivalent (AFAICS), just coming from different angles. If you also added an 'export all' of a sub module (which I think you were mentioning the other day), they'd pretty much be exact alternatives. (Export is still in the docs as 'not yet implemented!)
Don't you mean `exports' (which is, AFIAK, for BP compatible "libraries", not to be confused with `export' for EP modules)?
No. This sort of thing does make this kind of discussion confusing :-) I'm talking about modules in that paragraph (a la EP) and hence 'export'. Confused? ;-) (You've already answered part of this above.)
unit FileOps;
interface uses FnameUnit;
function WordCount( var fname: fnames ): integer ;
implementation { expected stuff... } end.
[snip]
The latter has its advantages... It'd be nicer if we had the option of only passing on just the things needed by FileOps should we want to, e.g. uses FnameUnit only ( fnames ); (in analogous fashion to how import specifies subsets.)
Doing the restriction on the import would be too ... well, restricting. ;-) You might need other stuff from FnameUnit within FileOps.
In your model not mine! Think two parallel uses as posted elsewhere, etc, etc. I should really have made up some complicated full code example that illustrates all the points, but I'm too lazy ;-) The example above doesn't include everything, so it looks like the existing unit model which is confusing I suppose. To round out the picture a bit more there should be a 'uses FnameUnit' in the implementation section (so the implementation has everything it needs) and ideally the uses in the interface section would only import fnames so that fnames is the only thing propagated. And you don't have the uses in the interface section "crossing the boundary" to the implementation section (which, FWIW, I personally think is a design flaw in "standard" units, but *please* don't start arguing about that!!). Someone posted a code example like this earlier. Read the *gist* of what I am writing more that my code which is just an incomplete skeleton. But this is all getting a bit long in the tooth -- ?
For a similar reason, having a brute force compile-all option appeals to me (do the equivalent of --automake, but simply recompile all units you run into regardless of what status the units have [except that you'll want to detect circular dependencies!]).
You mean `--autobuild'?
Oh. That one slipped past me! Must try it out... thanks. (I have this list of compiler options on paper I thought might be useful: this never made it into it... must have thought I'd never need --autobuild given the definition of --automake. My fault.)
As a general rule, everything after the first error message may or may not be accurate. This will always remain true, since no compiler can guess what you really mean when there is an error.
Like most people, I've been through that particular mill for a long time...! (But I'm still capable of confusing myself over an error message!) We'd all love a compiler that didn't do this, but "that'll be the day..." :-) (Maybe in my own programming language...;-) )
My suggestion: Implement a real AI first, then the rest will be trivial. ;-)
If how this conversation has spiralled off in weird angles is anything to go by, AI would be a walking disaster! :-) (No offense to any AI folks!) Imagine our AI units all busy arguing how things ought to be...!! Users would wonder why their CPUs are so busy but nothing productive seems to be get done... ;-)
Grant
Grant Jacobs wrote:
At 8:33 AM +0100 1/3/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
[snip]
You want to distinguish known and unknown identifiers in the error messages to make things clearer (?)
unknown identifier `%s'
type name expected, `%s' given
?
Yup. Such a major change, huh ;-) Being pedantic, I'd add 'unknown identifier' after the comma in the second line too:
type name expected, unknown identifier `%s' given
Perhaps I wasn't clear -- the second line is for the case where the identifier is known, but not a type name (but perhaps a variable).
Yes, that's why GPC has `export Foo = all' as an extension (but referring only to the current module's interface, not to re-exporting). With the proposed extension `export Foo = all (Bar, ...)', it would be possible to export all from the current interface and some selected items from imported modules (such as some types used, as in your example) which I'd consider more reasonable than just re-exporting everything.
I'd like to know more about this as it might let me do roughly what I wanted.
I've yet to implement it, so any details that may come up are still open. But AFAICS, it will mean:
- All declarations of the current interface are exported.
- If a name from the current interface occurs within the parentheses with renaming, the renaming is applied. E.g.:
export Foo = all (Bar => Baz);
var Bar, Qux: Integer;
would export Bar as Baz and Qux as Qux.
- If a name from the current interface occurs within the parentheses without renaming, this is redundant (maybe should be an error?).
- Any other name occuring within the parentheses must come from something imported in the interface, and is re-exported again. (With or without renaming.)
I think if you want to re-export selected items from imported units (say, an occasional type or constant), this would fit quite nicely. If, however, you routinely want to re-export everything, it will still be clumsy.
added an 'export all' of a sub module (which I think you were mentioning the other day), they'd pretty much be exact alternatives. (Export is still in the docs as 'not yet implemented!)
Don't you mean `exports' (which is, AFIAK, for BP compatible "libraries", not to be confused with `export' for EP modules)?
No. This sort of thing does make this kind of discussion confusing :-) I'm talking about modules in that paragraph (a la EP) and hence 'export'. Confused? ;-) (You've already answered part of this above.)
I meant your reference to the docs. `exports' is listed as not yet implemented (which is true), but where does it say that `export' is not yet implemented?
Read the *gist* of what I am writing more that my code which is just an incomplete skeleton. But this is all getting a bit long in the tooth -- ?
Sorry, I don't think I can do you this favour. I've just seen far too many cases of people making vague descriptions and incomplete examples, expecting us to second-guess what they really mean (which often turned out way off). Not your fault, sure. But I've given up on this, and prefer to reply to what's actually said.
By the way, to do this properly AFAIK, you need two places for uses per unit.
As I've said elsewhere and an earlier code example showed, there are two locations for uses under this approach. One in the interface section and one in the implementation section.
That's possible with both EP modules and BP units.
The latter imports everything and the kitchen sink to implement its stuff, with no effect on other units. The former, if done properly, only imports (and hence propagates) just those things needed to satisfy the declaration part of the interface. Those are "coincidently" just the things that any importing unit must acquire to use the interface.
Not necessarily. Consider:
Unit1:
const Foo = 42;
Unit2:
uses Unit1;
type Bar = array [1 .. Foo] of Integer;
or:
Unit1:
type Foo = [...];
Unit2:
uses Unit1;
type Bar = record Baz: Foo end;
In both cases (and many more), a program can use Unit2 without using Unit1 (explicitly or implicitly) and have full access to Unit2's definitions.
(2) two parallel uses, one in the interface section and one in the implementation section rather than having one uses work across the interface/implementation boundary
I find this a strange idea -- the implementation gets all the declarations from its interface part, but shouldn't get its imports, but since you don't want to discuss this, we can only agree to disagree.
(3) ideally a means of only importing a subset of an interface so that the users can choose not to propagate the whole of the lower-level interfaces.
That's already possible with `import only' (note: for importing, not for automatic re-exporting).
By way of example, think about what happens if we shift fnames to another unit. Under what you've told me (assuming I'm reading you right), all units which need fnames are now going to have to be edited and re-compiled to import the new unit that now holds fnames. By contrast under the inherited model if fnames is exported by FileOps, units importing FileOps need not be changed; the inheritance looks after the dependencies. Only units immediately "above" where fnames is exported need to be updated. Higher levels will get the type regardless via inheritance. The higher levels are still getting exactly the same items, its just they no longer need to maintain the dependencies of the lower-level units.
Yes, in this example there would be an advantage. We could probably argue about the importance of such cases. Generally, when you shift around definitions across units mid-way in a large project, you should be prepared to have some `uses' lines changed. -- In this example there are probably units that do use FNameUnit directly and do not use FileOps, and those still have to be amended. (If there are no such units, then why separate FNameUnit and FileOps at all? Then you could just move the definition of FNames into FileOps where it would then seem to belong.)
In principle another way of doing this would be to have interfaces export not just the procedure and function identifiers but also the types found in the procedure/function declarations.
Perhaps nitpicking again: They do export the types, but not the type names. In my 2nd example above, the compiled interface of Unit2 would contain a description of type Foo, but not the identifier Foo. I.e., to a program using Unit2, but not Unit1, it would appear like a nameless type, as if Unit2 had said: `type Bar = record Baz: [...] end;'. Maybe that's clear to you, I'm mentioning it just in case, trying to avoid further confusion.
Trouble is there may be the odd constant, etc. that the unit logically wants exported for higher-level use and you'd still need a mechanism to export these. So you might not be much better off.
Of course, you could create an aritifical subrange type just to have the constant mentioned, but that would really be a kludge ... ;-)
This would be a backend change. Peter suggested this to the GCC list some time ago, and it was rejected. (Or rather, he suggested something else, and someone turned turned his suggestion into this, and others flamed him for suggesting what he didn't and for a dozen of other things ...)-:
I am feeling a small bit of how Peter must have felt right now as a similar thing is happening. Some people, rather than trying to *understand* what I am driving at are presenting their own vision, pointing at their *own* vision, then saying "this is stupid" implying in the process this is what I suggested when in fact it *their* vision they are talking about... If they stopped at *thinking* (to themselves, of course!) "that is stupid"... therefore I perhaps don't really get what this guy is after... let's either look at this again or ask... I'd feel a hell of lot more welcome here.
Sorry if you feel this way. However, AFAICS, no-one is claiming that your idea is the same as our model or something like this. Your presented your model, and we showed ours, then we discussed the relative pros and cons of them. Isn't this a valid form of discussion?
Frank
At 2:27 PM +0100 2/3/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
You want to distinguish known and unknown identifiers in the error messages to make things clearer (?)
unknown identifier `%s'
type name expected, `%s' given
?
Yup. Such a major change, huh ;-) Being pedantic, I'd add 'unknown identifier' after the comma in the second line too:
type name expected, unknown identifier `%s' given
Perhaps I wasn't clear -- the second line is for the case where the identifier is known, but not a type name (but perhaps a variable).
Ah. You could just do variations on the one-liner... e.g.
type name expected, unknown identifier '%s' given type name expected, variable '%s' given type name expected, constant '%s' given
constant name expected, ...
variable name expected, ...
etc. Combinatorial logic :-)
Is this OK, or am I still off in the woop woops? (the sticks, that is)
Yes, that's why GPC has `export Foo = all' as an extension (but referring only to the current module's interface, not to re-exporting). With the proposed extension `export Foo = all (Bar, ...)', it would be possible to export all from the current interface and some selected items from imported modules (such as some types used, as in your example) which I'd consider more reasonable than just re-exporting everything.
I'd like to know more about this as it might let me do roughly what I wanted.
I've yet to implement it, so any details that may come up are still open.
OK.
But AFAICS, it will mean:
All declarations of the current interface are exported.
If a name from the current interface occurs within the parentheses with renaming, the renaming is applied. E.g.:
export Foo = all (Bar => Baz);
var Bar, Qux: Integer;
would export Bar as Baz and Qux as Qux.
If a name from the current interface occurs within the parentheses without renaming, this is redundant (maybe should be an error?).
A warning rather than an error since its only redundant?
- Any other name occuring within the parentheses must come from something imported in the interface, and is re-exported again. (With or without renaming.)
I think if you want to re-export selected items from imported units (say, an occasional type or constant), this would fit quite nicely. If, however, you routinely want to re-export everything, it will still be clumsy.
Hmm. Good except for that last one, which isn't so hot for what I was after (but I suppose other will debate that!). If you added the ability to name an imported unit rather than its contents in an export statement, it looks like the sort thing I was after. If people don't want to export unit interfaces (or parts thereof), they can just not do it. I assume unit names and other identifiers can't conflict...?
added an 'export all' of a sub module (which I think you were mentioning the other day), they'd pretty much be exact alternatives. (Export is still in the docs as 'not yet implemented!)
Don't you mean `exports' (which is, AFIAK, for BP compatible "libraries", not to be confused with `export' for EP modules)?
No. This sort of thing does make this kind of discussion confusing :-) I'm talking about modules in that paragraph (a la EP) and hence 'export'. Confused? ;-) (You've already answered part of this above.)
I meant your reference to the docs. `exports' is listed as not yet implemented (which is true), but where does it say that `export' is not yet implemented?
Sorry, you're right; export is listed as '(under construction)' (i.e. no docs for it). But I was trying to talk about export, not exports.
Read the *gist* of what I am writing more that my code which is just an incomplete skeleton. But this is all getting a bit long in the tooth -- ?
Sorry, I don't think I can do you this favour. I've just seen far too many cases of people making vague descriptions and incomplete examples, expecting us to second-guess what they really mean (which often turned out way off). Not your fault, sure. But I've given up on this, and prefer to reply to what's actually said.
For designing something you're right and I sympathetise. I'm really just trying to get the basic concepts across. When I started this I wasn't really trying to suggest this as something to implement - I was just confused as to how units work in GPC vs. how I vaguely remembered them working and wished there was something like it in GPC. Other people criticised what I posted and as if it were an implementation suggestion, which it has turned into... I'll see if I can swop over to "design mode" properly for you in a following post and try and explain things there. Its getting scattered across too many posts.
By the way, to do this properly AFAIK, you need two places for
uses per unit.
As I've said elsewhere and an earlier code example showed, there are two locations for uses under this approach. One in the interface section and one in the implementation section.
That's possible with both EP modules and BP units.
But the effect of placing a uses into different places in BP units seems to have no difference in effect (aside from satisfying the interface declaration part); that is uses seems to 'ignore' the context of interface, implementation (or for that matter outside both). I'll address this properly in a later post.
The latter imports everything and the kitchen sink to implement its stuff, with no effect on other units. The former, if done properly, only imports (and hence propagates) just those things needed to satisfy the declaration part of the interface. Those are "coincidently" just the things that any importing unit must acquire to use the interface.
Not necessarily. Consider:
Unit1:
const Foo = 42;
Unit2:
uses Unit1;
type Bar = array [1 .. Foo] of Integer;
or:
Unit1:
type Foo = [...];
Unit2:
uses Unit1;
type Bar = record Baz: Foo end;
In both cases (and many more), a program can use Unit2 without using Unit1 (explicitly or implicitly) and have full access to Unit2's definitions.
Sure, but I don't think this addresses my argument. You're right as long as the program doesn't need to check the array's upper bound in case 1 or fool around with things of type Foo in case 2. In either case they'd have to do a 'uses Unit1'. What I was talking about is a scheme where a program can use Unit2 and "automatically" get (the portion of) Unit1's interface which is relevant to use of Unit2.
(2) two parallel uses, one in the interface section and one in the implementation section rather than having one uses work across the interface/implementation boundary
I find this a strange idea -- the implementation gets all the declarations from its interface part, but shouldn't get its imports, but since you don't want to discuss this, we can only agree to disagree.
Ah, you're thinking something quite different about the interface section. I'll bear this in mind as I write my later post; it probably explains some of the confusion.
(3) ideally a means of only importing a subset of an interface so that the users can choose not to propagate the whole of the lower-level interfaces.
That's already possible with `import only' (note: for importing, not for automatic re-exporting).
By way of example, think about what happens if we shift fnames to another unit. Under what you've told me (assuming I'm reading you right), all units which need fnames are now going to have to be edited and re-compiled to import the new unit that now holds fnames. By contrast under the inherited model if fnames is exported by FileOps, units importing FileOps need not be changed; the inheritance looks after the dependencies. Only units immediately "above" where fnames is exported need to be updated. Higher levels will get the type regardless via inheritance. The higher levels are still getting exactly the same items, its just they no longer need to maintain the dependencies of the lower-level units.
Yes, in this example there would be an advantage. We could probably argue about the importance of such cases. Generally, when you shift around definitions across units mid-way in a large project, you should be prepared to have some `uses' lines changed. --
Perhaps, but shouldn't you try minimise the impact of changes? Its part of what modularity, etc., is all about.
In this example there are probably units that do use FNameUnit directly and do not use FileOps, and those still have to be amended.
True: as I was trying to say the units on the immediately higher level are always going to have to be updated in such a case - probably regardless of the unit scheme chosen. But why impose these changes all the way up the unit hierarchy when "inheritance" (I'm still not sure if this is the right word) could take care of it.
(If there are no such units, then why separate FNameUnit and FileOps at all?
For one you can't forsee the future! :-) For another the sheer size and presence of common elements in the units might naturally suggest splitting a unit. Peter gives a nice e.g. of this.
Then you could just move the definition of FNames into FileOps where it would then seem to belong.)
Equally you could leave them in separate units, and use inheritance make it look as if the two are in the same unit (FileOps) to higher levels ;-)
See Peter's example, perhaps. He's also more concise than me :-)
In principle another way of doing this would be to have interfaces export not just the procedure and function identifiers but also the types found in the procedure/function declarations.
Perhaps nitpicking again: They do export the types, but not the type names. In my 2nd example above, the compiled interface of Unit2 would contain a description of type Foo, but not the identifier Foo. I.e., to a program using Unit2, but not Unit1, it would appear like a nameless type, as if Unit2 had said: `type Bar = record Baz: [...] end;'. Maybe that's clear to you, I'm mentioning it just in case, trying to avoid further confusion.
I got you, & thanks this is a nice point. But see my comments on your e.g.
Trouble is there may be the odd constant, etc. that the unit logically wants exported for higher-level use and you'd still need a mechanism to export these. So you might not be much better off.
Of course, you could create an aritifical subrange type just to have the constant mentioned, but that would really be a kludge ... ;-)
Yeah, it would :-) And it'd be wordier than just importing the constant.
Sorry if you feel this way. However, AFAICS, no-one is claiming that your idea is the same as our model or something like this. Your presented your model, and we showed ours, then we discussed the relative pros and cons of them. Isn't this a valid form of discussion?
Yes, if people realised that their (early) criticism was actually directly at their own model! I think people are beginning to see that of their own accord anyway, so the point is moot now anyway.
Grant
Grant Jacobs wrote:
At 2:27 PM +0100 2/3/03, Frank Heckenbach wrote:
Grant Jacobs wrote:
You want to distinguish known and unknown identifiers in the error messages to make things clearer (?)
unknown identifier `%s'
type name expected, `%s' given
?
Yup. Such a major change, huh ;-) Being pedantic, I'd add 'unknown identifier' after the comma in the second line too:
type name expected, unknown identifier `%s' given
Perhaps I wasn't clear -- the second line is for the case where the identifier is known, but not a type name (but perhaps a variable).
Ah. You could just do variations on the one-liner... e.g.
type name expected, unknown identifier '%s' given type name expected, variable '%s' given type name expected, constant '%s' given
constant name expected, ...
variable name expected, ...
etc. Combinatorial logic :-)
And exponential complexity, as so often in combinatorics.
program Foo; type bar = 1 .. 1;
procedure Baz; const BaR = 64;
type Foo (n: Integer) = record BAR: Integer; case Bar of 1: () end; [...]
error: a variant selector that is a single identifier can be either a tag-type or a discriminant-identifier; however, the given identifier `Bar' is defined, albeit with different capitalization `BAR', as a field of the record being defined which is no discriminant, although the record is a schema type; the identifier is also defined as an ordinal type, with different capitalization `bar', at the global level, which would be suitable for a tag-type, but this type definition is shadowed by a constant definition, with yet another capitalization `BaR', within the current procedure `Baz'. There is also a type definition `bAr' in the file `myunits/foo42.pas', but this unit is not used in this program, so its declarations are not available, and if they were, this one would still be shadowed by the constant definition.
SCNR. ;-)
I mean, sure, the compiler could always tell if the identifier is a constant, variable, etc., perhaps if it has shadowed meanings, and some amount of other information, but how far should we go? (Or rather, who would like to implement it?) Usually, I think, it's enough to know whether the identifier is unknown ...
In your case, the real confusion arose of a different issue (how units work), and I don't think the error message could have really helped here ...
- If a name from the current interface occurs within the parentheses without renaming, this is redundant (maybe should be an error?).
A warning rather than an error since its only redundant?
OK.
Hmm. Good except for that last one, which isn't so hot for what I was after (but I suppose other will debate that!). If you added the ability to name an imported unit rather than its contents in an export statement, it looks like the sort thing I was after.
I thought so.
If people don't want to export unit interfaces (or parts thereof), they can just not do it. I assume unit names and other identifiers can't conflict...?
They can (according EP, module names are in a separate "name space").
Besides, it would be harder to implement than, say, a special form of `uses', since the compiler would have to keep track of which identifier was imported from which unit, so it can do the re-exports properly.
I meant your reference to the docs. `exports' is listed as not yet implemented (which is true), but where does it say that `export' is not yet implemented?
Sorry, you're right; export is listed as '(under construction)' (i.e. no docs for it).
If this is confusing, should it be changed to `(description under construction)' or so?
Sure, but I don't think this addresses my argument. You're right as long as the program doesn't need to check the array's upper bound in case 1 or fool around with things of type Foo in case 2. In either case they'd have to do a 'uses Unit1'.
Or use `High (Bar)' or `type of MyBar.Baz'. ;-)
If it wants to do more with type `Baz' (declare variables of this type etc.), I see nothing wrong in requiring that it actually uses the unit that declares this type ...
Frank
On 1 Mar 2003 at 12:48, Grant Jacobs wrote:
[...]
When uses is placed into an interface, all of the interface of used unit is propagated (inherited, etc.) unless the programmer restricts what is imported in the uses.
No.
[This is what I was hoping for, that is.]
That wouldn't comply with the UCSD/BP "standards" for units.
And as I said, I thought this was how MW Pascal units did things. Anyone know how Delphi/Kylix goes about this? (My 'Deplhi in a Nutshell' isn't helpful here and I haven't time to test this just yet.)
A unit exports only things in it's own interface section. If you use another unit, it only allows you access to things in the interface section of that other unit - and that is all it does. Any program/unit that desire similar access has to use the other unit for itself. This behaviour is far safer IMHO to the "interface inheritance" that you propose. Like Frank, I think that interface inheritance will cause problems. I certainly do not want to automatically reexport all interfaces to all units that I might be using. This can, IMHO, lead to nothing but confusion.
This would have the disadvantage that separate compilation (automake) wouldn't be possible ...
I know! -- I'm getting frustrated enough that I might just have to do this... (I don't *want* to...) Modern computers are fast enough speed won't be an issue, but re-use of the units eventually will. I'm just trying to get the initial port going at all first!
What is wrong with simply using all the units that you need in any source wherein you need them? Am I missing something here?
[...]
Importing <> declaring. The things are declared in the interface section of the lower-level units, but not in the one of the present unit.
This could be confusing and/or limiting: consider this case:
unit FileOps;
interface uses FnameUnit;
function WordCount( var fname: fnames ): integer ;
implementation { expected stuff... } end.
(Leave aside the fact most people would pass the opened file, not its name!)
No, most people would not. In fact, not a single Pascal programmer would. How did you come by this idea?
Assume fnames is defined in FnameUnit. Naturally, we want fnames exported for users of unit FileOps. Under the 'no export' scheme the other units need to "just know" that unit FnameUnit has the definition required and import FnameUnit when using FileOps.
Yes. It is up to the programmer to supply that information. Supplying it takes only two seconds: "Uses FNameUnit;". If one does not want to edit every source file manually, supplying "--uses=FNameUnit" on the command line should to the job. I am not sure what the problem is here.
Under the alternative scheme other units can just 'uses FileOps' and get everything they need without have to know about any dependencies. The latter has its advantages... It'd be nicer if we had the option of only passing on just the things needed by FileOps should we want to, e.g. uses FnameUnit only ( fnames ); (in analogous fashion to how import specifies subsets.)
It seems that what you want is a hybrid of units+modules+something_else The rules for units and modules are quite clear (according to the standards being supported thereby). Something_else would be a new thing, with its own rules. I am not sure where that will lead us! I personally would not have thought that "interface inheritance" was a desirable thing to add (nor would I have thought that it was a priority at this point in time). The problems you raise are easily addressed by "--uses". Why do we need something else?
It might of course be easy to add this type of inheritance if the unit mechanism were object oriented, but I don't know anything about the internal implementation of it.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~african_chief/
On 28 Feb 2003 at 22:27, Frank Heckenbach wrote:
[...]
In fact, quite a bit of work in GPC is currently spent on continuing after an error within one source file. This is extra work in the parser (syntactic recovery, still far from perfect) and in the rest of the compiler (passing around error indicators (`error_mark_node') and trapping them in the right places to avoid crashing).
I'm wondering if it wouldn't be easier to drop this all. Today's compilation times are usually short enough that one can retry after each error. But then, the current behaviour is GCC compatible, so I guess we'll have to keep it ...
It comes in handy sometimes - but I don't believe that all Pascal compilers do it. One might miss having the feature in some situations, but I don't think it will be a disaster to dump it. It is also a good thing to have the option of stopping after the first error is encountered.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~african_chief/
Prof A Olowofoyeku (The African Chief) wrote:
It comes in handy sometimes - but I don't believe that all Pascal compilers do it. One might miss having the feature in some situations, but I don't think it will be a disaster to dump it. It is also a good thing to have the option of stopping after the first error is encountered.
This would be a backend change. Peter suggested this to the GCC list some time ago, and it was rejected. (Or rather, he suggested something else, and someone turned turned his suggestion into this, and others flamed him for suggesting what he didn't and for a dozen of other things ...)-:
Implementing such an option would be trivial, but I guess as long as anyone still believes in GCC integration, you better try to convince the GCC people before we do it on our own. (Good luck!)-:
Frank
At 7:47 PM +0100 1/3/03, Frank Heckenbach wrote:
Prof A Olowofoyeku (The African Chief) wrote:
It comes in handy sometimes - but I don't believe that all Pascal compilers do it. One might miss having the feature in some situations, but I don't think it will be a disaster to dump it. It is also a good thing to have the option of stopping after the first error is encountered.
This would be a backend change. Peter suggested this to the GCC list some time ago, and it was rejected. (Or rather, he suggested something else, and someone turned turned his suggestion into this, and others flamed him for suggesting what he didn't and for a dozen of other things ...)-:
I am feeling a small bit of how Peter must have felt right now as a similar thing is happening. Some people, rather than trying to *understand* what I am driving at are presenting their own vision, pointing at their *own* vision, then saying "this is stupid" implying in the process this is what I suggested when in fact it *their* vision they are talking about... If they stopped at *thinking* (to themselves, of course!) "that is stupid"... therefore I perhaps don't really get what this guy is after... let's either look at this again or ask... I'd feel a hell of lot more welcome here.
Observing these replies, for the most part they quote the bits that are least relevant to my overall point... sigh :-( Maybe I'll just give up? Some of you are on a different, albeit related, tack. The irony for me is that I believe I can see where you're coming from and where your argument heads to which makes it even more frustrating to me. I can understand the confusion as the model you are presenting is related, but its *not* what I am thinking of!
(The full story is, of course, longer and more confusing, but I really don't want to start down that road...)
Implementing such an option would be trivial, but I guess as long as anyone still believes in GCC integration, you better try to convince the GCC people before we do it on our own. (Good luck!)-:
As I think I've already said, I'm not really fussed either way, it was just a loose thought. If its more work, let it slide; I'm not trying to make work for anyone ;-)
Grant
On 1 Mar 2003 at 19:47, Frank Heckenbach wrote:
Prof A Olowofoyeku (The African Chief) wrote:
It comes in handy sometimes - but I don't believe that all Pascal compilers do it. One might miss having the feature in some situations, but I don't think it will be a disaster to dump it. It is also a good thing to have the option of stopping after the first error is encountered.
This would be a backend change. Peter suggested this to the GCC list some time ago, and it was rejected. (Or rather, he suggested something else, and someone turned turned his suggestion into this, and others flamed him for suggesting what he didn't and for a dozen of other things ...)-:
Implementing such an option would be trivial, but I guess as long as anyone still believes in GCC integration, you better try to convince the GCC people before we do it on our own. (Good luck!)-:
Hmmm. ...I think I'll pass ...
I am not sure what advantages GCC integration will bring, but it is probably something to be striven for.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~african_chief/