One item on the to-do list are BP compatible dynamic methods. As far as I understand the BP description, they are semantically equivalent to virtual methods, only the syntax is different (an integer index added) and the implementation (which we don't have to follow, of course).
Ignoring this index syntactically would be easy to do. Would this be useful, and is it really all that's required?
Frank
On 22 Apr 2003 at 17:54, Frank Heckenbach wrote:
One item on the to-do list are BP compatible dynamic methods. As far as I understand the BP description, they are semantically equivalent to virtual methods, only the syntax is different (an integer index added) and the implementation (which we don't have to follow, of course).
Ignoring this index syntactically would be easy to do. Would this be useful, and is it really all that's required?
Ignoring the index would make a dynamic method the same as a virtual method. This can be somewhat useful (with appropriate health warnings) when porting existing code (i.e., GPC accepts the syntax, but the meaning would be different). From what I can see the real usefulness of dynamic methods (apart from alleged savings in memory) lies in the index. BP uses this for its message dispatching mechanisms in the OWL framework (although this is probably at library level rather than at compiler level). Delphi can use it as well. I have sorely missed this feature when trying to build an OWL-compatible OOP framework, and have had to find klugdes round it. A simple example; type foo = object (tfoo) procedure cmSave (var m : tmessage); virtual cm_Save; end;
What this means in BP/Delphi is that when the menu or button (or whatever) with the integer ID of cm_Save is selected/clicked, etc., then run the method "cmSave". Obviously there is a message dispatch mechanism involved, in order for anything to know that cm_Save has been clicked/selected.
Having now written all this, I am starting to wonder as to its relevance as it seems clear that this has nothing to do with the compiler per se ;-/
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 22 Apr 2003 at 17:54, Frank Heckenbach wrote:
One item on the to-do list are BP compatible dynamic methods. As far as I understand the BP description, they are semantically equivalent to virtual methods, only the syntax is different (an integer index added) and the implementation (which we don't have to follow, of course).
Ignoring this index syntactically would be easy to do. Would this be useful, and is it really all that's required?
Ignoring the index would make a dynamic method the same as a virtual method. This can be somewhat useful (with appropriate health warnings) when porting existing code (i.e., GPC accepts the syntax, but the meaning would be different).
Depends. For "normal" usage (without this dispatching) it would be the same AFAICS.
From what I can see the real usefulness of dynamic methods (apart from alleged savings in memory) lies in the index. BP uses this for its message dispatching mechanisms in the OWL framework (although this is probably at library level rather than at compiler level). Delphi can use it as well. I have sorely missed this feature when trying to build an OWL-compatible OOP framework, and have had to find klugdes round it. A simple example; type foo = object (tfoo) procedure cmSave (var m : tmessage); virtual cm_Save; end;
What this means in BP/Delphi is that when the menu or button (or whatever) with the integer ID of cm_Save is selected/clicked, etc., then run the method "cmSave". Obviously there is a message dispatch mechanism involved, in order for anything to know that cm_Save has been clicked/selected.
Having now written all this, I am starting to wonder as to its relevance as it seems clear that this has nothing to do with the compiler per se ;-/
I think there are different ways how to implement such a dispatcher:
a)
case ID of ... cm_Save: cmSave (m); ... end;
This is plain (OOP) Pascal, and would work with normal virtual methods, and therefore also with "dynamic" ones if GPC just ignores the index.
b)
Using some magic that looks up the dynamic method index in some internal table to do the call.
The disadvantages of b) are that it requires more compiler magic, and that it might be type-unsafe (if the method is looked up at runtime, there's no way for the compiler to check the parameter list -- unless all dynamic methods are required to have the same parameters).
The only advantages I can see are that it might save a few bytes in memory (which is most likely a non-issue today and probably it was even back then), and that it may have been faster at runtime than a `case' statement in BP. However, GPC/GCC optimizes `case' much better, using jump tables when useful, so that's probably no issue, either.
Then, it's a little effort to write the `case' statement, but that's mostly a one-time job I think (and can probably be added to existing BP code locally), so I'd suggest this.
Frank
On 23 Apr 2003 at 11:47, Frank Heckenbach wrote:
[...]
Ignoring the index would make a dynamic method the same as a virtual method. This can be somewhat useful (with appropriate health warnings) when porting existing code (i.e., GPC accepts the syntax, but the meaning would be different).
Depends. For "normal" usage (without this dispatching) it would be the same AFAICS.
Correct.
From what I can see the real usefulness of dynamic methods (apart from alleged savings in memory) lies in the index. BP uses this for its message dispatching mechanisms in the OWL framework (although this is probably at library level rather than at compiler level). Delphi can use it as well. I have sorely missed this feature when trying to build an OWL-compatible OOP framework, and have had to find klugdes round it. A simple example; type foo = object (tfoo) procedure cmSave (var m : tmessage); virtual cm_Save; end;
What this means in BP/Delphi is that when the menu or button (or whatever) with the integer ID of cm_Save is selected/clicked, etc., then run the method "cmSave". Obviously there is a message dispatch mechanism involved, in order for anything to know that cm_Save has been clicked/selected.
Having now written all this, I am starting to wonder as to its relevance as it seems clear that this has nothing to do with the compiler per se ;-/
I think there are different ways how to implement such a dispatcher:
a)
case ID of ... cm_Save: cmSave (m); ... end;
This is what I have done/am doing in my OOP framework (slightly similar to Borland's OWL framework). But it involved a number of extra steps and kludges which are not necessary in BP or Delphi. It was actually do-able without any major grief.
This is plain (OOP) Pascal, and would work with normal virtual methods, and therefore also with "dynamic" ones if GPC just ignores the index.
b)
Using some magic that looks up the dynamic method index in some internal table to do the call.
The disadvantages of b) are that it requires more compiler magic, and that it might be type-unsafe (if the method is looked up at runtime, there's no way for the compiler to check the parameter list -- unless all dynamic methods are required to have the same parameters).
In BP OWL and Delphi VCL code, they all seemed to involve a parameter "(Var Message : TMessage)" - but may well be a result of the message dispatching protocols involved in the respective OOP frameworks. If you tried this in OWL without having that parameter, you always got a GPF (aka segfault, stackdump, or whatever corresponds on other systems). The GPF may well be a sign of the type- unsafeness that you talk about (i.e., it is up to the programmer to get it right). Personally, I think that is a small price to pay - but others will probably disagree.
The only advantages I can see are that it might save a few bytes in memory (which is most likely a non-issue today and probably it was even back then), and that it may have been faster at runtime than a `case' statement in BP. However, GPC/GCC optimizes `case' much better, using jump tables when useful, so that's probably no issue, either.
Then, it's a little effort to write the `case' statement, but that's mostly a one-time job I think (and can probably be added to existing BP code locally), so I'd suggest this.
Unfortunately, my experience with building an OOP framework showed that it is far more involved than a simple case statement. I basically had to invent my own message dispatching mechanisms. These involved case statements, but that was only the end of the process. It may well be however that, for normal programming, this wouldn't be the case, and it would just be a matter of a simple case statement.
Be that as it may, simply supporting the syntax would be good enough, and I would be happy with whatever direction you choose to go.
Best regards, The Chief --------- Prof. Abimbola Olowofoyeku (The African Chief) Web: http://www.bigfoot.com/~african_chief/
Prof. A Olowofoyeku (The African Chief) wrote:
b)
Using some magic that looks up the dynamic method index in some internal table to do the call.
The disadvantages of b) are that it requires more compiler magic, and that it might be type-unsafe (if the method is looked up at runtime, there's no way for the compiler to check the parameter list -- unless all dynamic methods are required to have the same parameters).
In BP OWL and Delphi VCL code, they all seemed to involve a parameter "(Var Message : TMessage)" - but may well be a result of the message dispatching protocols involved in the respective OOP frameworks. If you tried this in OWL without having that parameter, you always got a GPF (aka segfault, stackdump, or whatever corresponds on other systems). The GPF may well be a sign of the type- unsafeness that you talk about (i.e., it is up to the programmer to get it right).
It seems so.
The only advantages I can see are that it might save a few bytes in memory (which is most likely a non-issue today and probably it was even back then), and that it may have been faster at runtime than a `case' statement in BP. However, GPC/GCC optimizes `case' much better, using jump tables when useful, so that's probably no issue, either.
Then, it's a little effort to write the `case' statement, but that's mostly a one-time job I think (and can probably be added to existing BP code locally), so I'd suggest this.
Unfortunately, my experience with building an OOP framework showed that it is far more involved than a simple case statement. I basically had to invent my own message dispatching mechanisms. These involved case statements, but that was only the end of the process. It may well be however that, for normal programming, this wouldn't be the case, and it would just be a matter of a simple case statement.
It might not be all that's required for an OOP framework, but I can't see the dynamic methods doing much more than a `case' statement. After all, it's only one integer value ...
Maybe they use other "magic", hidden in the compiler or (I hope) the libraries, independent of dynamic methods.
Be that as it may, simply supporting the syntax would be good enough, and I would be happy with whatever direction you choose to go.
For now I'll just ignore the index. If it ever becomes necessary to use it, one will have to think about how to make it available ...
Frank
On 23 Apr 2003 at 18:11, Frank Heckenbach wrote:
[...]
It might not be all that's required for an OOP framework, but I can't see the dynamic methods doing much more than a `case' statement. After all, it's only one integer value ...
Maybe they use other "magic", hidden in the compiler or (I hope) the libraries, independent of dynamic methods.
Be that as it may, simply supporting the syntax would be good enough, and I would be happy with whatever direction you choose to go.
For now I'll just ignore the index. If it ever becomes necessary to use it, one will have to think about how to make it available ...
I may just be about to reveal my ignorance - but can the integer values for dynamic methods not be stored somewhere in the VMT or some other such place? And if they can, is there some way of accessing the VMT or its data?
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 23 Apr 2003 at 18:11, Frank Heckenbach wrote:
[...]
It might not be all that's required for an OOP framework, but I can't see the dynamic methods doing much more than a `case' statement. After all, it's only one integer value ...
Maybe they use other "magic", hidden in the compiler or (I hope) the libraries, independent of dynamic methods.
Be that as it may, simply supporting the syntax would be good enough, and I would be happy with whatever direction you choose to go.
For now I'll just ignore the index. If it ever becomes necessary to use it, one will have to think about how to make it available ...
I may just be about to reveal my ignorance - but can the integer values for dynamic methods not be stored somewhere in the VMT or some other such place?
Sure, storing them is easy. The question is what to do with them afterwards?
And if they can, is there some way of accessing the VMT or its data?
`TypeOf' returns a `PObjectType' pointer which contains the invarying parts of VMTs (see the manual).
It is possible with some dirty tricks (type-casting etc.) to go beyond these fields and access the further contents of the VMT. But I'm not sure we should introduce an "official" feature for it. It wouldn't be too difficult to get the address of some virtual method, but the problem is to store the type (parameter etc.) information -- and much more, checking them on calling.
I.e., when you read the address of a method directly from the VMT, it will just be an untyped pointer, and the compiler can't guess the right type if the method is found at a non-constant place (e.g., by searching for a dynamic index).
So doing this would forfeit all type-checking. This is typical for type-casts, so as said above, I prefer to not make it possible unless one uses type-casts (on the VMT pointer in this case) ...
And again, I don't recommend this for this dispatcher. As I said, I can't see this index doing more than a `case' statement (if it does, then please explain ...), and writing one `case' statement is a small job (one time, and probably mostly automatic), compared to getting mysterious crashes etc. due to the lack of type-checking (and that for possibly every user of the library).
Frank
On 24 Apr 2003 at 10:55, Frank Heckenbach wrote:
[...]
I may just be about to reveal my ignorance - but can the integer values for dynamic methods not be stored somewhere in the VMT or some other such place?
Sure, storing them is easy. The question is what to do with them afterwards?
That can be left to the programmer to do something with it, or to do nothing with it. Never underestimate the ability of programmers to do something with something ;-/
And if they can, is there some way of accessing the VMT or its data?
`TypeOf' returns a `PObjectType' pointer which contains the invarying parts of VMTs (see the manual).
It is possible with some dirty tricks (type-casting etc.) to go beyond these fields and access the further contents of the VMT. But I'm not sure we should introduce an "official" feature for it. It wouldn't be too difficult to get the address of some virtual method, but the problem is to store the type (parameter etc.) information -- and much more, checking them on calling.
I.e., when you read the address of a method directly from the VMT, it will just be an untyped pointer, and the compiler can't guess the right type if the method is found at a non-constant place (e.g., by searching for a dynamic index).
So doing this would forfeit all type-checking. This is typical for type-casts, so as said above, I prefer to not make it possible unless one uses type-casts (on the VMT pointer in this case) ...
And again, I don't recommend this for this dispatcher. As I said, I can't see this index doing more than a `case' statement (if it does, then please explain ...), and writing one `case' statement is a small job (one time, and probably mostly automatic), compared to getting mysterious crashes etc. due to the lack of type-checking (and that for possibly every user of the library).
It can do more than a case statement. In fact, it can save one from having to use a whole load of case statements (which is what I had to do in my OOP framework, and I could have avoided all that, had this facility been available). Look at this rough (and probably defective, since I haven't really thought this through) example:
Type TMethodType = Procedure (Sender : pObject);
Procedure Dispatcher (Sender : pObject; index: integer); Var p : TMethodType; begin If index <> 0 then begin p := GetMethodPtr (GetVMT (Sender), index); If Assigned (p) then p (Sender) else {raise an exception, be silent, or whatever} end; end;
If I could have done this (or anything vaguely resembling it), my life would have been a lot easier when I was working on the OOP framework. Yes, this would have been type unsafe, etc., but it would only have been done in one place (the above dispatcher would be at the lowest level), and all that the user of the framework needs to know is that dynamic methods in the framework must have the format; procedure foo (Sender : pObject); virtual index_number;
Everything else is taken care of at a lower level, and users will not need to worry about producing case statements in subclasses, or whatever.
The above example of course presupposes that one can readily access the VMT and can retrieve various things for it (even if it would mean some hackery or dirty tricks).
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 24 Apr 2003 at 10:55, Frank Heckenbach wrote:
[...]
I may just be about to reveal my ignorance - but can the integer values for dynamic methods not be stored somewhere in the VMT or some other such place?
Sure, storing them is easy. The question is what to do with them afterwards?
That can be left to the programmer to do something with it, or to do nothing with it. Never underestimate the ability of programmers to do something with something ;-/
Sorry, such blanket statements don't impress me.
If just done the obvious way, the programmers would probably write a linear search loop, which is much less efficient than a `case' statement.
And if they can, is there some way of accessing the VMT or its data?
`TypeOf' returns a `PObjectType' pointer which contains the invarying parts of VMTs (see the manual).
It is possible with some dirty tricks (type-casting etc.) to go beyond these fields and access the further contents of the VMT. But I'm not sure we should introduce an "official" feature for it. It wouldn't be too difficult to get the address of some virtual method, but the problem is to store the type (parameter etc.) information -- and much more, checking them on calling.
I.e., when you read the address of a method directly from the VMT, it will just be an untyped pointer, and the compiler can't guess the right type if the method is found at a non-constant place (e.g., by searching for a dynamic index).
So doing this would forfeit all type-checking. This is typical for type-casts, so as said above, I prefer to not make it possible unless one uses type-casts (on the VMT pointer in this case) ...
And again, I don't recommend this for this dispatcher. As I said, I can't see this index doing more than a `case' statement (if it does, then please explain ...), and writing one `case' statement is a small job (one time, and probably mostly automatic), compared to getting mysterious crashes etc. due to the lack of type-checking (and that for possibly every user of the library).
It can do more than a case statement. In fact, it can save one from having to use a whole load of case statements (which is what I had to do in my OOP framework, and I could have avoided all that, had this facility been available). Look at this rough (and probably defective, since I haven't really thought this through) example:
Type TMethodType = Procedure (Sender : pObject);
So, you don't make it dependent only on the programmer's "discipline", but also on the internals (here, calling conventions of methods). If they ever change, this will just break.
Procedure Dispatcher (Sender : pObject; index: integer); Var p : TMethodType; begin If index <> 0 then begin p := GetMethodPtr (GetVMT (Sender), index); If Assigned (p) then p (Sender) else {raise an exception, be silent, or whatever} end; end;
If I could have done this (or anything vaguely resembling it), my life would have been a lot easier when I was working on the OOP framework. Yes, this would have been type unsafe, etc., but it would only have been done in one place (the above dispatcher would be at the lowest level), and all that the user of the framework needs to know is that dynamic methods in the framework must have the format; procedure foo (Sender : pObject); virtual index_number;
Which means precisely that the type-unsafety is *not* in one place, but everywhere!
Everything else is taken care of at a lower level, and users will not need to worry about producing case statements in subclasses, or whatever.
That's true for my suggestion. The `case' statement is in the library (in the lowest subclass for which this mechanism applies, AFAIUI). In contrast, in your suggestion the users *do* have to worry (about using the types, and any mistake will lead to obscure problems).
The above example of course presupposes that one can readily access the VMT and can retrieve various things for it (even if it would mean some hackery or dirty tricks).
In contrast, what's the alternative: Beside the `case' statement, the only thing it does it to check for missing methods. There are also better ways:
a) If every object type that uses the mechanism is supposed to implement all methods the dispatcher handles (directly or inherited), then one should use `abstract' methods. In this case, the compiler can check at compile time that all methods are implemented which is, of course, much better than a runtime check.
b) Otherwise, the base type can provide methods that abort, so the virtual method mechanism will handle the rest.
So you need in a) a `case' statement and abstract method declarations or b) a `case' statement, virtual method declarations and aborting implementations. All of them can be produced fully automatic, given the list of codes. For that, you get safe and probably more efficient code.
Frank
On 25 Apr 2003 at 2:27, Frank Heckenbach wrote:
[...]
Sure, storing them is easy. The question is what to do with them afterwards?
That can be left to the programmer to do something with it, or to do nothing with it. Never underestimate the ability of programmers to do something with something ;-/
Sorry, such blanket statements don't impress me.
Each to his own - but I don't think anyone person can presume to know what any number of other people could do with any particular feature in a piece of software. Of course one may know what one would prefer for others not to do - but in the end, people will do what they choose/need to do. So, if you can store the integer values, please store them. Others can then decide for themselves what (if any) they want to do with it.
[...]
Type TMethodType = Procedure (Sender : pObject);
So, you don't make it dependent only on the programmer's "discipline", but also on the internals (here, calling conventions of methods).
I am not sure what the objection is here. This is precisely how BP's OWL framework and Delphi's VCL framework operate. You might not like it, and it might prove restrictive for programmers who would like to do things differently, but BP and Delphi programmers have been coping quite well for a very long time.
If they ever change, this will just break.
It never changed in BP's OWL, and it will never change in Delphi's VCL. I am not expecting mine to change either. In any case, anything can change in any library, OOP framework, or software. If that happens, people have the option of not upgrading to the new version (just like some BP programmers decided to ignore Delphi), or making the necessary changes in their code. This is not rocket science, and it happens everywhere - in gcc, gpc, and many other things.
[....]
So you need in a) a `case' statement and abstract method declarations or b) a `case' statement, virtual method declarations and aborting implementations. All of them can be produced fully automatic, given the list of codes.
You cannot have a list of all possible codes in advance. In BP or Delphi, all I need to do is: Procedure foo (Var Message : TMessage); virtual my_integer;
"my_integer" can be any integer value that I choose (and of course there is no way for Borland to predict what it will be). Yet I know that whenever the user clicks on or selects a control or menu item with the ID of "my_integer", then my method "foo" will be executed. I don't have to bother myself with a case statement, or anything other than to have my method in the format described above. I am not sure that I understand what is so objectionable about this.
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 25 Apr 2003 at 2:27, Frank Heckenbach wrote:
[...]
Sure, storing them is easy. The question is what to do with them afterwards?
That can be left to the programmer to do something with it, or to do nothing with it. Never underestimate the ability of programmers to do something with something ;-/
Sorry, such blanket statements don't impress me.
Each to his own - but I don't think anyone person can presume to know what any number of other people could do with any particular feature in a piece of software.
Congratulations! You have just found a reason to include any feature at all into any programming language.
Seriously, when discussing the merits of some feature, we must at least consider it concretely. Just saying someone may be able to do something with it is pointless.
Of course one may know what one would prefer for others not to do - but in the end, people will do what they choose/need to do. So, if you can store the integer values, please store them. Others can then decide for themselves what (if any) they want to do with it.
Why don't they compile the application with debug info and read the necessary information from there at runtime? Sure, this is fragile and system-dependent and perhaps more or less undocumented and what not, but it's the same if I just store the values in the VMT, without any guarantee of where and how to access them or whether it may change anytime.
The point is that there is no documented way to retrieve them (at least not AFAIK -- the BP documentation doesn't contain any, and you didn't tell me any so far either).
[...]
Type TMethodType = Procedure (Sender : pObject);
So, you don't make it dependent only on the programmer's "discipline", but also on the internals (here, calling conventions of methods).
I am not sure what the objection is here. This is precisely how BP's OWL framework and Delphi's VCL framework operate. You might not like it, and it might prove restrictive for programmers who would like to do things differently, but BP and Delphi programmers have been coping quite well for a very long time.
Yes, BP and Delphi do much other dirty stuff as well. In almost all cases I've seen so far, there was a clean, "Pascalish" and easier to use alternative available. E.g., dynamic sized memory allocation with `GetMem' and `SizeOf' vs. schemata. Or how about being able to pass local routines as procedural parameters only by using hand-written assembler code (as they do in at least one of their famous class libraries, quite likely in each of time), because doing it in plain Pascal (which is according to classic Pascal, not even EP) doesn't work in BP ...
My impression is that they're just too lazy to implement the nontrivial features of Pascal. (No news so far, I know.) But even if they find they'd need them in their own code, they do not finally implement them, but invent some dirty work-around (which then spreads to their users and spoils many more people's programming habits).
I hope you understand now why I'm so allergic to this "that's how Borland does it" argument. When discussing the usefulness of some feature, more often than not that's an indication of bad design it seems ...
If they ever change, this will just break.
It never changed in BP's OWL, and it will never change in Delphi's VCL.
Uhm, the calling convention is defined by the compiler, not the library. When it changes in the compiler (or some other compiler uses another one), the library will silently break. Apparently you don't mind code that silently breaks, but for me that's one of the worst kind of bugs.
I am not expecting mine to change either. In any case, anything can change in any library, OOP framework, or software. If that happens, people have the option of not upgrading to the new version (just like some BP programmers decided to ignore Delphi),
Apparently they don't mind to create some dead-ends (like so much proprietary software). In contrast, we should try to design our features in a future-proof way, so that they will not force some people to keep using old versions when something changes ...
or making the necessary changes in their code.
After probably some hours of debugging to find out what went wrong, and a number of "bug" reports sent to the wrong places ...
This is not rocket science, and it happens everywhere - in gcc, gpc, and many other things.
Fortunately it isn't rocket science. Anyone who programs rockets (or any real machinery) with such fragile code will not keep their jobs very long I hope.
"my_integer" can be any integer value that I choose (and of course there is no way for Borland to predict what it will be). Yet I know that whenever the user clicks on or selects a control or menu item with the ID of "my_integer", then my method "foo" will be executed. I don't have to bother myself with a case statement, or anything other than to have my method in the format described above. I am not sure that I understand what is so objectionable about this.
- It's type unsafe. As I said a lot of times now (and would like to repeat again and again), type-unsafety means, sooner or later, mysterious crashes and some nice time spent with the debugger (low-level, even).
- The mechanism to access them via procedural types (as you described, and I don't know a better way) adds another type-unsafety. (Though at least this one, in constrast to the first one, might affect only the library author, not every user of it.)
- It's probably less efficient. I didn't check exactly (which might require debugging such a program on the assembler level), but in the documentation there are some indications that they use a linear search loop whose time used is roughly proportional to the number of indices that exist (whereas a `case' statement can run in constant time). If there are only a few indices, it doesn't matter, but AFAIUI, there are usually a lot of them, so the speed difference might be significant.
- They apparently use an undocumented feature to retrieve the information (at least BP doesn't have a `GetMethodPtr', don't know about Delphi).
- Actually, it's not only only undocumented, but contrary to the documentation. According to the documentation of BP 7 the only difference to virtual methods is that dyanamic methods may save memory. (So if someone takes the documentation for what it says, they might choose to remove the indices because they don't care about memory usage and they feel the code becomes more readable without them or want it to run a little faster or for whatever reason, and the code will break, altough according to the documentation it should behave the same -- only perhaps run faster and take more memory.)
Enough for a start? ;-)
I suppose a more useful feature for this goes in the direction of procedural types for object methods (which may have some nontrivial aspects, which may be why they didn't choose to implement them).
I haven't checked the details -- if they don't suffice it may be necessary to do some pre-compiling step (which, e.g. scans the source code for those IDs and generates the `case' statement or whatever). With their RAD tools, it would probably be easy (since it works pre-compile, anyway). For a non-RAD programming environment, it may be a little new (though it could be mostly hidden from the user as part of their "automake" mechanism). Even if there would be a little inconvenience (such as slightly longer build time), that's still much better than using an inherently unsafe mechanism.
Frank
On 26 Apr 2003 at 18:42, Frank Heckenbach wrote:
[...]
Seriously, when discussing the merits of some feature, we must at least consider it concretely. Just saying someone may be able to do something with it is pointless.
I think I have already stated what *I* would like to do with it, and why. The reason why my OWL-compatible framework is not really OWL- compatible is because I could not produce a compatible dispatch mechanism for dynamic methods, but had to use case statements. Thus the solution which you espouse adds nothing to what I am trying to do, since that is the one I already use - and it is not OWL-compatible. Whether the OWL mechanism is good or not is not the point when the goal is to produce a framework that is compatible with it. Implementing dynamic methods wherein the indices are ignored and cannot be utilised to achieve my goal does not really advance the matter very much (other than avoiding the need to change method declarations in existing OWL code).
[...]
The point is that there is no documented way to retrieve them (at least not AFAIK -- the BP documentation doesn't contain any, and you didn't tell me any so far either).
No, there is no documented way to retrieve them. That just means that we have the freedom to choose. The choices are to provide nothing, or to provide a clean way. If the former is chosen, then one might as well not bother trying to implement the feature. This is a supposedly BP- compatible feature and it is clear that, in BP, there is a way to access the information, even if they chose not to document it (of course anyone with source OWL code will see how they did it - but I cannot let myself look at - it for obvious reasons).
[...]
I hope you understand now why I'm so allergic to this "that's how Borland does it" argument.
No - especially not at the level of a BP-compatible feature. If the aim is to be compatible, then how they did it is relevant. There may be "better" (in someone's opinion) ways of doing it - and we all have the benefit of hindsight so as to be able to avoid pitfalls if possible. fine. Even at the general level of the usefulness of a proposed feature, it is still relevant to discuss how someone else has done it - not for the purpose of following it slavishly, but to show that it can be done and has been done, and then to see if and how it can be done better.
Whether or not to implement a BP-compatible feature is one thing - but once the decision has been made, then implementing a BP-compatible feature that is not really BP-compatible seems odd.
[...]
Apparently they don't mind to create some dead-ends (like so much proprietary software). In contrast, we should try to design our features in a future-proof way, so that they will not force some people to keep using old versions when something changes ...
In this case, there is no dead end at all. OWL can be ported to Delphi without too much pain, and the dispatching mechanism for dynamic methods that we are discussion is already present in Delphi.
In the event, it is up to you what you choose to implement in the compiler. I have nothing further to add to this discussion. It seems that discussions on implementing BP-compatible features often lead to endless and pointless debates that often involve Borland-bashing. Perhaps if you want to implement a new BP-compatible feature, you should just do it however you see fit, instead of inviting comments, since you clearly have very definite views on these things, and you are obviously unimpressed with Borland's choices on how to do them.
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:
[...]
The point is that there is no documented way to retrieve them (at least not AFAIK -- the BP documentation doesn't contain any, and you didn't tell me any so far either).
No, there is no documented way to retrieve them. That just means that we have the freedom to choose. The choices are to provide nothing, or to provide a clean way.
So why not try to design a clean way? The best solution to me so far seems be a utility that is run before compilation that scans the source code for dynamic methods and creates the `case' statement. This would be completely external of GPC, BTW.
If you have any other ideas for a clean way, we can discuss them.
If the former is chosen, then one might as well not bother trying to implement the feature. This is a supposedly BP- compatible feature
Not only supposedly. As it is now, ignoring the index, it is compatible to the documented BP behaviour, let's not forget this.
and it is clear that, in BP, there is a way to access the information, even if they chose not to document it (of course anyone with source OWL code will see how they did it - but I cannot let myself look at - it for obvious reasons).
Perhaps you can find someone to look at it and just describe this particular thing (in words, of course, not copying the code).
Actually, this might be an interesting point: If we implement some compatible feature by reading the documentation and finding out ourselves how to implement it, that's a clean reimplementation, AFAIK. But if it requires looking at their source code to find out what some feature does at all, we must be very careful not to get into copyright problems ...
[...]
I hope you understand now why I'm so allergic to this "that's how Borland does it" argument.
No - especially not at the level of a BP-compatible feature. If the aim is to be compatible, then how they did it is relevant.
I hold that it's not relevant *how* they did it, but *what* the feature actually achieves. E.g., if we'd decide to implement such a dispatcher with a faster binary search or a table lookup (compared to the linear search that BP probably uses), that would still be compatible.
There may be "better" (in someone's opinion) ways of doing it - and we all have the benefit of hindsight so as to be able to avoid pitfalls if possible.
It really doesn't take much hindsight. Whenever type-casts are needed (whether in the library or in the, perhaps hidden, dispatcher), that's an indication that there might be a design problem. (Which is not to say that I never use type-casts myself, but when I do, I'm well aware that what I do is probably not the best thing possible.)
In the event, it is up to you what you choose to implement in the compiler. I have nothing further to add to this discussion. It seems that discussions on implementing BP-compatible features often lead to endless and pointless debates that often involve Borland-bashing. Perhaps if you want to implement a new BP-compatible feature, you should just do it however you see fit, instead of inviting comments, since you clearly have very definite views on these things, and you are obviously unimpressed with Borland's choices on how to do them.
Yes -- because these are often quite bad choices (in many of those things we've discussed -- of course, there are other ones, but most of them have been implemented some time ago with much discussion; naturally, it's the controversial areas that cause most discussion).
Note that I haven't said I dislike this feature just because it's Borland's. I gave some concrete arguments which have not been rebutted.
When I started the discussion, I wasn't aware of these issues. I thought it was a trivial thing to do. Now at least I know there's more to it.
I've done the easy thing (i.e., the documented behaviour) now, and put the other thing back on the to-do list.
Frank
I just fixed a bug in the Mac interfaces where I had accidently put:
type SInt8 = Integer(8); SInt8Ptr = ^SInt8Ptr;
Needless to say that second line should be SInt8Ptr = ^SInt8;
I'm not sure what Pascal strictly requires in this case, but it seems an error would be indicated. CW does give an error in this case. Certainly the type is pretty dubious.
It is a handy definition for really opaque types though! Peter.
Peter N Lewis wrote:
I just fixed a bug in the Mac interfaces where I had accidently put:
type SInt8 = Integer(8); SInt8Ptr = ^SInt8Ptr;
Needless to say that second line should be SInt8Ptr = ^SInt8;
I'm not sure what Pascal strictly requires in this case, but it seems an error would be indicated. CW does give an error in this case. Certainly the type is pretty dubious.
Any references to why it might be wrong in Pascal? Generally, pointers may be used before declaration within one type declaration part.
Frank
Does anyone have any idea what this means:
#### #### FDE 0x2aea4 (OB 0x13fd60, fde_end 0x2affc) has zero length!Aborting. #### Abort
I am trying to use the Authorization code with GPC. If I write the code in C, it works fine. If I write the code in GPC, it works unless the user cancels the authorization dialog, in which case it gets the above error.
I'm running out of ideas as to what the problem might be.
I found this reference:
Index: unwind-dw2-fde.c =================================================================== RCS file: /cvs/Darwin/src/live/gcc3/gcc/unwind-dw2-fde.c,v retrieving revision 1.30 retrieving revision 1.31 diff -u -r1.30 -r1.31 --- unwind-dw2-fde.c 2002/09/10 20:53:31 1.30 +++ unwind-dw2-fde.c 2002/11/07 20:33:20 1.31 @ @-1,5 +1,5 @ @ /* Subroutines needed for unwinding stack frames for exception handling. */ -/* Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. +/* Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. Contributed by Jason Merrill <jason@ cygnus.com>.
This file is part of GCC. @ @-38,203 +38,7 @ @#include "unwind-dw2-fde.h" #include "gthr.h" #endif -/* APPLE LOCAL declare string functions */ -#include <string.h>
-/* APPLE LOCAL begin fde end extension */ -#ifdef DWARF2_OBJECT_END_PTR_EXTENSION -static inline int VALID_FDE_P(struct object *OB, struct dwarf_fde *FDE) -{ - if ((char *)FDE < OB->dwarf_fde_end) - { - if (FDE->length <= 0) - { - printf ("####\n#### FDE %p (OB %p, fde_end %p) has zero length!" - "Aborting.\n####\n", FDE, OB, OB->dwarf_fde_end); - abort(); - } - else - return 1; - } - return 0; -} -#else -#define VALID_FDE_P(OB, FDE) ((FDE)->length != 0) -#endif -/* APPLE LOCAL end fde end extension */
Which seems to indicate that the code has been removed from gcc and it is related to unwiding the stack somehow, perhaps exception handling?
It's possible I've badly translated the Authorization[Tags].h files, but I'm getting a bit desperate for an idea on where the problem might lie, so if anyone has any clues, that'd be appreciated.
Thanks, Peter.
Peter N Lewis wrote:
Does anyone have any idea what this means:
#### #### FDE 0x2aea4 (OB 0x13fd60, fde_end 0x2affc) has zero length!Aborting. #### Abort
I am trying to use the Authorization code with GPC. If I write the code in C, it works fine. If I write the code in GPC, it works unless the user cancels the authorization dialog, in which case it gets the above error.
I'm running out of ideas as to what the problem might be.
I found this reference:
[...]
Which seems to indicate that the code has been removed from gcc
It is not present in the official gcc AFAICS. Is it part of the Apple patches (and was removed from them now)? Did you try removing it in your copy as well?
and it is related to unwiding the stack somehow, perhaps exception handling?
According to the comment in this file, yes. But GPC doesn't use EH. So either the code is also used for nonlocal gotos etc., or the Authorization code uses EH?
It's possible I've badly translated the Authorization[Tags].h files, but I'm getting a bit desperate for an idea on where the problem might lie, so if anyone has any clues, that'd be appreciated.
No real idea. Unless it's a problem with the code itself (which might be why it was removed), it could be any kind of memory corruption or whatever (which could, among other things, be due to a wrong translation in the header, such as a pointer/value (e.g., array) mixup). But I can't really tell. It might take a debugger session ...
Frank
Ok, I'm still confused on this, but here is an interesting data point.
If I build my Pascal code as a unit and a .o, and then have a trivial C main that calls it, that works and does not have the problem.
But if I use GPC with a trivial main that calls it, then I get the error. Perhaps it is something to do with the way GPC sets up the initial stack frame as compared to GCC or some such? Here are my two commands:
With cc, this works:
cc -I/Developer/Headers/FlatCarbon -flat_namespace -undefined suppress -framework Carbon -framework ApplicationServices -framework Security -o build/Installer Sources/Main.c build/Installer.o
With GPC, this fails:
gpc -o installer -Wl,-framework,Carbon,-framework,Security,-framework,ApplicationServices Main.pas ../build/installer.o ../build/Authorization.o ../build/AuthorizationTags.o /Developer/Pascal/GPCPInterfaces/GPCMacOSAll.o /Developer/Pascal/GPCPInterfaces/GPCStringsAll.o
Note that with GPC I have to include all the "empty" .o files which are otherwise unnecessary. Beats me if that is related to the issue.
So it seems there is some issue about the ay GPC initializes as compared to the way cc initializes that is conflicting some how.
I tried gdb, but it didn't tell me anything about the problem (at least nothing I could understand).
For now I have a not very good workaround (since I think this will fail if I try to actually use any of the GPC runtime stuff), but at least I can move on, I've spent the last several days trying to figure this out :-( If I hadn't got it working this morning, I'd have had to spend the rest of the day converting the .pas file to C :-(
Oh well, perhaps this will help shed some light on the subject. Perhaps once I can use that --longjmp-all-nonlocal-labels flag that'll make a difference.
Thanks, Peter.
Peter N Lewis wrote,
Does anyone have any idea what this means:
#### #### FDE 0x2aea4 (OB 0x13fd60, fde_end 0x2affc) has zero length!Aborting. #### Abort
I am trying to use the Authorization code with GPC. If I write the code in C, it works fine. If I write the code in GPC, it works unless the user cancels the authorization dialog, in which case it gets the above error.
I'm running out of ideas as to what the problem might be.
I found this reference:
Index: unwind-dw2-fde.c
RCS file: /cvs/Darwin/src/live/gcc3/gcc/unwind-dw2-fde.c,v retrieving revision 1.30 retrieving revision 1.31 diff -u -r1.30 -r1.31 --- unwind-dw2-fde.c 2002/09/10 20:53:31 1.30 +++ unwind-dw2-fde.c 2002/11/07 20:33:20 1.31 @ @-1,5 +1,5 @ @ /* Subroutines needed for unwinding stack frames for exception handling. */ -/* Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. +/* Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. Contributed by Jason Merrill <jason@ cygnus.com>.
This file is part of GCC. @ @-38,203 +38,7 @ @#include "unwind-dw2-fde.h" #include "gthr.h" #endif -/* APPLE LOCAL declare string functions */ -#include <string.h>
-/* APPLE LOCAL begin fde end extension */ -#ifdef DWARF2_OBJECT_END_PTR_EXTENSION -static inline int VALID_FDE_P(struct object *OB, struct dwarf_fde *FDE) -{
- if ((char *)FDE < OB->dwarf_fde_end)
- {
if (FDE->length <= 0)
{
printf ("####\n#### FDE %p (OB %p, fde_end %p) has zero
length!"
"Aborting.\n####\n", FDE, OB, OB->dwarf_fde_end);
abort();
}
else
return 1;
- }
- return 0;
-} -#else -#define VALID_FDE_P(OB, FDE) ((FDE)->length != 0) -#endif -/* APPLE LOCAL end fde end extension */
Which seems to indicate that the code has been removed from gcc and it is related to unwiding the stack somehow, perhaps exception handling?
It's possible I've badly translated the Authorization[Tags].h files, but I'm getting a bit desperate for an idea on where the problem might lie, so if anyone has any clues, that'd be appreciated.
After a lot af experimenting, we found that the problem was related to linking-in libgpc.a and/or large parts libgcc.a. Peter finally asked the maintainers of the gcc-exception handling code:
I am trying to track down a problem we are experiencing with GNU Pascal/Mac OS X. Specifically, when the Mac OS X authorization system is called to ask for the administrator password, and the cancel button is pressed, instead of returning normally with an error, the program is aborted with the following message:
#### #### FDE 0x2aea4 (OB 0x140880, fde_end 0x2affc) has zero length!Aborting. #### Abort
Similar programs compiled in gcc do not have this problem. As near as I can understand it, the authorization system is throwing an exception, and instead of it being caught as normal, the exception system is detecting an error condition and aborting the program. The problem seems to be the existence of some DWARF "object" that has a negative FDE length. I don't actually know what that last sentence really means though.
Is there any light you could shed on this issue to help us figure out what GNU Pascal might be doing that could induce this error?
Thanks, Peter.
The answer came from Geoffrey Keating:
You have out-of-date exception handling support (libgcc.a and crt2.o). Try upgrading the base compiler to GCC 3.3.
Peter N Lewis wrote:
Would this problem likely be fixed with GCC 3.3 even if the authorization code in the system remains as it is?
Geoffrey Keating wrote:
Yes, if you upgrade the compiler that's part of GNU Pascal to GCC 3.3, GNU Pascal will stop producing the invalid exception-handling layout.
My conclusion is that Apple has patched-gcc exception handling in its own version of gcc, used to compile the system software that causes the exception. The patch is not compatible with fsf-gcc-3.2.x but will be compatible with fsf-gcc-3.3. Many Apple-specific gcc-patches are now being merged into standard-fsf-gcc. For example, Apple has donated precompiled header support that will be part of fsf-gcc-3.4. The merge is good news, as it enables us to stay with fsf-gcc for the Mac OS X port of gpc.
Regards,
Adriaan van Os
Could someone explain o me what CString is in the RTS?
It seems it is a pointer, and the docs somewhere describe it as ^Char, and it is generally used as a pointer, but some places it is used as an array, as in Dest[Size] := #0;
Since there seems good support for CString in string.pas, I'm trying to figure out if I can use it in the Mac Interfaces, if it is binary compatible with the Mac's definition of CStringPtr = ^Char;
BTW, I noticed in string.pas
if (s2 = nil) or (s2^ = #0)
I know GPC defaults to always doing short circuit, but would that be better written or_else?
Thanks, Peter.
Intuition tells me that CString is a 'C' string, as in the programming language 'C' (or a 'char *' if you want the C type definition). In C, these can be treated as either pointers or as character arrays (to cut a long story short). They are terminated with a \0 (if used correctly, that is: one of the "standard" buggy things to do in C is lose the terminating \0 which can cause all sorts of trouble. The Dest[size] := #0 example you gave is doing the "safe" thing of insisting that the string be terminated correctly.).
Its almost certainly compatible with Apple's CStringPtr = ^char ; as the point of these is C compatibility usually...
Usually characters are byte-sized in C. There are also wide characters, but that's another story again...
But we can want for Frank's definitive answer and the usual nitpicking of my sloppy answer :-|
Grant
At 6:32 AM +0000 29/4/03, Peter N Lewis wrote:
Could someone explain o me what CString is in the RTS?
It seems it is a pointer, and the docs somewhere describe it as ^Char, and it is generally used as a pointer, but some places it is used as an array, as in Dest[Size] := #0;
Since there seems good support for CString in string.pas, I'm trying to figure out if I can use it in the Mac Interfaces, if it is binary compatible with the Mac's definition of CStringPtr = ^Char;
BTW, I noticed in string.pas
if (s2 = nil) or (s2^ = #0)
I know GPC defaults to always doing short circuit, but would that be better written or_else?
Thanks, Peter.
-- http://www.interarchy.com/ http://documentation.interarchy.com/
Grant Jacobs wrote:
Intuition tells me that CString is a 'C' string, as in the programming language 'C' (or a 'char *' if you want the C type definition).
Yes. And also compatible to BP (there called `PChar' which GPC also accepts).
In C, these can be treated as either pointers or as character arrays (to cut a long story short). They are terminated with a \0 (if used correctly, that is: one of the "standard" buggy things to do in C is lose the terminating \0 which can cause all sorts of trouble. The Dest[size] := #0 example you gave is doing the "safe" thing of insisting that the string be terminated correctly.).
Its almost certainly compatible with Apple's CStringPtr = ^char ; as the point of these is C compatibility usually...
Usually characters are byte-sized in C. There are also wide characters, but that's another story again...
But we can want for Frank's definitive answer and the usual nitpicking of my sloppy answer :-|
That's correct. Also GPC automatically "converts" strings to CStrings (by appending to #0 when necessary and taking the address). But it doesn't (usually) copy the string, so the CString will remaing valid only as long as the string is not modified. Usually this is used to pass string values to C routines (which should not modify the CString, either).
Peter N Lewis wrote:
BTW, I noticed in string.pas
if (s2 = nil) or (s2^ = #0)
I know GPC defaults to always doing short circuit, but would that be better written or_else?
Call it laziness, but there are many such places. I think the code becomes more readable (and a little easier to write when one doesn't have to consider whether `or' can be safely used or `or_else' must be used) ...
Frank
Peter N Lewis wrote:
Could someone explain o me what CString is in the RTS?
It is the same thing as BP and Delphi's "pChar" (aka a null- terminated string). It is used mainly to interface to C library (and, for example, Windows and OS/2 API) routines, and can often be used where the C library expects a "char *" or anything of the kind, or where the WinAPI expects "LPCSTR" or any other variation of that. The "strings" and Sysutils units contain a number of routines for dealing with such null-terminated strings.
It seems it is a pointer, and the docs somewhere describe it as ^Char, and it is generally used as a pointer, but some places it is used as an array, as in Dest[Size] := #0;
Yes. You can do this in BP/Delphi - but I am not sure whether it is inherent in the type or whether GPC just allows this to be compatible with BP.
BTW: a CString/pChar is type-compatible with zero-based arrays of char (e.g., var foo : array [0..1023] of char). I think you might have to use {$X+} in your code for this - but I don't remember (I always use it anyway).
Since there seems good support for CString in string.pas, I'm trying to figure out if I can use it in the Mac Interfaces, if it is binary compatible with the Mac's definition of CStringPtr = ^Char;
I don't know about "binary" but I would have thought it would be compatible.
BTW, I noticed in string.pas
if (s2 = nil) or (s2^ = #0)
I know GPC defaults to always doing short circuit, but would that be better written or_else?
Dunno. I think that expression is one of the safest ways of dealing with pChar variables. I don't know what "or_else" means or how it differs from "or".
Best regards, The Chief --------- Prof. Abimbola Olowofoyeku (The African Chief) Web: http://www.bigfoot.com/~african_chief/
"Prof. A Olowofoyeku (The African Chief)" wrote:
Peter N Lewis wrote:
... snip ...
BTW, I noticed in string.pas
if (s2 = nil) or (s2^ = #0)
I know GPC defaults to always doing short circuit, but would that be better written or_else?
Dunno. I think that expression is one of the safest ways of dealing with pChar variables. I don't know what "or_else" means or how it differs from "or".
It is not safe in Standard Pascal, nor even in Turbo (which can be configured for complete evaluation) because the s2^ component may be dereferenced. It is necessary to write it as:
IF s2 <> nil THEN BEGIN IF s2^ <> chr(0) THEN (* whatever *) ELSE (* handle empty string *) END ELSE (* handle nil pointer case *)
with attendant complications. the OR_ELSE was introduced to ensure short circuit evaluation, and allow the simpler sort of expression you are using.
At 2:35 PM +0100 29/4/03, Prof. A Olowofoyeku (The African Chief) wrote:
BTW: a CString/pChar is type-compatible with zero-based arrays of char (e.g., var foo : array [0..1023] of char). I think you might have to use {$X+} in your code for this - but I don't remember (I always use it anyway).
So basically, CString is ^Char, but why then can it be accessed as an array?
Can any other pointers be accessed as arrays? Is this only for $X+?
if (s2 = nil) or (s2^ = #0)
I know GPC defaults to always doing short circuit, but would that be better written or_else?
Dunno. I think that expression is one of the safest ways of dealing with pChar variables. I don't know what "or_else" means or how it differs from "or".
or_else always does short circuit evaluation.
Call it laziness, but there are many such places. I think the code becomes more readable (and a little easier to write when one doesn't have to consider whether `or' can be safely used or `or_else' must be used) ...
Fair enough, I just get nervous whenever I see if (s <> nil) or (s^ ...). In CW Pascal, they use | and & for short circuit evaluation - anything is better than plain or in my book which no Pascal standard ever defined to short circuit (or even left to right evaluation I think?). Peter.
Peter N Lewis wrote:
At 2:35 PM +0100 29/4/03, Prof. A Olowofoyeku (The African Chief) wrote:
BTW: a CString/pChar is type-compatible with zero-based arrays of char (e.g., var foo : array [0..1023] of char). I think you might have to use {$X+} in your code for this - but I don't remember (I always use it anyway).
So basically, CString is ^Char, but why then can it be accessed as an array?
BP compatibility (which in this case means C compatibility)-: ...
Can any other pointers be accessed as arrays?
Nope (at least not intentionally).
Is this only for $X+?
Yes (if you find another case, report it as a bug).
Call it laziness, but there are many such places. I think the code becomes more readable (and a little easier to write when one doesn't have to consider whether `or' can be safely used or `or_else' must be used) ...
Fair enough, I just get nervous whenever I see if (s <> nil) or (s^ ...). In CW Pascal, they use | and & for short circuit evaluation - anything is better than plain or in my book which no Pascal standard ever defined to short circuit (or even left to right evaluation I think?).
No standard indeed, AFAIK. But BP and GPC define it so if the short circuit option is enabled (which it is in GPC by default, and I think also in BP).
Frank
On Wed, 30 Apr 2003, Frank Heckenbach wrote:
Peter N Lewis wrote:
At 2:35 PM +0100 29/4/03, Prof. A Olowofoyeku (The African Chief) wrote:
BTW: a CString/pChar is type-compatible with zero-based arrays of char (e.g., var foo : array [0..1023] of char). I think you might have to use {$X+} in your code for this - but I don't remember (I always use it anyway).
So basically, CString is ^Char, but why then can it be accessed as an array?
BP compatibility (which in this case means C compatibility)-: ...
Can any other pointers be accessed as arrays?
Nope (at least not intentionally).
[..]
Would it be less confusing if CString was defined as an array accessed by a pointer of the same name?
Russ
Russell Whitaker wrote:
Would it be less confusing if CString was defined as an array accessed by a pointer of the same name?
It wouldn't work because it is a pointer. (BTW, how big should the array be, otherwise?)
Frank
Peter N Lewis wrote:
Prof. A Olowofoyeku (The African Chief) wrote:
BTW: a CString/pChar is type-compatible with zero-based arrays of char (e.g., var foo : array [0..1023] of char). I think you might have to use {$X+} in your code for this - but I don't remember (I always use it anyway).
So basically, CString is ^Char, but why then can it be accessed as an array?
Can any other pointers be accessed as arrays? Is this only for $X+?
if (s2 = nil) or (s2^ = #0)
I know GPC defaults to always doing short circuit, but would that be better written or_else?
Dunno. I think that expression is one of the safest ways of dealing with pChar variables. I don't know what "or_else" means or how it differs from "or".
or_else always does short circuit evaluation.
Call it laziness, but there are many such places. I think the code becomes more readable (and a little easier to write when one doesn't have to consider whether `or' can be safely used or `or_else' must be used) ...
Fair enough, I just get nervous whenever I see if (s <> nil) or (s^ ...). In CW Pascal, they use | and & for short circuit evaluation - anything is better than plain or in my book which no Pascal standard ever defined to short circuit (or even left to right evaluation I think?).
Didn't I just point out that this is insecure, even on Turbo Pascal?
On Wed, Apr 30, 2003 at 07:08:10AM +0000, Peter N Lewis wrote:
At 2:35 PM +0100 29/4/03, Prof. A Olowofoyeku (The African Chief) wrote:
BTW: a CString/pChar is type-compatible with zero-based arrays of char (e.g., var foo : array [0..1023] of char). I think you might have to use {$X+} in your code for this - but I don't remember (I always use it anyway).
So basically, CString is ^Char, but why then can it be accessed as an array?
Can any other pointers be accessed as arrays? Is this only for $X+?
if (s2 = nil) or (s2^ = #0)
I know GPC defaults to always doing short circuit, but would that be better written or_else?
Dunno. I think that expression is one of the safest ways of dealing with pChar variables. I don't know what "or_else" means or how it differs from "or".
or_else always does short circuit evaluation.
Call it laziness, but there are many such places. I think the code becomes more readable (and a little easier to write when one doesn't have to consider whether `or' can be safely used or `or_else' must be used) ...
Fair enough, I just get nervous whenever I see if (s <> nil) or (s^ ...). In CW Pascal, they use | and & for short circuit evaluation - anything is better than plain or in my book which no Pascal standard ever defined to short circuit (or even left to right evaluation I think?).
GPC guarantees short-circuit left to right evaluation by default. The original example was taken from the RTS sources, which explicitly reset the default mode ({$gnu-pascal}). Anyway, compiler internals (including RTS) are by definition unportable, hence it makes no sense to insist on avoiding implementation dependent features there.
Emil
Peter.
http://www.interarchy.com/ http://documentation.interarchy.com/
Hi Frank
Actually, there is a documented way of accessing the dynamic method index. I found it in the Delphi help file, and you won't like it - the access method is via asm. In any case, below is the documentation - and you can do with it (or not) as you deem fit. In the example given, "message MYMESSAGE" is the same thing as the BP "virtual methodindex".
Here it goes:
"Two additional directives allow assembly code to access dynamic and virtual methods: VMTOFFSET and DMTINDEX.
VMTOFFSET retrieves the offset in bytes of the virtual method pointer table entry of the virtual method argument from the beginning of the virtual method table (VMT). This directive needs a fully specified class name with a method name as a parameter (for example, TExample.VirtualMethod), or an interface name and an interface method name.
DMTINDEX retrieves the dynamic method table index of the passed dynamic method. This directive also needs a fully specified class name with a method name as a parameter, for example, TExample.DynamicMethod. To invoke the dynamic method, call System.@CallDynaInst with the (E)SI register containing the value obtained from DMTINDEX.
Note
Methods with the message directive are implemented as dynamic methods and can also be called using the DMTINDEX technique. For example:
TMyClass = class procedure x; message MYMESSAGE; end;
The following example uses both DMTINDEX and VMTOFFSET to access dynamic and virtual methods:
program Project2;
type TExample = class procedure DynamicMethod; dynamic; procedure VirtualMethod; virtual; end;
procedure TExample.DynamicMethod; begin
end;
procedure TExample.VirtualMethod; begin
end;
procedure CallDynamicMethod(e: TExample); asm // Save ESI register PUSH ESI
// Instance pointer needs to be in EAX MOV EAX, e
// DMT entry index needs to be in (E)SI MOV ESI, DMTINDEX TExample.DynamicMethod // Now call the method CALL System.@CallDynaInst
// Restore ESI register POP ESI end;
procedure CallVirtualMethod(e: TExample); asm // Instance pointer needs to be in EAX MOV EAX, e
// Retrieve VMT table entry MOV EDX, [EAX]
// Now call the method at offset VMTOFFSET CALL DWORD PTR [EDX + VMTOFFSET TExample.VirtualMethod]
end;
var e: TExample;
begin e := TExample.Create; try CallDynamicMethod(e); CallVirtualMethod(e); finally e.Free; end; end. "
Then the Delphi help file provides extra information about the features of dynamic methods, as follows:
1. "Dynamic methods are virtual methods with a slightly different dispatch mechanism. Because dynamic methods don't have entries in the object's virtual method table, they can reduce the amount of memory that objects consume. However, dispatching dynamic methods is somewhat slower than dispatching regular virtual methods. If a method is called frequently, or if its execution is time-critical, you should probably declare it as virtual rather than dynamic.
Objects must store the addresses of their dynamic methods. But instead of receiving entries in the virtual method table, dynamic methods are listed separately. The dynamic method list contains entries only for methods introduced or overridden by a particular class. (The virtual method table, in contrast, includes all of the object's virtual methods, both inherited and introduced.) Inherited dynamic methods are dispatched by searching each ancestor's dynamic method list, working backwards through the inheritance tree.
To make a method dynamic, add the directive dynamic after the method declaration."
2. "Virtual versus dynamic
Virtual and dynamic methods are semantically equivalent. They differ only in the implementation of method-call dispatching at runtime. Virtual methods optimize for speed, while dynamic methods optimize for code size.
In general, virtual methods are the most efficient way to implement polymorphic behavior. Dynamic methods are useful when a base class declares many overridable methods which are inherited by many descendant classes in an application, but only occasionally overridden.
Note Only use dynamic methods if there is a clear, observable benefit. Generally, use virtual methods."
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:
Actually, there is a documented way of accessing the dynamic method index. I found it in the Delphi help file, and you won't like it - the access method is via asm. In any case, below is the documentation - and you can do with it (or not) as you deem fit. In the example given, "message MYMESSAGE" is the same thing as the BP "virtual methodindex".
Well, GPC's `asm' in incompatible to BP's -- that's known, and not likely to change. (And even if it would, such an implementation would be unnecessarily(*) nonportable by the use of asm.)
(*) In some low-level cases, such as accessing I/O hardware ports, the use of assembler code is unavoidable (though it is debatable whether it should be inlined in Pascal, or linked separately, but that's not the point here), but here we have a feature that can be described in high-level terms and in principle be implemented in high-level ways (`case' etc. ...), so the use of `asm' is an unnecessary restriction.
So, I won't do anything with it now. I still think an (automatically generated) `case' statement can work.
procedure DynamicMethod; dynamic;
This syntax seems to be new in Delphi (BP doesn't allow it). Should I just treat is as equivalent to `virtual'? (Since there's no index there, I suppose the dispatch issues don't apply here, and this time it's really equivalent, apart from memory and speed issues.)
Then the Delphi help file provides extra information about the features of dynamic methods, as follows:
- "Dynamic methods are virtual methods with a slightly different
dispatch mechanism. Because dynamic methods don't have entries in the object's virtual method table, they can reduce the amount of memory that objects consume. However, dispatching dynamic methods is somewhat slower than dispatching regular virtual methods. If a method is called frequently, or if its execution is time-critical, you should probably declare it as virtual rather than dynamic.
Objects must store the addresses of their dynamic methods. But instead of receiving entries in the virtual method table, dynamic methods are listed separately. The dynamic method list contains entries only for methods introduced or overridden by a particular class. (The virtual method table, in contrast, includes all of the object's virtual methods, both inherited and introduced.) Inherited dynamic methods are dispatched by searching each ancestor's dynamic method list, working backwards through the inheritance tree.
To make a method dynamic, add the directive dynamic after the method declaration."
That's about the same that BP 7 says. I.e., no semantic difference.
- "Virtual versus dynamic
Virtual and dynamic methods are semantically equivalent.
Ha! :-)
Frank
On 29 Apr 2003 at 15:22, Frank Heckenbach wrote:
Prof A Olowofoyeku (The African Chief) wrote:
Actually, there is a documented way of accessing the dynamic method index. I found it in the Delphi help file, and you won't like it - the access method is via asm. In any case, below is the documentation - and you can do with it (or not) as you deem fit. In the example given, "message MYMESSAGE" is the same thing as the BP "virtual methodindex".
Well, GPC's `asm' in incompatible to BP's -- that's known, and not likely to change. (And even if it would, such an implementation would be unnecessarily(*) nonportable by the use of asm.)
Agreed. There is no justification for using asm for this type of access (i.e., assuming it can be done in Pascal).
(*) In some low-level cases, such as accessing I/O hardware ports, the use of assembler code is unavoidable (though it is debatable whether it should be inlined in Pascal, or linked separately, but that's not the point here), but here we have a feature that can be described in high-level terms and in principle be implemented in high-level ways (`case' etc. ...), so the use of `asm' is an unnecessary restriction.
Agreed.
So, I won't do anything with it now. I still think an (automatically generated) `case' statement can work.
Probably - except that, in your suggestion, the utility to do this will be extraneous to GPC itself. The use of an external utility is IMHO an extra complication and overhead.
procedure DynamicMethod; dynamic;
This syntax seems to be new in Delphi (BP doesn't allow it). Should I just treat is as equivalent to `virtual'? (Since there's no index there, I suppose the dispatch issues don't apply here, and this time it's really equivalent, apart from memory and speed issues.)
Yes, it is new in Delphi - and it is only valid where you are defining a "class", rather than an "object". For example, this is accepted by Delphi:
type bar = class procedure foo; dynamic; end;
But this is not accepted: type bar = object procedure foo; dynamic; end;
Since both BP and Delphi reject it, there is a case for GPC rejecting it too. On the other hand, perhaps this is an unnecessary restriction and so GPC should accept it. I have no strong view either way.
It gets more interesting, with regard to type-safeness. Given this; type bar = class procedure foo1; message 1; procedure foo2; virtual 2; procedure foo3 (var msg : tmessage); message 3; procedure foo4 (var msg : integer); message 4; end;
Both "foo1" and "foo4" are rejected with "Error: Invalid message parameter list".
"foo2" is rejected with "Error: ';' expected but number found" (but it is accepted where "bar" is defined as "object" rather than "class").
"foo3" is accepted. There you have type unsafeness dealt with. As in BP's OWL, you *are* required to have the "var foo : tmessage" parameter. But here you get a compiler error, whereas BP would accept it without the parameter, and then the program will crash when the method is called. This might mean that the dispatcher for "message index_number" (equivalent to BP's "virtual index_number") is now implemented in the compiler and not in the OOP library - but who knows?
BTW: all this is of no relevance until when/if the "class" construct is fully supported. Indeed, whether one should copy all this behaviour is debateable.
[...]
- "Virtual versus dynamic
Virtual and dynamic methods are semantically equivalent.
Ha! :-)
Yes ...
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:
So, I won't do anything with it now. I still think an (automatically generated) `case' statement can work.
Probably - except that, in your suggestion, the utility to do this will be extraneous to GPC itself. The use of an external utility is IMHO an extra complication and overhead.
Which should be hidden from the user. For now, a Makefile is one way to do it. In the future, we can think about integrating it into the gp utility (which might be a good place for all such kinds of things).
procedure DynamicMethod; dynamic;
This syntax seems to be new in Delphi (BP doesn't allow it). Should I just treat is as equivalent to `virtual'? (Since there's no index there, I suppose the dispatch issues don't apply here, and this time it's really equivalent, apart from memory and speed issues.)
Yes, it is new in Delphi - and it is only valid where you are defining a "class", rather than an "object". For example, this is accepted by Delphi:
type bar = class procedure foo; dynamic; end;
But this is not accepted: type bar = object procedure foo; dynamic; end;
Since both BP and Delphi reject it, there is a case for GPC rejecting it too. On the other hand, perhaps this is an unnecessary restriction and so GPC should accept it. I have no strong view either way.
I don't know. When we implement `class' sometime, we'll see how similar or distinct it will be to `object' and whether it simplifies or complicates things to allow both's directives for both.
It gets more interesting, with regard to type-safeness. Given this; type bar = class procedure foo1; message 1; procedure foo2; virtual 2; procedure foo3 (var msg : tmessage); message 3; procedure foo4 (var msg : integer); message 4; end;
Both "foo1" and "foo4" are rejected with "Error: Invalid message parameter list".
"foo2" is rejected with "Error: ';' expected but number found" (but it is accepted where "bar" is defined as "object" rather than "class").
"foo3" is accepted. There you have type unsafeness dealt with. As in BP's OWL, you *are* required to have the "var foo : tmessage" parameter.
So at least this problem is dealt with (though apparently not the dispatcher, from Pascal code). However, this ties a syntax construct to one particular object class -- can't say that I particularly like this ...
But here you get a compiler error, whereas BP would accept it without the parameter, and then the program will crash when the method is called. This might mean that the dispatcher for "message index_number" (equivalent to BP's "virtual index_number") is now implemented in the compiler and not in the OOP library - but who knows?
I don't. But if it's in the compiler, it must be somehow available to the library that uses it (and this doesn't seem to be the case) ...
BTW: all this is of no relevance until when/if the "class" construct is fully supported. Indeed, whether one should copy all this behaviour is debateable.
Yes, I think, we can postpone this discussion until `class' is being implemented ...
Frank
Frank Heckenbach wrote:
... snip ...
Which should be hidden from the user. For now, a Makefile is one way to do it. In the future, we can think about integrating it into the gp utility (which might be a good place for all such kinds of things).
Which brings up a point. To me, it is entirely unclear how to use a makefile with gpc. There may well be something hidden away in the docs which I have not found. At any rate, an example using GNU make on a reasonably complex system would be useful. This should include more or less the gamut of 10206 modularization. A second example might implement Borland style units.
CBFalconer wrote:
Frank Heckenbach wrote:
... snip ...
Which should be hidden from the user. For now, a Makefile is one way to do it. In the future, we can think about integrating it into the gp utility (which might be a good place for all such kinds of things).
Which brings up a point. To me, it is entirely unclear how to use a makefile with gpc. There may well be something hidden away in the docs which I have not found. At any rate, an example using GNU make on a reasonably complex system would be useful. This should include more or less the gamut of 10206 modularization. A second example might implement Borland style units.
Either use automake, then you don't need much of a makefile at all (at least not for compilation which is done with a single GPC invocation; you might, of course, want to use a makefile for installation, building documentation and whatever).
Otherwise, do it basically like in C, i.e. compile all you units/modules and the main program with `-c' and link everything at the end. You'll have to add all dependencies yourself. For a program/unit/module that uses some unit/module, make it dependent on the object or gpi file of the latter. In case of circular unit/module dependencies, you have to compile one with `--interface-only' first (I haven't tried to teach this to a Makefile, but it should be possible using dependencies on the gpi file for the interface and the object file for the implementation or so).
In my previous example with the dispatcher, I'd still use automake, something like this (where $(SRC) contains a list of all source files):
dispatcher.pas: $(SRC) make-dispatcher -o $@ $(SRC)
myprog: $(SRC) dispatcher.pas gpc --automake ...
Frank
Sorry if this is somewhat off-topic, but I thought this might be a good place to ask (a lot of Pascal experience):
Does anyone know of a Pascal compiler which offers OOP semantics to match Objective C (or SmallTalk)? From what I have seen, the OOP semantics of most Pascal compilers seem quite different, but I am looking into merging Objective C object/method semantics into Pascal as part of a master's project, and was wondering if anyone has seen this done before.
For those who don't know, Objective C and SmallTalk have such capabilities as adding new methods to existing classes at runtime, performing late binding of method invocations (which involve a partially call-by-name syntax, where parameters after the first one are tagged), and some other interesting features.
I have not firmly decided as to whether I will try to merge these features into a local copy of GPC, or write a translator into Objective C code (which would be nice for interfacing with Cocoa under OS X, for example; the GPC route may be more portable, but would add a lot more work to the project).
If someone is aware of such a compiler already, please let me know!
Thank you.
===== ======= 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!? The New Yahoo! Search - Faster. Easier. Bingo http://search.yahoo.com