| |
| Posted by Don Clugston | PermalinkReply |
|
Don Clugston
| 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!
|