www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - One question about templates

reply bearophile <bearophileHUGS lycos.com> writes:
How can I tell that S1 and S2 are different instantiations of the same struct
template, while Bar is an instantiation of a different struct template?


struct Foo(T...) {}
struct Bar(T...) {}

template SameStructTemplate(S1, S2) {
    // enum bool SameStructTemplate = ?
}

void main() {
    alias Foo!(int,int,int) S1;
    alias Foo!(float,int,int,int) S2;
    alias Bar!(float,int,int,int) S3;

    static assert(SameStructTemplate!(S1, S2));
    static assert(!SameStructTemplate!(S1, S3));
}


Bye and thank you,
bearophile
Aug 04 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Something simpler is enough for my purposes, to tell that a type is one
instantiation of Foo or not:


struct Foo(T...) {}
struct Bar(T...) {}

template IsFoo(S) {
    // ?
}

void main() {
    alias Foo!(int,int,int) S1;
    alias Foo!(float,int,int,int) S2;
    alias Bar!(float,int,int,int) S3;

    static assert(IsFoo!S1);
    static assert(IsFoo!S2);
    static assert(!IsFoo!S3);
}

Bye,
bearophile
Aug 04 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
I have found one bad looking solution :-)

template IsFoo(alias S) {
    enum bool IsFoo = __traits(compiles, { void isf(T...)(Foo!T){} isf(S.init);
});
}

Bye,
bearophile
Aug 04 2010
parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
On Thu, Aug 5, 2010 at 04:26, bearophile <bearophileHUGS lycos.com> wrote:

 I have found one bad looking solution :-)

 template IsFoo(alias S) {
    enum bool IsFoo = __traits(compiles, { void isf(T...)(Foo!T){}
 isf(S.init); });
 }
I used to be confronted to this pb too. Here is what I did: /** Alias itself to true if $(M T) is an instance of $(M 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. 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: FromXToBar(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 Bar to Foo... It worked for me, tell me if it's OK for you. Philippe
Aug 04 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Philippe Sigaud:

 I used to be confronted to this pb too. Here is what I did:
 ...
You are gentle and helpful, and your post has given me useful ideas, but your post it too much unreadable for me. If you take a look here you can see 16 attaches, and it seems part of your message are missing, where is the code for TemplateName? And what does it happen if two templates coming from two modules share the same name? http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.learn&article_id=20943 Through your post I have found this, that can't be used to solve my problem, but seems interesting and worth putting into Phobos2 if not already present: private string[3] _between(char b, char e, string s)() { int foundb, 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 .. $]]; else foundb--; } } return [s, notFound, notFound]; } template TemplateParameters(T) { mixin("alias TypeTuple!(" ~ _between!('(', ')' , T.stringof)[1] ~ ") TemplateParameters;"); } But in the end I have not understood how you suggest me to solve my problem, sorry :-) Writing code like this is not a good idea because I think it instantiates extraneous templates: assert(is(Foo!(TemplateParameters!(S1)) == S1)); Bye, bearophile
Aug 05 2010
parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
bearophile Wrote:

 Philippe Sigaud:
 
 I used to be confronted to this pb too. Here is what I did:
 ...
You are gentle and helpful, and your post has given me useful ideas, but your post it too much unreadable for me. If you take a look here you can see 16 attaches, and it seems part of your message are missing, where is the code for TemplateName?
Oww, what a mess! That's strange, I didn't do anything different from all other answers. Man, are my posts always like this? I'd be horrified to pollute the NG like this. I did not attach anything or whatever, just replied to you mail. The code I pasted was complete and functional. I don't have it here, I'll answer again tonight, using the digitalmars website.
 And what does it happen if two templates coming from two modules share the
same name?
Good question. The names returned by .stringof or __traits(identifier) are always short, not fully qualified. Just this week, I needed to get a qualified name and could not found a way. I can start a thread about it on the D main NG. Maybe doing a static import would work? Philippe
Aug 05 2010
parent reply Philippe Sigaud <philippe.sigaud gmail.com> writes:
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
Aug 05 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Philippe Sigaud:
 I used to be confronted to this pb too. Here is what I did:
Thank you for all your code :) Bye, bearophile
Aug 05 2010