One question about templates
Philippe Sigaud
philippe.sigaud at gmail.com
Thu Aug 5 10:50:24 PDT 2010
OK, let's try again.
I used to be confronted to this pb too. Here is what I did:
/**
Alias itself to true if T is an instance of templ. To obtain the template parameters,
see TemplateParametersTypeTuple.
Example:
----
auto cy = cycle([0,1,2,3]); // cy is a Cycle!(int[])
alias typeof(cy) Cy;
assert(isInstanceOf!(Cy, Cycle));
----
*/
template isInstanceOf(T, alias templ)
{
static if (T.stringof.length >= __traits(identifier, templ).length
&& T.stringof[0..__traits(identifier, templ).length] == __traits(identifier, templ))
enum bool isInstanceOf = true;
else
enum bool isInstanceOf = false;
}
/**
Alias itself to true iff templ is a template name (standard, function, class or struct template).
*/
template isTemplate(alias templ)
{
static if (is(typeof(templ) == void) && is(typeof(templ.stringof)))
enum bool isTemplate = true;
else
enum bool isTemplate = false;
}
The converse is a bit more complicated: given a type T, of which you know it's a template
instantiation (T == U!someTypes), get the (someTypes) as a typetuple.
/**
CTFE function to search a string s for a begin char b and an end char e.
returns [s[0..index of b], s[index of b+1, index of e], s[index of e+1, .. $]]
Above all else, it's done for a pair of enclosing chars, like ( and )
As such, it does not stop at the first ')' after a '(' but will count the correct numbers of
parenthesis.
So, between!('(',')', "Foo!(A, B!(int, double), C)") will return ["Foo!", "A,B!(int,double),C", ""]
and _not_ ["Foo!", "A,B!(int,double", ", C)"]
This may not work so well with non-paired characters.
*/
string[3] between(char b, char e, string s)()
{
int foundb;
int ib;
string notFound = "";
foreach(i,c; s)
{
if (c==b)
{
if (foundb == 0)
{
foundb = 1;
ib = i+1;
continue;
}
else
{
++foundb;
}
}
if (c==e)
{
if (foundb == 1)
{
return [s[0..ib-1], s[ib..i], s[i+1..$]]; // before b, between b and e, after e.
Standard case.
}
else
{
--foundb;
}
}
}
return [s, notFound,notFound]; // no b found, explored the whole string
}
/**
Takes a type instantiating a template (that is, T == A!(someTypes...) for some A)
and becomes the template's parameters typetuple: TypeTuple!(someTypes) in the previous example.
It won't work for alias parameters, because they're not imported.
Example:
----
assert(is(TemplateParametersTypeTuple!(Cycle!(int[])) == TypeTuple!(int[])));
----
*/
template TemplateParametersTypeTuple(T)
{
mixin("alias TypeTuple!(" ~ between!('(',')',T.stringof)[1] ~ ") TemplateParametersTypeTuple;");
}
As a nice side-effect, you can also extract the template name:
/**
If T is a template instantiation, becomes the template name. For a non-templated type,
it just becomes this type name.
----
struct Foo(T...) {}
alias Foo!(int, double) Foo_id;
assert(TemplateName!(Foo_id) == "Foo");
assert(TemplateName!(int) == "int");
----
*/
template TemplateName(T)
{
enum string TemplateName = between!('!','(',T.stringof)[0];
}
So, given T, you can
- determine if it's a template instantiation or not (TBD)
- extract the template name (and so, test for their equality)
- extract the template parameters, as a TypeTuple
I used this to transfer template parameters from one template to another:
ToBar(someFoo)
-> test if it's a template
-> extract the parameters
-> instantiate a Bar!Parameters.
So, given a Bar!(int,double), it creates a Foo!(int,double). It's a kind of function, from any
templated type to Foo... A sort of projection, if you will.
> And what does it happen if two templates coming from two modules share the same name?
Say I have a Foo in module A, another in module B.
In my main module, when I create a Foo, I have to indicate if that's a A.Foo or a B.Foo.
Similarly, any use of Foo has to be qualified, or else the compiler will complain.
Halas, it then cuts all qualifiying information from the names :(
So:
alias A.Foo!(int, double) S1;
isInstanceOf(S1, Foo; // Foo does not exist -> doesn't compile
isInstanceOf(S1, A.Foo); // compiles, returns true;
isInstanceOf(S1, B.Foo); // compiles also (OK), returns true (NOK !)
I tested with a static import and it did not change anything.
Philippe
More information about the Digitalmars-d-learn
mailing list