varargs in GPC_(re)

Thu, 13 Mar 1997 18:43:02 GMT



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)...
-- 
Frank Heckenbach, Erlangen, Germany
heckenb@mi.uni-erlangen.de
Turbo Pascal:   http://www.mi.uni-erlangen.de/~heckenb/programs.htm
Internet links: http://www.mi.uni-erlangen.de/~heckenb/links.htm


Frank Heckenbach (heckenb@mi.uni-erlangen.de)

HTML conversion by Lluís de Yzaguirre i Maura
Institut de Lingüística Aplicada - Universitat "Pompeu Fabra"
e-mail: de_yza@upf.es