Topic: ARC++ and Variable Argument Functions


Author: jones@jameson.arclch.com ((Ben Jones))
Date: Thu, 3 Mar 94 10:07:33 EST
Raw View
This is the third in a series of articles which will explore ideas
for extending the C++ language.  These extensions are implemented in
an experimental preprocessor called ARC++ which takes the extended C++
syntax and generates ANSI C++.  Those who wish to try out the ideas
being presented here may obtain a copy of ARC++ for the PC, Mac,
Sparcstation, or Iris from the anonymous ftp on: "arcfos1.arclch.com".
Please go to the directory "/pub" and download the file "arc.READ_ME"
for instructions.


                VARIABLE ARGUMENT FUNCTIONS
                ===========================

                        Ben Jones
              (c) 1994 ARSoftware Corporation
                 jones@jameson.arclch.com

It is a curious thing but every high level language ever invented has
a provision for functions with a variable number of arguments (usually
in the I/O library) but the most commonly used ones don't give the
programmer a reasonable way to define their own such functions.

C and C++ come the closest with their ... notation in a function
declaration.  However, they provide no way to find out how many
arguments are actually passed and no ability to check the types of the
arguments.  Two main problems arise:

* If you misspell the format string to a function like "scanf", the
function can run off the end of the stack.  If it finds something that
acts like a reasonable address, it could inadvertantly clobber some
memory and cause mysterious crashes.

* If you are using any C++ conversion operators and expecting an
object to turn into a "double" or something like that, the illusion
breaks if the object is passed to a variable argument function.

So don't use <stdio.h> or anything else with ... in it, right?

Oh, yes, you can use default arguments and overloading in C++ to give
the illusion of a variable number of arguments but then you can't deal
with the arguments as arrays (lists).  You can create functions which
take arrays as arguments.  However, the whole point of any variable
argument function is user-friendliness.  For example:

    p1 = point(x1,y1);
    p2 = point(x2,y2,z2);
       ...
    py1 = polygon(p1,p2,...);
       ...
    box = solid(py1,py2,py3,...);

It is simply a lot easier to read the above program than to read:

    double p1a[] = { x1,y1 };
    p1 = point(2,p1a);
    double p2a[] = { x2,y2,z2 };
    p2 = point(3,p2a);
       ...

ARC++ adds several features to C/C++ to allow an elegant solution to
this problem.


Hidden Argument Count
---------------------

The first step is to get the number of arguments passed to a function.
ARC++ declaration syntax for functions recognizes a parameter declaration
beginning with "?" as a special hidden argument.  A keyword (not a reserved
identifier) following the "?" identifies the type of hidden argument.

In this case, "?count" indicates a hidden argument which receives the
number of actual arguments which follow this hidden argument:

    void myprint(char *format,?count,...);

Within the body of "myprint", "count" is an "int" which contains the
number of arguments actually given.  The macros in <stdarg.h> can
still be used to obtain the actual arguments.

Argument Array
--------------

But ARC++ does not stop there.  If an array is declared after "?count",
it appears to receive the actual arguments, appropriately type-cast:

    void fprint(FILE *file,?count,double arg[])
        {
        for (int i=0;i<count;i++)
            fprintf(file,"%12.0lf  ",arg[i]);
        fprintf(file,"\n");
        }

    fprint(stdout,1,2,3.5,4);

would print:

    1  2  3.5  4


Passing an Explicit Array
-------------------------

There are times when you want to be able to pass an array of arguments
rather than passing them inline.  In the <stdio.h> library the
"vprintf" function is available for this purpose.  But ARC++ makes
the "?count" notation do double duty:

    double a[] = {1  2  3.5  4};
    print(4,a);

That is, if a count and an array are passed to "print", ARC++ accepts
them.  In effect, when "print" is called with a variable number of
arguments, an array is created on the stack for the purpose of passing
those arguments.

This makes it very convenient to pass a variable argument list through
a series of functions:

    void print(?count,double arg[])
        { fprint(stdout,count,arg); }

Reference Arguments
-------------------

In order to have symmetry, ARC++ allows an array of references (which
is not normally allowed in C++) to be specified following "?count":

    void scan(?count,double &arg[])
    {
        for (int i=0;i<count;i++)
            scanf(%12.0lf",&arg[i]);
        }

    double x,y,z;
    scan(x,y,z);

To complete the symmetry, you want to be able to pass an array, not of
references but of values:

    double a[5];
    scan(5,a);
    print(5,a);

Here, ARC++ automatically overloads "scan" with a version expecting an
array of values as will as the version with references.


Variable Types in Variable Argument Lists
-----------------------------------------

The array specified after the "?count" can be of any type.  If you define
a class hierarchy which uses type codes or virtual functions to identify
the types of arguments passed, you can pass a variety of types to a
variable argument function:

    class X
    {
        virtual void print();
    };

    void print(?count,&X[])
    {
        for (int i=0;i<count;i++)
            X[i].print();
    }