www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Discovery: D already supports template value parameters for arbitrary

reply Don Clugston <dac nospam.com.au> writes:
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!
Nov 08 2005
next sibling parent Ivan Senji <ivan.senji_REMOVE_ _THIS__gmail.com> writes:
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.
Nov 10 2005
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
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
Nov 10 2005
parent Sean Kelly <sean f4.ca> writes:
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
Nov 10 2005
prev sibling parent Don Clugston <dac nospam.com.au> writes:
 ========================================
 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.
Nov 14 2005