View mode: basic / threaded / horizontal-split · Log in · Help
March 23, 2012
Template constraint and specializations
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
Re: Template constraint and specializations
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
Re: Template constraint and specializations
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
Re: Template constraint and specializations
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
Re: Template constraint and specializations
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
Re: Template constraint and specializations
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
Re: Template constraint and specializations
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
Re: Template constraint and specializations
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
Re: Template constraint and specializations
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)
Top | Discussion index | About this forum | D home