Thread overview | |||||||
---|---|---|---|---|---|---|---|
|
January 07, 2004 More games with varargs. | ||||
---|---|---|---|---|
| ||||
Awhile ago, I linked to the XL language website regarding its implementation of vararg functions. (that is, functions that accept an arbitrary number of arguments) XL's solution is to resolve the call to a series of recursive calls, using an opaque symbol to represent the arguments. The more I think about it, the more I get the feeling that such a symbol would have lots of really interesting uses. I'll just call it 'other' for the moment. (it doesn't matter exactly what keyword you use. '...' would be C-ish) Basically, 'other' represents some unspecified arguments, each of which is some type or other. (this is purely a compile-time construct) We don't care exactly what. What's important is how to pry arguments out of it: void write() { // do nothing. Or, if we want BASIC-like print statement behaviour // we could print a newline here. } void write(int i, other) { writeInt(i); write(other); } void write(float f, other) { writeFloat(f); write(other); } These functions (except the first) accept an argument, then some other junk that we don't care about just yet. The latter two use the argument that we pried out, and pass the others along. If 'other' is empty, then the first method matches, and the recursion ends. So, now we've implemented typesafe functions that accept any number of arguments, each of an arbitrary type. Score. Additionally, this allows for exceptionally elegant min/max functions. But this would also be handy for just about any time you want to wrap a callable object without concern for the number or types of arguments it accepts. class FunctionPtrWrapper(T) { private T _func; this(T func) { _func = func; } // accept any arguments void opCall(other) { // try to cram them into the function pointer. // (this would be a compile-time error if the signatures // don't match exactly) _func(other); } } alias void delegate(int, float, char[]) DelegateType; alias void function(int, float, char[]) CFunctionPtr; DelegateType theDelegate = new FunctionPtrWrapper!(CFunctionPtr) (&someGlobalFunction).opCall; The biggest downside I can see is that we run into the same limitations as templates: the method source must be available to the compiler, and it would only be good for global or static methods. A bit aggrivating, but still pretty nice. Of course, this all assumes that implementating it in the compiler is realistic. Okay, I've spent long enough writing this that I'm starting to second guess myself. Better send it off before I convince myself to delete it. -- andy |
January 07, 2004 Re: More games with varargs. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andy Friesen | Andy Friesen wrote:
> Awhile ago, I linked to the XL language website regarding its implementation of vararg functions. (that is, functions that accept an arbitrary number of arguments) XL's solution is to resolve the call to a series of recursive calls, using an opaque symbol to represent the arguments.
>
> The more I think about it, the more I get the feeling that such a symbol would have lots of really interesting uses. I'll just call it 'other' for the moment. (it doesn't matter exactly what keyword you use. '...' would be C-ish)
>
> Basically, 'other' represents some unspecified arguments, each of which is some type or other. (this is purely a compile-time construct) We don't care exactly what. What's important is how to pry arguments out of it:
>
> void write() {
> // do nothing. Or, if we want BASIC-like print statement behaviour
> // we could print a newline here.
> }
> void write(int i, other) {
> writeInt(i);
> write(other);
> }
> void write(float f, other) {
> writeFloat(f);
> write(other);
> }
>
> These functions (except the first) accept an argument, then some other junk that we don't care about just yet. The latter two use the argument that we pried out, and pass the others along. If 'other' is empty, then the first method matches, and the recursion ends.
>
> So, now we've implemented typesafe functions that accept any number of arguments, each of an arbitrary type. Score. Additionally, this allows for exceptionally elegant min/max functions.
>
> But this would also be handy for just about any time you want to wrap a callable object without concern for the number or types of arguments it accepts.
>
> class FunctionPtrWrapper(T) {
> private T _func;
> this(T func) {
> _func = func;
> }
>
> // accept any arguments
> void opCall(other) {
> // try to cram them into the function pointer.
> // (this would be a compile-time error if the signatures
> // don't match exactly)
> _func(other);
> }
> }
>
> alias void delegate(int, float, char[]) DelegateType;
> alias void function(int, float, char[]) CFunctionPtr;
>
> DelegateType theDelegate =
> new FunctionPtrWrapper!(CFunctionPtr) (&someGlobalFunction).opCall;
>
> The biggest downside I can see is that we run into the same limitations as templates: the method source must be available to the compiler, and it would only be good for global or static methods. A bit aggrivating, but still pretty nice. Of course, this all assumes that implementating it in the compiler is realistic.
>
> Okay, I've spent long enough writing this that I'm starting to second guess myself. Better send it off before I convince myself to delete it.
>
> -- andy
I like it (particularly since I suggested something similar with templates).
|
January 07, 2004 Re: More games with varargs. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andy Friesen | nice and clean. Walter, you like that? In article <bthm67$2hcs$1@digitaldaemon.com>, Andy Friesen says... > >Awhile ago, I linked to the XL language website regarding its implementation of vararg functions. (that is, functions that accept an arbitrary number of arguments) XL's solution is to resolve the call to a series of recursive calls, using an opaque symbol to represent the arguments. > >The more I think about it, the more I get the feeling that such a symbol would have lots of really interesting uses. I'll just call it 'other' for the moment. (it doesn't matter exactly what keyword you use. '...' would be C-ish) > >Basically, 'other' represents some unspecified arguments, each of which is some type or other. (this is purely a compile-time construct) We don't care exactly what. What's important is how to pry arguments out of it: > >void write() { > // do nothing. Or, if we want BASIC-like print statement behaviour > // we could print a newline here. >} >void write(int i, other) { > writeInt(i); > write(other); >} >void write(float f, other) { > writeFloat(f); > write(other); >} > >These functions (except the first) accept an argument, then some other junk that we don't care about just yet. The latter two use the argument that we pried out, and pass the others along. If 'other' is empty, then the first method matches, and the recursion ends. > >So, now we've implemented typesafe functions that accept any number of arguments, each of an arbitrary type. Score. Additionally, this allows for exceptionally elegant min/max functions. > >But this would also be handy for just about any time you want to wrap a callable object without concern for the number or types of arguments it accepts. > >class FunctionPtrWrapper(T) { > private T _func; > this(T func) { > _func = func; > } > > // accept any arguments > void opCall(other) { > // try to cram them into the function pointer. > // (this would be a compile-time error if the signatures > // don't match exactly) > _func(other); > } >} > >alias void delegate(int, float, char[]) DelegateType; >alias void function(int, float, char[]) CFunctionPtr; > >DelegateType theDelegate = > new FunctionPtrWrapper!(CFunctionPtr) (&someGlobalFunction).opCall; > >The biggest downside I can see is that we run into the same limitations as templates: the method source must be available to the compiler, and it would only be good for global or static methods. A bit aggrivating, but still pretty nice. Of course, this all assumes that implementating it in the compiler is realistic. > >Okay, I've spent long enough writing this that I'm starting to second guess myself. Better send it off before I convince myself to delete it. > > -- andy |
January 08, 2004 Re: More games with varargs. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andy Friesen | Smooth , why would it not be good for member functions ? C "Andy Friesen" <andy@ikagames.com> wrote in message news:bthm67$2hcs$1@digitaldaemon.com... > Awhile ago, I linked to the XL language website regarding its implementation of vararg functions. (that is, functions that accept an arbitrary number of arguments) XL's solution is to resolve the call to a series of recursive calls, using an opaque symbol to represent the arguments. > > The more I think about it, the more I get the feeling that such a symbol would have lots of really interesting uses. I'll just call it 'other' for the moment. (it doesn't matter exactly what keyword you use. '...' would be C-ish) > > Basically, 'other' represents some unspecified arguments, each of which is some type or other. (this is purely a compile-time construct) We don't care exactly what. What's important is how to pry arguments out of it: > > void write() { > // do nothing. Or, if we want BASIC-like print statement behaviour > // we could print a newline here. > } > void write(int i, other) { > writeInt(i); > write(other); > } > void write(float f, other) { > writeFloat(f); > write(other); > } > > These functions (except the first) accept an argument, then some other junk that we don't care about just yet. The latter two use the argument that we pried out, and pass the others along. If 'other' is empty, then the first method matches, and the recursion ends. > > So, now we've implemented typesafe functions that accept any number of arguments, each of an arbitrary type. Score. Additionally, this allows for exceptionally elegant min/max functions. > > But this would also be handy for just about any time you want to wrap a callable object without concern for the number or types of arguments it accepts. > > class FunctionPtrWrapper(T) { > private T _func; > this(T func) { > _func = func; > } > > // accept any arguments > void opCall(other) { > // try to cram them into the function pointer. > // (this would be a compile-time error if the signatures > // don't match exactly) > _func(other); > } > } > > alias void delegate(int, float, char[]) DelegateType; > alias void function(int, float, char[]) CFunctionPtr; > > DelegateType theDelegate = > new FunctionPtrWrapper!(CFunctionPtr) (&someGlobalFunction).opCall; > > The biggest downside I can see is that we run into the same limitations as templates: the method source must be available to the compiler, and it would only be good for global or static methods. A bit aggrivating, but still pretty nice. Of course, this all assumes that implementating it in the compiler is realistic. > > Okay, I've spent long enough writing this that I'm starting to second guess myself. Better send it off before I convince myself to delete it. > > -- andy |
January 08, 2004 Re: More games with varargs. | ||||
---|---|---|---|---|
| ||||
Posted in reply to C | C wrote:
> Smooth , why would it not be good for member functions ?
>
> C
It wouldn't work for member functions because you'd need to recompile these things a whole lot. Doing this with member functions means that the vtable semantics get butchered. (which, if I'm not mistaken, is why you can't declare nonstatic member functions within templates defined inside a class)
Come to think of it, that could lead to quite a lot of generated code, if the function in question isn't inlined, or is nontrivially large.
write(int, string, float, char, wchar[]) would call
write(string, float, char, wchar[]), which would in turn call
write(float, char, wchar[]) and so forth.
If it gets inlined, then it's not a big deal, but each of these functions would be made for exactly one invokation if not. eep.
In my write() example, I had simply relayed the write call to some other normal function, but I don't think it's very reasonable for the compiler to be able to un-inline (outline?) some arbitrary part of a function. (this is what I get for pulling an idea from a language that uses inferred typing to implement generics)
Maybe the solution is to just leave it as it is, and strongly suggest that such functions do as little actual work as possible, relaying to a helper that does the actual work.
A less powerful implementation that works pretty much the same way could be to directly translate the call into a single call for each individual argument. The lack of explicit recursion and pattern matching (matching each argument's type using typical overloading rules) casterates it some, but leaves it plenty useful for things like a typesafe printf(). This would also not require that code be generated specifically for each invokation, which would make it realistic to use them as member functions.
-- andy
|
Copyright © 1999-2021 by the D Language Foundation