Thread overview
Template constraint and specializations
Mar 23, 2012
Ed McCardell
Mar 23, 2012
Andrej Mitrovic
Mar 23, 2012
Ed McCardell
Mar 23, 2012
Philippe Sigaud
Mar 23, 2012
Andrej Mitrovic
Mar 23, 2012
Philippe Sigaud
Mar 23, 2012
Andrej Mitrovic
Mar 24, 2012
Philippe Sigaud
Mar 23, 2012
bearophile
March 23, 2012
Is there a way to write a template constraint that matches any specialization of a given type?

For example can the following be done without having to write out every combination of feature1 and feature2:

  class Foo(bool feature1, bool feature2) { ... }

  void useFoo(T)(T foo)
  if (is(T == Foo!(false, false)) || is(T == Foo!(false, true)) ||
      is(T == Foo!(true, false)) || is(T == Foo!(true, true)))
  {
     // call methods of foo that don't change based on feature1/feature2
  }

Thanks,

--Ed
March 23, 2012
On 3/23/12, Ed McCardell <edmccard@hotmail.com> wrote:
> Is there a way to write a template constraint that matches any specialization of a given type?

Nope. But there are simple workarounds:

class Foo(bool feature1, bool feature2) { enum _isFoo = true; }

template isFoo(T) {
    enum bool isFoo = __traits(hasMember, T, "_isFoo");
}

void useFoo(T)(T foo)
   if (isFoo!T)
{
   // call methods of foo that don't change based on feature1/feature2
}

void main()
{
    Foo!(true, false) foo;
    useFoo(foo);
}
March 23, 2012
On 03/23/2012 04:14 AM, Andrej Mitrovic wrote:
> On 3/23/12, Ed McCardell<edmccard@hotmail.com>  wrote:
>> Is there a way to write a template constraint that matches any
>> specialization of a given type?
>
> Nope. But there are simple workarounds:
>
> class Foo(bool feature1, bool feature2) { enum _isFoo = true; }
>
> template isFoo(T) {
>      enum bool isFoo = __traits(hasMember, T, "_isFoo");
> }

Thanks! I was tempted to try something hacky for the constraint, like

  if (T.stringof == "Foo")

but tagging the type with an enum works better all around.

--Ed
March 23, 2012
Andrej Mitrovic:

> Nope. But there are simple workarounds:

Why isn't something similar to this working?

import std.traits: Unqual;

class Foo(bool feature1, bool feature2) {}

template isFoo(T) {
    static if (is(Unqual!T Unused : Foo!Features, Features...)) {
        enum isFoo = true;
    } else {
        enum isFoo = false;
    }
}

void main() {
    auto f1 = new Foo!(true, false)();
    static assert(isFoo!(typeof(f1)));
}


Bye,
bearophile
March 23, 2012
On Fri, Mar 23, 2012 at 10:17, Ed McCardell <edmccard@hotmail.com> wrote:

>>> Is there a way to write a template constraint that matches any
>>>
>>> specialization of a given type?
>>
>>
>> Nope. But there are simple workarounds:
>>
>> class Foo(bool feature1, bool feature2) { enum _isFoo = true; }
>>
>> template isFoo(T) {
>>     enum bool isFoo = __traits(hasMember, T, "_isFoo");
>> }

Another solution that does not require you to add an _isFoo member: use template function instantiation:

template isFoo(T)
{
    enum bool isFoo = __traits(compiles, {

void testFoo(Args...)(Foo!Args arg);

testFoo(T.init);
                                                                });
}

testFoo is a function that accepts any Foo!( ... ) for any ... The
second line tests it on a value of type T (T.init).

This can be generalized even further, to create any template-testing function:

template isA(alias Foo)
{
    template isA(T)
    {
        enum bool isA = __traits(compiles, { void
tester(Args...)(Foo!Args args); tester(T.init);});
    }
}


usage:

alias isA!Foo isFoo;

template useFoo(T) if (isFoo!T)
{
....
}
March 23, 2012
On 3/23/12, Philippe Sigaud <philippe.sigaud@gmail.com> wrote:
> testFoo is a function that accepts any Foo!( ... ) for any ... The
> second line tests it on a value of type T (T.init).

That can't work. For a Foo!int your code will expand like so:

// original code
void tester(Args...)(Foo!Args args);
tester(T.init);

void tester(Args...)(Foo!Args args);
tester((Foo!int).init);  // T is a template instance

void tester(Foo!int)(Foo!(Foo!int) args);  // Args.. becomes the
template instance type
tester((Foo!int).init);

See for yourself:

template isA(alias Foo)
{
    template isA(T)
    {
        //~ pragma(msg, T.init);
        enum bool isA = __traits(compiles,
        {
            void tester(Args...)(Foo!Args args);
            tester(T.init);
        });
    }
}

struct Foo(T)
{
}

alias isA!Foo isFoo;

void useFoo(T)(T t)
    if (isFoo!T)  // no-go
{
}

void main()
{
    Foo!int foo;
    useFoo(foo);
}
March 23, 2012
On Fri, Mar 23, 2012 at 21:27, Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote:

> That can't work. For a Foo!int your code will expand like so:

> See for yourself:

? It works for me:


template isBar(T)
{
    enum isBar = __traits(compiles,
                                    {
                                        void tester(Args...)(Bar!Args arg) {}
                                        tester(T.init);
                                    });
}


template isFoo(T)
{
    enum isFoo = __traits(compiles,
                                    {
                                        void tester(Arg)(Foo!Arg arg) {}
                                        tester(T.init);
                                    });
}


struct Foo(T)
{
}

struct Bar(T...)
{}

void useFoo(T)(T t) if (isFoo!T)
{
}

void useBar(T)(T t) if (isBar!T)
{
}

void main()
{
    Foo!int foo;
    useFoo(foo);
    Bar!(int, string, double) bar;
    useBar(bar);
}
March 23, 2012
On 3/23/12, Philippe Sigaud <philippe.sigaud@gmail.com> wrote:
> It works for me

Yes but check the isA template. It seems there's something causing a nested variadic template to fail. This won't work in a template constraint (it returns false):

template isA(alias Foo)
{
   template isA(T)
   {
       enum bool isA = __traits(compiles,
       {
           void tester(Args...)(Foo!(Args) args);
           tester(T.init);
       });
   }
}
alias isA!Foo isFoo;

But if you change "Args..." to "Args" then it works, although only for single-type templates.. I'm not sure what's going on..
March 24, 2012
On Fri, Mar 23, 2012 at 23:19, Andrej Mitrovic <andrej.mitrovich@gmail.com> wrote:

> Yes but check the isA template. It seems there's something causing a nested variadic template to fail. This won't work in a template constraint (it returns false):

> But if you change "Args..." to "Args" then it works, although only for single-type templates.. I'm not sure what's going on..

Then the generic template should be

enum isA = ( Arg case ) || (Args... case)