Variable number of parameters

Mon, 10 Mar 1997 15:24:13 GMT



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.
-- 
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