www.digitalmars.com         C & C++   DMDScript  

D - typesafe varargs: Another try

reply Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
Here's another shot at typesafe varargs.  This idea uses dynamic array 
syntax, since we don't know the number of arguments, but each element of 
the array has a different value.  Also, you aren't allowed to modify any 
of the elements of the array, or add any elements.  An example 
implementation of sprintf is given at the bottom.

A varargs function would be defined as:

    varargs retType func(type1 arg1, type2 arg2, type3 arg3, ..., type4 
endArg1, type5 endArg2)
    {
        for(int i=0; i<varargs.length; i++)
            DoThingy(arg1,arg2,arg3, varargs[i]);

        OtherVarargsFunc(endArg1, endArg2, varargs);
        OtherVarargsFunc(endArg1, endArg2, varargs[3..40]);

        varargs = varargs[41..80];
        return OtherVarargsFunc(endArg1, endArg2, varargs);
    }

Key things to note:
    1) Keyword 'varargs' makes it really clear this is a varargs 
function.  Of course, we could omit this, since the ... in the arglist 
means the same thing
    2) You can specify fixed arguments before AND after they varargs 
section...why not?
    3) All of the varargs are in the psuedo-array varargs.  You can pass 
any one of these to any non-varargs function.  The type of the element 
is the type that was passed into your varargs function.
    4) You can pass your entire varargs list, or any slice of it, into 
another varargs function.  This covers the need for equivalents of 
vsprintf and such.
    5) While you can't modify the values in the vararg array, you can 
take slices of that if needed.

Other things that could be possible to add:
    * Multiple ranges of varargs?  Why can't varargs be a 
multidimensional psuedo-array?
    * Concatenate varargs pseudo arrays?



SPRINTF EXAMPLE

Now, sprintf would be implemented something like this:

    // these could be implemented in this module, or this could just be 
an interface
    // into a binary library.

    void sprintf_helper_printUpToNextFormatSpecifier(inout char[] ret, 
inout char[] format);
        // assertion error if we hit end of string without another 
format specifier
    void sprintf_helper_printToEnd(ret,format);
        // assertion error if the format specifier has any remaining 
format specifiers

    void sprintf_helper_handleSpecifier(inout char[] ret, inout char[] 
format, char c);
    void sprintf_helper_handleSpecifier(inout char[] ret, inout char[] 
format, unsigned long ul);
    void sprintf_helper_handleSpecifier(inout char[] ret, inout char[] 
format, long l);
    void sprintf_helper_handleSpecifier(inout char[] ret, inout char[] 
format, float f);
    void sprintf_helper_handleSpecifier(inout char[] ret, inout char[] 
format, double d);
    void sprintf_helper_handleSpecifier(inout char[] ret, inout char[] 
format, char[] str);
    void sprintf_helper_handleSpecifier(inout char[] ret, inout char[] 
format, Object *ref);
        // and more....

    varargs char[] sprintf(char[] format, ...);
    {
        char[] ret;

        while(varargs.length > 0)
        {
            sprintf_helper_printUpToNextFormatSpecifier(ret,format);
                // assertion error if we hit end of string without 
another format specifier

            sprintf_helper_handleSpecifier(ret, format, varargs[0]);
            varargs = varargs[1..];
        }
        sprintf_helper_printToEnd(ret,format);
                // assertion failure if the format specifier has any 
remaining format specifiers

        return ret;
    };
Aug 20 2002
parent reply Burton Radons <loth users.sourceforge.net> writes:
Russell Lewis wrote:

 Here's another shot at typesafe varargs.  This idea uses dynamic array 
 syntax, since we don't know the number of arguments, but each element of 
 the array has a different value.  Also, you aren't allowed to modify any 
 of the elements of the array, or add any elements.  An example 
 implementation of sprintf is given at the bottom.
 
 A varargs function would be defined as:
 
    varargs retType func(type1 arg1, type2 arg2, type3 arg3, ..., type4 
 endArg1, type5 endArg2)
    {
        for(int i=0; i<varargs.length; i++)
            DoThingy(arg1,arg2,arg3, varargs[i]);
 
        OtherVarargsFunc(endArg1, endArg2, varargs);
        OtherVarargsFunc(endArg1, endArg2, varargs[3..40]);
 
        varargs = varargs[41..80];
        return OtherVarargsFunc(endArg1, endArg2, varargs);
    }
 
 Key things to note:
    1) Keyword 'varargs' makes it really clear this is a varargs 
 function.  Of course, we could omit this, since the ... in the arglist 
 means the same thing
    2) You can specify fixed arguments before AND after they varargs 
 section...why not?
It doesn't hurt you to put them in the beginning. The compiler would have to reorder them to the beginning anyway, or it would have no way to reference them. I've implemented the varargs list for my port. I use a hybrid of Python-style with types: char [] fmt (char [] format, generic [] args...) where generic is partially: struct generic { TypeInfo type; void *data; } Which leaves the door open for further extensions, such as specific-type arrays or keyword arguments. Leaves the door open, mind, doesn't ensure they'll come in. Expanding a list into arguments is definitely useful in Python, but I don't see any way to clearly do that using current syntax. Perhaps just borrow the triple-dot: fmt (format, args...); Although mandating v-form variants isn't too heavy a cross to bear.
    3) All of the varargs are in the psuedo-array varargs.  You can pass 
 any one of these to any non-varargs function.  The type of the element 
 is the type that was passed into your varargs function.
Egad, that's a lot of code, and even if it worked I would never agree to dynamic dispatch as you illustrate below; it would have to have one target type that it would try to conform to at runtime. Even that would be a big complication. Better to give the user a tool for doing whatever he wants and stay within normal operations.
Aug 20 2002
parent reply Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
Burton Radons wrote:
    3) All of the varargs are in the psuedo-array varargs.  You can 
 pass any one of these to any non-varargs function.  The type of the 
 element is the type that was passed into your varargs function.
Egad, that's a lot of code, and even if it worked I would never agree to dynamic dispatch as you illustrate below; it would have to have one target type that it would try to conform to at runtime.
Sorry, I didn't understand what you're saying here. Can you restate it? Sorry :/ My vision of this was as a purely compile-time thing. The compiler would know the type of each element in the varargs pseudo-array, and would be generating static, typesafe code based on that. Sort of like template instantiation in C++, but with what I hoped was a simpler syntax. I'm open to the 'struct generic' approach, as well. I don't prefer it because it requires the user to implement switch blocks on the typeinfo. But that said, it certainly is workable, and it allows true typesafety, along with a fair amount of runtime flexibility.
Aug 20 2002
parent Burton Radons <loth users.sourceforge.net> writes:
Russell Lewis wrote:

 Burton Radons wrote:
 
    3) All of the varargs are in the psuedo-array varargs.  You can 
 pass any one of these to any non-varargs function.  The type of the 
 element is the type that was passed into your varargs function.
Egad, that's a lot of code, and even if it worked I would never agree to dynamic dispatch as you illustrate below; it would have to have one target type that it would try to conform to at runtime.
Sorry, I didn't understand what you're saying here. Can you restate it? Sorry :/
I thought you were talking about this being run-time, so it would be dynamic dispatch in that case.
 I'm open to the 'struct generic' approach, as well.  I don't prefer it 
 because it requires the user to implement switch blocks on the typeinfo. 
  But that said, it certainly is workable, and it allows true typesafety, 
 along with a fair amount of runtime flexibility.
Okay. Bring this up again in the future if you find the generic list approach deficient in use.
Aug 21 2002