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