Thread overview
Compile time variadic functions
Mar 13, 2006
Oskar Linde
Mar 14, 2006
Don Clugston
Mar 14, 2006
Don Clugston
Mar 15, 2006
Oskar Linde
Mar 15, 2006
Walter Bright
Mar 15, 2006
Kevin Bealer
March 13, 2006
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...)



March 14, 2006
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...
March 14, 2006
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();
> }
March 15, 2006
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!


March 15, 2006
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


March 15, 2006
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