D - typesafe varargs: Another try
- Russell Lewis (77/77) Aug 20 2002 Here's another shot at typesafe varargs. This idea uses dynamic array
- Burton Radons (26/56) Aug 20 2002 It doesn't hurt you to put them in the beginning. The compiler would
- Russell Lewis (11/20) Aug 20 2002 Sorry, I didn't understand what you're saying here. Can you restate it?...
- Burton Radons (5/21) Aug 21 2002 I thought you were talking about this being run-time, so it would be
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
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
Burton Radons wrote: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.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.
Aug 20 2002
Russell Lewis wrote:Burton Radons wrote:I thought you were talking about this being run-time, so it would be dynamic dispatch in that case.Sorry, I didn't understand what you're saying here. Can you restate it? Sorry :/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.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