www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Compile time variadic functions

reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Hello,

I recently found out a way to use D's new implicit function template 
instantiation support to do compile time variadic functions 
(http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/35151).I think 
it is cool enough to deserve its own post. :) I've added a slightly 
clumsy vararg declaration mixin to declare vararg functions.

Here is a simple variadic function:

template myFuncImpl(T) {
   void myFuncImpl(T) {
     static if (is (T == Empty)) {
       writefln("Done");
     } else {
       writefln("Got argument %s of type %s",args.head,
                typeid(typeof(args.head)));
       .myFuncImpl(T.tail); // tail-recurse
     }
   }
}

struct myFuncImplDummy { mixin decl_vararg_func!(myFuncImpl); }
alias myFuncImplDummy.func myFunc;

And here is how it's called:

void main() {
   myFunc(1.0,2,3L);
   myFunc();
}

Is there any better way to do the decl_vararg_func and dummy struct 
thing? An identifier name template parameter type would be awesome* (I 
would have other uses for that as well).

To be fully useful, one should add a conversion so that static array 
arguments are converted into dynamic arrays. Any other comments?

Find the implementation including a (slightly updated) demo attached.

/Oskar

*) Another thing that would be nice is an auto (type inferred) return 
type for functions:

auto func(int x) { return x+1; }

Sometimes, I find myself having to write separate templates just to 
define a return type the compiler already knows. One example of one such 
template is TupleType in the attached code. (But I guess there might be 
a better way to write those templates...)
Mar 13 2006
next sibling parent Don Clugston <dac nospam.com.au> writes:
Oskar Linde wrote:
 Hello,
 
 I recently found out a way to use D's new implicit function template 
 instantiation support to do compile time variadic functions 
 (http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/35151).I think 
 it is cool enough to deserve its own post. :) I've added a slightly 
 clumsy vararg declaration mixin to declare vararg functions.
 
 Here is a simple variadic function:
 
 template myFuncImpl(T) {
   void myFuncImpl(T) {
     static if (is (T == Empty)) {
       writefln("Done");
     } else {
       writefln("Got argument %s of type %s",args.head,
                typeid(typeof(args.head)));
       .myFuncImpl(T.tail); // tail-recurse
     }
   }
 }
 
 struct myFuncImplDummy { mixin decl_vararg_func!(myFuncImpl); }
 alias myFuncImplDummy.func myFunc;
 
 And here is how it's called:
 
 void main() {
   myFunc(1.0,2,3L);
   myFunc();
 }
 
 Is there any better way to do the decl_vararg_func and dummy struct 
 thing? 
Probably. But this is seriously cool already.
 An identifier name template parameter type would be awesome* (I 
 would have other uses for that as well).
Alias parameters almost do that. I made qualifiednameof!(alias A), prettynameof!(alias A) and symbolnameof!(alias A) templates which convert identifier aliases to text. Is that any use?
 To be fully useful, one should add a conversion so that static array 
 arguments are converted into dynamic arrays. Any other comments?
 
 Find the implementation including a (slightly updated) demo attached.
 
 /Oskar
 
 *) Another thing that would be nice is an auto (type inferred) return 
 type for functions:
 
 auto func(int x) { return x+1; }
 
 Sometimes, I find myself having to write separate templates just to 
 define a return type the compiler already knows. One example of one such 
 template is TupleType in the attached code. (But I guess there might be 
 a better way to write those templates...)
Sounds great, but could an auto function cope with 'if' ? auto func(int x) { if (x>2) return x+1; else return Nasty!(SomeClass)(x); } In general, I think the compiler couldn't determine the type of an auto function without evaluating the templates inside it...
Mar 14 2006
prev sibling next sibling parent reply Don Clugston <dac nospam.com.au> writes:
Oskar Linde wrote:
 Hello,
 
 I recently found out a way to use D's new implicit function template 
 instantiation support to do compile time variadic functions 
 (http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/35151).I think 
 it is cool enough to deserve its own post. :) I've added a slightly 
 clumsy vararg declaration mixin to declare vararg functions.
 
 Here is a simple variadic function:
 
 template myFuncImpl(T) {
   void myFuncImpl(T) {
     static if (is (T == Empty)) {
       writefln("Done");
     } else {
       writefln("Got argument %s of type %s",args.head,
                typeid(typeof(args.head)));
       .myFuncImpl(T.tail); // tail-recurse
     }
   }
 }
 
 struct myFuncImplDummy { mixin decl_vararg_func!(myFuncImpl); }
 alias myFuncImplDummy.func myFunc;
 
 And here is how it's called:
 
 void main() {
   myFunc(1.0,2,3L);
   myFunc();
 }
 
 Is there any better way to do the decl_vararg_func and dummy struct 
 thing? An identifier name template parameter type would be awesome* (I 
 would have other uses for that as well).
// Just change the 'func' to decl_vararg_func to take advantage // of implicit template name promotion. template decl_vararg_func(alias realFunc) { template decl_vararg_func(A = Empty, B = Empty, C = Empty, D = Empty, E = Empty, F = Empty, G = Empty /*, ...*/) { static void decl_vararg_func(A a = A.init, B b = B.init, C c = C.init, D d = D.init, E e = E.init, F f = F.init, G g = G.init /*,...*/) { realFunc(Tuple(a,b,c,d,e,f,g /*,...*/)); } } } // and there's no need for a mixin. alias decl_vararg_func!(funcImpl) myFunc; Which means the best effort so far is:
 import variadic;

 template myFuncImpl(T) {
   void myFuncImpl(T) {
     static if (is (T == Empty)) {
       writefln("Done");
     } else {
       writefln("Got argument %s of type %s",args.head,
                typeid(typeof(args.head)));
       .myFuncImpl(T.tail); // tail-recurse
     }
   }
 }

 alias decl_vararg_func!(myFuncImpl) myFunc;
