In news:comp.lang.pascal.borland, there's just a discussion about variable number/types of procedure/function parameters going on. Since Borland Pascal doesn't support this, the discussion has moved quite a bit away from BP towards gpc. That's why I'm forwarding my posting to this list. The complete thread can be seen in the mentioned newsgroup under the same subject as this mail. Perhaps there are some more ideas about this topic in the gpc community, or even some of the things below have been implemented that I don't know about. Certainly, this would rather be a long-time project, and it's not as urgent as removing bugs and implementing some other nice features...
Norsk / Hoaxers wrote (in several postings, cut together):
On Tue, 04 Mar 1997 04:46:26 GMT, rdonais@southeast.net (R.E.Donais) wrote:
Since Pascal knows the file is a text file, the programmer is not required to perform conversions on binary values. In "C", the programmer would at some point have to convert the integer to string using something like itoa().
Not explicitly call itoa(), but (s)he'd have to specify the type by a format specifier ("%d") in the format string. This, of course, provides no means of type checking, and is therefore not suited for Pascal.
I doubt that you want to write separate routines for every combination of variables that you plan to write to a text file. Yet, that's what you imply in your example. :-\
Yes, I think one has to distnguish overloading from "varargs" (allowing to pass any number and type of parameters). Many useful things can be done with overloading, but Writeln definitely needs varargs, otherwise you'd have to declare an infinite number of Writeln variants.
It was JUST an example. Allow me to give you a better one:
Load_PcxPicture(s:String;segg,off:Word; Load_PcxPicture(s:String;segg,off:Word;var Pal:Palettestructure);
I don't think it's a good example. In this case, I would pass Pal as a pointer with NIL meaning no palette. Easy to implement and easy to call. I don't think you would write two procedures with similar functionality, so in the end, you'd probably be doing something like this, anyway.
But this would be a case for default values as in C++. If you could declare something like procedure LPP(s:String;segg,off:Word;PPal:PPalettestructure value NIL); and call it with wither 3 or 4 parameters (in the former case, the 4th parameter would be implicitly NIL), that would be nice sometimes...
(Yet another feature that could be desirable!)
No, i didnt miss it at all, i just found it silly actually. You see, by implementing "variable syntax" or whatever i should call it, you dont make it like cobol, basic, fortran or whatever, you -just- make it more flexible. whats so friggin wrong about that? I really dont understand whats wrong with it.
I don't think there's anything wrong with it, but Borland will not do it. They've dropped TP, face it! So if you want these changes, don't look at TP, but perhaps at GNU Pascal. AFAIK, gpc already supports declaring and calling a procedure with variable arguments, like C - but only to allow calling C functions with varargs. There's not (yet) a way to implement such a procedure in Pascal, but the gpc developers are always looking to enhance the language, so if you've got some concrete ideas (or can even help to work on the compiler), you're always welcome to join the team.
Thats not what I where aiming at. You could make the product better, and alas get more satisfied customers.
That's how the gpc developers/maintainers feel too, I think.
As far as i can remember (this might be wrong) GNU pascal dont have oop.
By now, AFAIK, gpc has (nearly) the same OOP as TP, but doesn't implement the Object Pascal (not-yet-)standard.
To each his own. If you want overloading and procedures with variable number of parameters, then check out languages that support them.
Belive me, I have .. But I really dont like C. Its so.. what should I say.. pee->(wee&&) .. I just dont like it :)
I don't think that's valid in C (provided there's anything that's not valid in C - not sure if there is... :-> ), but I know what you mean. I'd also prefer to have the Pascal syntax and yet many of the other features.
I think that would be a bit to much trouble really. I was just presenting the idea to get a feeling of other people's thoughts on the matter.
If you're serious about it, join the gpc mailing list. I'll post a copy of this message to the list, since it's quite much about gpc.
Writeln, reset, and a whole bunch of others allready support it. it's just not any way for the -user- to define procedures like that.
and that is a shame . :)
But of course, it's much more difficult to provide a way for the user to declare and implement such procedures than to just allow some specially handled procedures like Writeln.
Works great as long as it's blindingly obvious which one is going to be called, but consider the following:
Procedure MyProc(C: Char); Begin ... End; Procedure MyProc(S: String); Begin ... End; Begin MyProc('A'); End.
In that example, you would use the c:char. I guess you would need a set of rules, but these rules are allready implemented in borland pascal, since some procedures have this. They have just stopped US from defining such procedures.
That's indeed a problem. The only predefined procedure with this particular ambiguity is (AFAICS) Write[ln], and there it doesn't matter which one is called because the result is the same. That would not be true in general if the user could define the procedures.
I see a couple of -possible- solutions to this.
One, not very good, would be to require VAR types in function overloading (thats what you called it, right?), like this: function f(var nr:byte);
This would remove the problem all together, but its not a very flexible way of doing it.
Quite restricting...
I'm curious for other suggestions. If you have some, just tell us about them.
There are problems with ambiguities, and they can be worse than here, as AME demonstrated. One has to find a way to cope with them (or restrict so much that no ambiguities remain). And of course, the rules should be relatively logical and comprehensible and implementable and ... :-)
As a side note: Doesn't FPK support function overloading?
?? It does?
AME replied:
Either FPK or GNU, I can't remember which. I can't even remember if it was fully implemented.
AFAIK, gpc supports part of the PCSX (Pascal scientific extensions) standard. I don't know this standard, and haven't used these extensions, but I think there is some kind of overloading. You might want to find out if there were any ambiguity problems involved, and how they were solved.
On Wed, 05 Mar 97 07:05:42 GMT, babis@hol.gr (Babis Athineos) wrote:
Once upon a time a programmer made a tiny little mistake: instead of writing procedure Store (var S:TStream); he wrote........ procedure Store (var S:PStream); Did you see the difference? Store procedures in TV use the stream, not the pointer. Pascal compiler would normally find the mistake. But Store procedures are not called normally. You pass the address of the procedure in a TStreamRec structure and then a TStream.Put method calls the Store procedure via the address you passed. No type checking! So everything worked OK, except that it didn't save right... (I hope I remember correctly the details of the error - it's been some years and I don't have the time to try it again...)
Did you get the point?
To me the point is that the extensions, however implemented, must still allow Pascal's strong type checking. (In this example, the type checking was annulled by assembler code, type casts or (unnecessarily) untyped pointers, I'd guess from what I know about TV.)
with variable number and type of parameters, I wouldn't normally use it (except maybe in cases like Writeln), but I would consider it useful. But if I couldn't choose (e.g. $VT-), I would say it is not a Pascal any more...
I agree fully!! It's a matter of being allowed to chose!
Sure. And gpc already has compiler switches to ensure that certain features are [not] used. All the extensions discussed here would be object to these (or additional) switches, too.
At last, I've got some suggestions for the varargs problem, too:
Apart from overloading (which I'm not talking about now), I'd suggest the following two extensions. Both of them combined would be something like "typed varargs", and allow declaring and implementing Writeln et al.:
1. Unknown number of arguments
To the procedure, this would be very much like Open Arrays (cf. TP/BP 7.0). The procedure could get their number with (something like) High, and access them (e.g.) as Parameter[i].
To the caller, however, it's much easier to write than with an Open Array. Anyone who's used these WVSPrintf (or similar) things in TP knows what I mean (declaring an array, assigning the parameters to the elements of the array and then calling the procedure with the array as an argument...)
Alternatively, it would suffice to allow "array expressions" like:
procedure varnumargs(p:array of integer); begin ... end;
var a,b,c:integer; begin varnumargs( (a,b,c,2,c-b,27) ) {^^^^^^^^^^^^^^^^ array declaration as in a "typed const"} end.
Ok, one pair of brackets more, but OTOH this allows for more than one open array in the parameter list of a procedure, like: anyproc( (...,...,...),(...,...) )
Probably both ways are useful and have their place...
2. Unknown type of an argument
Ok, there are untypes arguments in TP, but they don't allow for any type checking when used, so they're not suited here. Furthermore, the procedure doesn't get any information about the type, so this information would have to be passed separately (as in the printf format string in C), which is not acceptable.
So the type must be passed internally, and there must be a way for the procedure to check the type, and to access the parameter as its real type.
I could imagine something like the following:
procedure Write(Const a); var s:String[255]; begin IFTYPE a,Boolean then if a then WriteStr('True') else WriteStr('False') {^^^^^^^^ this is legal, since a is a Boolean now} else CASETYPE a OF Char: WriteStr(a); {Now a is a Char} String: WriteStr(a); {Here, a is a String - String would include any string type, not just String[255]} LongInt: {Since any integer type in TP can be seen as a subrange of LongInt, this includes all integer types.} begin {In this block, a is an integer} if a<0 then begin WriteStr('-'); a:=-a end; ... end; Real: ...; Extended:...; ... else Error('Cannot write variables of this type!') end end;
Note the IFTYPE statement. It would not just check for the type of a, but if successful, cast a into this type for the "then" block. Since the block can only be reached if the type matches, this is type-safe.
Similarly the CASETYPE, only for simplified writing. (Of course, the IFTYPE above could be part of the CASETYPE, I just wanted to show both ways.)
(This syntax is only tentative, it doesn't have to be called IFTYPE and CASETYPE.)
Of course, the SizeOf function would work on a and return the actual size.
I pointed to some possible problems with "similar" types (string types, subranges). There will be some problems (e.g. subranges with different sizes, esp. with reference parameters), but I think they can be solved.
Of course, it would be nice to elimiate the "else" of the CASETYPE above, and instead cause a compiler error at any attempt to call the procedure with any type not listed in CASETYPE. This would be a behaviour like the real Write. One has to investigate if and how this is possible (considering multiple, possibly nested IFTYPEs/CASETYPEs)...
Another idea is whether it is possible and/or useful to compare types of different arguments:
procedure equal(const a,b); begin IFTYPE a,b then {Compare SizeOf(a) bytes at @a and @b} else Error('Comparison of different types not allowed!') {^^^ or again, rather a compiler error...} end;
It also seems useful to declare local variables of the same type as an argument:
procedure swap(var a,b); var temp:TYPEOF(a); begin IFTYPE a,b then begin temp:=a;a:=b;b:=temp end else Error('Swapping different types not allowed!') {see above} end;
(I think, Extended Pascal (gpc???) has some kind of type inquiry, but probably only for known types. Anyway, this one could, of course, use the same syntax as EP (it's probably not the syntax above, since I don't know the EP syntax).)
Concerning the implementation, I'd say that for each "vartype" parameter an additional (invisible) parameter is passed that describes the type uniquely. Since the compiler has to keep track of all the types somehow anyway, it shouldn't be hard to generate a unique number for each type. The numbers don't have to have any other meaning except being unique, since the IFTYPE/CASETYPE comparisons would be the only things they're used for. Oh, sorry... subranges might have to be considered, but that should also be feasible...
The size (for SizeOf) would either be passed as another additional parameter (if used at all in the procedure), or be retrieved through a lookup table via the type number.
The varying size of the arguments on the stack (in the case of value parameters) also seems to be a little problem, but I think it can be solved, too. (Perhaps with the help of the SizeOf parameter as far as necessary, or by passing temporary copies by reference...)
Type inquiry would be rather easy, since the procedure would only need to know the size of the local variable which can be obtained like the SizeOf above.
Many ideas... many problems... much to do...
This message is distributed under the terms of the GNU General Public License. The license can be retrieved from many ftp and www sites, e.g. from: ftp://sunsite.unc.edu/pub/gnu/COPYING http://www.mi.uni-erlangen.de/~heckenb/COPYING If you can't get this file, mail me, and I'll send you a copy.