Thread overview
Discovery: D already supports template value parameters for arbitrary constants!
Nov 08, 2005
Don Clugston
Nov 10, 2005
Ivan Senji
Nov 10, 2005
Sean Kelly
Nov 10, 2005
Sean Kelly
Nov 14, 2005
Don Clugston
November 08, 2005
Template value parameters in D have the same restriction they have in
C++, ie, they must be of an integral type -- eg, int, long, enum.
It would be nice to support arbitrary compile-time constants.
Ideally, the type of the value could also be parametrised.
The 'holy grail' of template value parameters would be an 'auto'
value parameter, where a single parameter can accept a constant of any type.

I was astonished to find that D can already do this!!!!
The docs for alias template parameters says that they are a generalisation of template template parameters. Actually, they are
so general that they encompass all template value parameters as well!

I rely on all of the following facts:

* a template can include arbitrary const declarations (including reals, strings, arrays, etc). This seems to be completely unrestricted.
* templates can have alias parameters, which can refer to other templates.
* promotion of names for single-value templates
* templates can be nested in other templates.

It would still be nice to have reals allowable as template value parameters, but it no longer seems to be essential. Given the limitations of the OBJ formats, template value parameters for ALL constant types, including arbtitrary arrays of constants, seems to be infeasible. But for those rare cases, there's already a way of doing it.
Here it is. Sorry, it's an ugly, busy example because it shows so many things.

========================================
PROOF OF CONCEPT

The metafunction holygrail() demonstrates an 'auto' template value parameter, quest() shows its use with a string and a creal.
Some meta library functions are used to show it works.

Compile with dmd -c grail.d

Unfortunately, 'is' doesn't seem to work properly with alias template parameters. As a proof-of-concept, I've used 'sizeof' to distinguish the
two cases used here.  Many other bugs/limitations are uncovered:
- some properties work well (like sizeof), others work less well (length, re), others don't work at all (im).

========================================

------------------------------------
   LIBRARY FUNCTIONS
------------------------------------

// Much nicer since DMD 0.138!
template itoa_(long n)
{
  template digit(long n) { const char [] digit = "0123456789"[n..n+1]; }

         static if (n<0)  const char [] s = "-" ~ itoa_!(-n).s;
   else  static if (n<10L) const char [] s = digit!(n);
   else  const char [] s = itoa_!(n/10L).s ~ digit!(n%10L);
}

// overcome the promotion limitation
template itoa(long n)
{
  const char [] itoa = itoa_!(n).s;
}

// quick and dirty, just displays the
// integer part of a complex number c.
template complextoa(alias c)
{
   // workaround: most properties don't work in the current scope
   template parts() { const re = c!().re;
            // bug: 'im' returns the real part when used in a template!
                      const im = c!().im; }
   const char[] s = itoa!( cast(long)(parts!().re) ) ~ " + " ~ itoa!(cast(long)(parts!().im)) ~ "i";
}

-------------------------------------------
the auto template value parameter example
-------------------------------------------

// this accepts a string OR a creal
template holygrail(alias amazing)
{
  // HACK: should use static if ( is(amazing!() : char[]) )
  static if (amazing!().sizeof==8) {
     // HACK: .length doesn't work in the template scope
     template len() { const len = amazing!().length;    }
     pragma( msg,
           "We got a string, it was: " ~ amazing!()
           ~ ". Length is " ~ itoa!(len!()));
  } else
     pragma( msg, "We got a complex number: " ~ complextoa!(amazing).s);
   const bool hasbeenfound=true;
}


template quest(int n)
{
	template constructedstr() {
	   const char[] constructedstr = itoa!(n);
	}
	template sugarless() {
	  const char [] sugarless = "It's a bit lacking in syntactic sugar, but not too terrible";
	}
	template complex() {
	  const creal complex = 78i + 56435;
	}
	
	const bool done = holygrail!(constructedstr).hasbeenfound
	               && holygrail!(sugarless).hasbeenfound
	               && holygrail!(complex).hasbeenfound;	
}

static assert(quest!(434).done);

================================================
   END PROOF OF CONCEPT
================================================
So here's the current state of compile-time programming in D:
* Compile-time functions can be written with natural-looking syntax.
* They can return any constant type as a return value.
* They can accept any constant types as parameters.
* They can construct and manipulate arbitrary lists (using ~, [], and [..], but only if those lists are strings.

* Many workarounds are required (reminiscent of C++ template programming :-) )

BUT... with Walter's improvements in DMD 0.138, we now have ALL the essential elements of a complete compile-time programming language!
November 10, 2005
Don Clugston wrote:
<snip some interesting stuff/>

All i can say is very interesting, keep posting when you do something new and cool with templates please.
November 10, 2005
Don Clugston wrote:
>
> * They can construct and manipulate arbitrary lists (using ~, [], and [..], but only if those lists are strings.

Strings are probably the most important, but it would be nice if other value lists could be manipulated as well.  I wonder if there is any barrier to supporting manipulating integer lists as well?


Sean
November 10, 2005
Sean Kelly wrote:
> Don Clugston wrote:
>>
>> * They can construct and manipulate arbitrary lists (using ~, [], and [..], but only if those lists are strings.
>  Strings are probably the most important, but it would be nice if other value lists could be manipulated as well.  I wonder if there is any barrier to supporting manipulating integer lists as well?

Oops, I just saw Walter's remark about arrays ops as PrimaryExpressions.  So I guess it's feasible but it would take some work.


Sean
November 14, 2005
> ========================================
> PROOF OF CONCEPT
> 
> The metafunction holygrail() demonstrates an 'auto' template value parameter, quest() shows its use with a string and a creal.
> Some meta library functions are used to show it works.
> 
> Compile with dmd -c grail.d
> 
> Unfortunately, 'is' doesn't seem to work properly with alias template parameters.

My mistake. It DOES work. You need to use typeof() around it, eg:

static if ( is(typeof(amazing!()) : char[]) )

because amazing!() ==> amazing!().amazing -- which is a value, not a type. D is correct.
However, it seems that it is not a good idea to use the promotion effect I've used here, where the template is instantiated inside the metafunction. You need to pass an instantiated template in, if you want to pass the result of one metafunction to another.

I'm getting decent usability in my experimental 'meta' library by using the convention that all non-integral values are converted to and from constants using the name .val.

eg
import meta.string;
import meta.math;

template str()
{
  const char [] val = "some lowercase string";
}

template w()
{
  const real val = 54.5478456433887;
}

pragma(msg, "The capwords of: " ~ str!().val );
pragma(msg, " is: " ~ capwords!( str!() ).val);
pragma(msg, "The cube of " ~ toString!( w!() ).val ~
        "is " ~ toString!( pow!( w!(), 3) ).val);

Note that we only use .val when making a new constant, or combining one constant with another.

(Actually capwords!() is not yet implemented, but pow!() and toString!() are.)

If we used automatic promotion, then
toString!( pow!(w, 3) ) --> toString!( pow!(w!().w, 3).pow ).toString

which won't compile, because pow!().pow is a value (it's a real), not a type, and toString only accepts types and ints.