> void main() {
   myFunc(1.0,2,3L);
   myFunc();
 }
Mar 14 2006
parent Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Don Clugston skrev:
 Oskar Linde wrote:
 struct myFuncImplDummy { mixin decl_vararg_func!(myFuncImpl); }
 alias myFuncImplDummy.func myFunc;

 Is there any better way to do the decl_vararg_func and dummy struct 
 thing? An identifier name template parameter type would be awesome* (I 
 would have other uses for that as well).
// Just change the 'func' to decl_vararg_func to take advantage // of implicit template name promotion. template decl_vararg_func(alias realFunc) { template decl_vararg_func(A = Empty, B = Empty, C = Empty, D = Empty, E = Empty, F = Empty, G = Empty /*, ...*/) { static void decl_vararg_func(A a = A.init, B b = B.init, C c = C.init, D d = D.init, E e = E.init, F f = F.init, G g = G.init /*,...*/) { realFunc(Tuple(a,b,c,d,e,f,g /*,...*/)); } } } // and there's no need for a mixin. alias decl_vararg_func!(funcImpl) myFunc;
Of course. How silly of me :) And it easy to add should support for optional return values too: template decl_vararg_func(alias realFunc) { template decl_vararg_func(A = Empty, B = Empty, C = Empty, D = Empty, E = Empty, F = Empty, G = Empty /*, ...*/) { typeof(realFunc(Tuple(A.init,B.init,C.init,D.init,E.init,F.init,G.init /*,...*/))) decl_vararg_func(A a = A.init, B b = B.init, C c = C.init, D d = D.init, E e = E.init, F f = F.init, G g = G.init /*,...*/) { return realFunc(Tuple(a,b,c,d,e,f,g /*,...*/)); } } }
 
 
 Which means the best effort so far is:
 
 
  > import variadic;
  >
  > template myFuncImpl(T) {
  >   void myFuncImpl(T) {
  >     static if (is (T == Empty)) {
  >       writefln("Done");
  >     } else {
  >       writefln("Got argument %s of type %s",args.head,
  >                typeid(typeof(args.head)));
  >       .myFuncImpl(T.tail); // tail-recurse
  >     }
  >   }
  > }
  >
  > alias decl_vararg_func!(myFuncImpl) myFunc;
  >
  > void main() {
  >   myFunc(1.0,2,3L);
  >   myFunc();
  > }
It doesn't get any better than this (unless we get a short hand notation for function templates). Static array arguments are still a problem though. Static arrays need some special handling: - They are are the only type I know of where !is(T == typeof(T.init)) - They have schizophrenic value/reference semantics. (Behaves as a value type (declaring space) on declaration, but is a reference type otherwise) - There is no way (that I know of) to define template specializations generically for static arrays. (Only for static arrays of a defined number of elements) The first can be worked around: Change the default argument value from T.init to Init!(T): template Init(T) { const T Init = void; } The second is harder: struct Container(T) { T element; } ... Container!(typeof(x)) container; container.element = x; // Works for all types except static arrays. Meaning I have to convert static arrays into dynamic arrays: template Declare(T) { T Declare; } ... // Convert static arrays types into dynamic array types template Dynify(T) { static if (is (typeof(Declare!(T)[0]) E)) // Indexable static if (is (T : E[])) // Implicitly convertible to dynamic array alias E[] Dynify; else alias T Dynify; else alias T Dynify; } With those changes: myFunc("hello",5,2.3); works. (Attaching the code again) /Oskar
Mar 15 2006
prev sibling parent reply Walter Bright <Walter_member pathlink.com> writes:
In article <dv4j1r$eil$1 digitaldaemon.com>, Oskar Linde says...
*) Another thing that would be nice is an auto (type inferred) return 
type for functions:

auto func(int x) { return x+1; }
That would be cool, but it has some severe chicken-and-egg problems. The problem is that the function body must be fully semantically analyzed before the return type can be deduced. Think of what happens when many functions call each other!
Mar 14 2006
parent Kevin Bealer <Kevin_member pathlink.com> writes:
In article <dv7mtk$1ai3$1 digitaldaemon.com>, Walter Bright says...
In article <dv4j1r$eil$1 digitaldaemon.com>, Oskar Linde says...
*) Another thing that would be nice is an auto (type inferred) return 
type for functions:

auto func(int x) { return x+1; }
That would be cool, but it has some severe chicken-and-egg problems. The problem is that the function body must be fully semantically analyzed before the return type can be deduced. Think of what happens when many functions call each other!
I think languages like SML have this feature and the associated problem you mention. I was told in an A.I. course that the result of adding this to the language, is that the SML compiler needs a "theorem prover" to figure out what the return type from something is. I suspect you could solve logic 101 puzzles, by reformulating them as compile time type inference problems for an SML compiler. Something like C++ metaprogramming. (I think this possibility is a pretty good argument against adding such a feature. :)) Kevin
Mar 15 2006