Peter Gerwinski wrote (in several postings):
BTW, there is yet another Pascal standard definition, PXSC, which supports overloading of procedures and functions. In that dialect you can, for instance, implement an exponential function for real, rational, complex numbers, quaternions and matrices, and all of them are called `exp'. This covers many applications of varargs, is implemented in FPK Pascal and is planned for GNU Pascal, too.
I agree that most of the time you can do with overloading instead of varargs. Varargs are really needed only very rarely, I think, e.g. in things like Read[ln] and Write[ln] and variants like ReadStr, WriteStr, ...
BTW (a bit off-topic for the subject): Is there a meachnism like TP's "text file device drivers" in gpc, i.e. a means of (roughly speaking) assigning a Text "file" variable with a input/output routine, so that the main program can use Write[ln] etc. comfortably, and the routine gets the data in a raw form (a buffer and its length) (and similar for input) - so the routine would be relatively easy to implement but flexible to use? If there's not, is it realistic to hope fur such a thing in gpc? Note, I don't mean to duplicate Borland's way of doing it (which I find quite clumsy), but any way at all. It doesn't even have to be a Text variable, but if not, one would need another variant of Read[ln], Write[ln]. Such a meachnism would eleminate many of the (few) needs for varargs. I'm thinking of things like text output on a graphics screen. In TP (and also in the Graph unit of the BGI2GRX package) there's only an "OutText" procedure that takes a string. (This is reasonable since a graphics unit should not have to worry about formatting output.) If one wants to write a number, one has to manually convert it to a string before outputting. A mechanism like above could simplify this.
A way to pass a constant number of arguments of variable types is to use UCSD (or Borland) Pascal's untyped `Var' parameters in combination with Borland Pascal's `absolute' clauses:
Procedure Foo ( Var Bar ); Var BarChar: Char absolute bar; BarInt: Integer absolute bar; BarString: String ( 80 ) absolute bar; ...
I don't like this! (Actually, I myself use type casts instead of absolute variables, but I like them just a little.) The reason is simply that Pascal's type checking is annulled. (The TV example in the other posting was a good example of what can happen...) One can use it if there's nothing better, but, IMHO, this is no real Pascal (because Pascal is strictly typed). Therefore I'd still prefer having a mechanism like the "IFTYPE" I suggested.
However: Please help me to with VAX Pascal vararg syntax! I don't have a VAX, and I don't have the money to buy one, so I need example programs with descriptions what they are intended to do, etc. (you know).
I agree. If there's already a varargs syntax (and if it enables type checking - unlike C's varargs), gpc should try to be compatible.
Don't forget EP's Schema types. ;-) I am prepared to put them into GPC, but it will take some while, sorry.
Indeed, schema types can reduce the need for overloading, I suppose (I never used them - how should I?).
BTW (maybe I'm opening up another can of worms...): Can the "parameters" (is it called that?) of a schema type only be ordinal values like array indices, or could they be types themselves? IOW: Would it be possible to implement a general list (tree, dynamic array, whatever) as a schema. As I think about it, I don't see any principal obstacle to do this, using some of the ideas I had on variable types of procedure parameters. I think this could be a *great* simplification for many programs. (Who hasn't implemented the same concepts, slightly differently, again and again?)
I agree; however Borland has had something like this ("Form" procedure) in Turbo87 3.0 and something similar in Turbo Vision ("FormatStr" procedure),
And I don't like the FormatStr procedure, because
1. of missing type security (see above, concerning absolute variables)
2. it's very clumsy to use, IMHO. (To get a formatted string, one has to declare an array, assign values to each field, and call FormatStr - all of which a C programmer can do with a single call to sprintf.)
and you can call C functions like "printf" from GPC using the declaration
Function Printf ( format: CString; ... ): Integer;
(Yes, with three dots.)
Easier to use, but again no type checking, and besides, the point in Pascal programming isn't to call C functions... :-)
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.
Correct, too. With the only difference that we don't get money for our work on GPC. (Instead we will get our dream compiler.:)
He wasn't talking about money, but of satisfied customers. That's not the same at all - just look at M$!
Correct. It has the "Borland Pascal 7.0 with Object" standard. Only the "private" directive
If you do it, please do it not in the Borland way (private methods visible in the whole unit - or module here), but more reasonable as visible only for the declaring object type. A "protected" directive (visible to the declaring type and all sub-types of it) would be a good idea, too. Maybe one would also need something like C++'s "friend" (not sure if this is really necessary).
and "dynamic methods" (Function Foo; virtual 1234;) are still missing.
Up to now, I've seen only one use for them, namely Borland's way of implementing message/event handlers in TV/OWL. I'm not convinced that this is really a good thing (all it saves seems to be a case statement or a loop through a constant array or list) - I think it could be done better if one allowed "class" variables/constants, i.e. variables/constants that are the same for all objects of a given type. Here, the class constants would be tables or lists of the message IDs together with their handlers, and "dynamic methods" might not be needed at all. (If there are other applications to them, please tell me!) Class variables/constants, OTOH, are a feature found in several OOP languages, and have more uses. (I'd have liked to have them myself sometimes).
However, function and oparator overloading are not new to Pascal. At least the PXSC standard has this feature. (You *need* it for scientific programming!)
I have to support this! Especially for operators.
Unfortunately I have never seen a working PXSC compiler, only a book with the standard specifications ...
That's the problem with standards, they just don't compile any program... ;-)
But it already works, in GPC and in Borland Pascal: Use an untyped formal `Var' parameter, and use `absolute' variables to access it. (See my other mail.)
See above, why I don't like it. IMHO, a real untyped parameter (usually together with a Size value) should be used only if the type doesn't matter at all (such as raw memory copy/compare, file read/write), and not as a substitute where the language provides no way for a "correct" declaration.
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.
This I don't understand.
What I meant here is a way of declaring an "array expression" (similar for records), i.e. a way of constructing an array in an expression, e.g.:
var v:record s:string; a:array[1..3] of integer end;
begin v:=('H.W.',(1,2,3)) end.
This is not strictly related to varargs and might not be too easy to implement, but can be useful sometimes, too. I'm just collecting ideas here.
Parameters like "Var p: array of Integers" *do* work in GPC (note the `Var';
Is there any reason why this doesn't work with value parameters? In TP, it does, AFAIR.
lower bound is set to zero,
BTW: Is this reasonable? Borland does it this way, but IMHO, it would be more Pascal-ish to pass the bounds together with the parameter and...
upper remains unchecked;
...to provide something like Borland's High and Low pseudo-functions (to get the actual bounds within the procedure). High and Low should work on any array (not only open array parameters), and on ordinal types (giving the minimum and maximum value), IOW with "type a=array[r] of t;" it would hold low(a)=low(r).
Extended Pascal's schemas will provide a better way to achive the same functionality.)
Yes; and in this case perhaps also conformant arrays (I don't know them)?
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.
Objects.
Yes, I was thinking of OOP, too, when I wrote it, but unless Pascal is going to be a completely OOP language in the future (which I don't think), a non-OOP way will be useful. As long as only a single parameter is concerned, you can usually achieve the same with overloading, but when more parameters can be of variable type, the number of needed overloads would increase dramatically...
I could imagine something like the following: [...] (This syntax is only tentative, it doesn't have to be called IFTYPE and CASETYPE.)
Perhaps a good idea, but the type would have to be passed as an (implicit or explicit) additional parameter anyway,
Yes, the type has to be passed, but I see no (sensible) way to use the type except to compare it to a known type (my "IFTYPE") or to another unknown type (my variant on "IFTYPE").
and you can do the cast using `absolute' variables - which already work. I am not sure whether it is worth the effort and the loss of compatibility to other compilers to implement `iftype' and `casetype'.
Sure it will be a lot of work, but I think compatibility to other compilers is not the point here. There are already many extensions in gpc (e.g. parts of EP *and* TP, or gpc's own extensions), that are not supported by any other compiler. And, if we find a good mechanism, it might be part of a standard sometime...
[About the compiler catching type errors in variable types] One has to investigate if and how this is possible (considering multiple, possibly nested IFTYPEs/CASETYPEs)...
Much work. Probably too much, sorry ...
As I think of it, probably yes, but it might be possible to specify the allowed types in the declaration, like:
procedure p(v:(Integer,String,Boolean));
Then the compiler can make sure that no other types are passed. This would be similar to an overloaded procedure, but as I said above, useful if more than one parameter is involved. Of course, there could also be parameters with no type restrictions, where the procedure would implement some default behaviour for types it doesn't know, treating it as unstructured data of known length (SizeOf still works).
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;
This I don't understand.
What I meat is that IFTYPE could not only compare the type of one unknown parameter to a know type (e.g. Integer), but also compare the types of two unknown parameters. For procedures like (raw) compare or swap, all the procedure has to know is the size and that the types match.
Revised delcaration: procedure equal(const a;const b:type of a); Why not...
AFAIK, it works only for known types.
Probably, or are there any unknown types in EP at all (in parameters, schema types, whatever)?
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.
It *is* hard. Consider a large project with many Units/Modules which are compiled separately ...
No problem: Unique_Type_ID = Unique_Module_ID * C + Unique_Type_ID_Within_Module where C is larger than the biggest possible/reaonable number of types whithin a module, in the worst case $100000000, but that might be a little too much.
Or: Unique_Type_ID = Module_Offset + Unique_Type_ID_Within_Module where Module_Offset of a module is calculated just before linking as the sum of the number of Type_IDs in all modules before the one considered, given any ordering of the modules.
Only types that are actually used for variable type parameters, or that appear in an interface, need type IDs.
Of course, any use of these IDs would have to be partially (the Unique_Module_ID/Module_Offset part) resolved at link time - I hope this is possible with the usual linkers - or if not, at run time by use of an additional variable per module that contains its Module_ID/_Offset)...