www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Template constraint and specializations

reply Ed McCardell <edmccard hotmail.com> writes:
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
Mar 23 2012
next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
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); }
Mar 23 2012
parent Ed McCardell <edmccard hotmail.com> writes:
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
Mar 23 2012
prev sibling next sibling parent "bearophile" <bearophileHUGS lycos.com> writes:
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
Mar 23 2012
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
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 =3D true; } template isFoo(T) { =C2=A0 =C2=A0 enum bool isFoo =3D __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 =3D __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 functi= on: template isA(alias Foo) { template isA(T) { enum bool isA =3D __traits(compiles, { void tester(Args...)(Foo!Args args); tester(T.init);}); } } usage: alias isA!Foo isFoo; template useFoo(T) if (isFoo!T) { .... }
Mar 23 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
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); }
Mar 23 2012
prev sibling next sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
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); }
Mar 23 2012
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
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..
Mar 23 2012
prev sibling parent Philippe Sigaud <philippe.sigaud gmail.com> writes:
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)
Mar 24 2012