Conditional Templates

Frits van Bommel fvbommel at REMwOVExCAPSs.nl
Mon Jan 29 01:13:51 PST 2007


Derek Parnell wrote:
> Help! The D documentation makes me seem such a fool. 
> 
> Here is what I want to do ...
> 
> I want to create a template for a single class member method that behaves
> differently depending on the type of the one parameter supplied to the
> method. In other words, if the method is called with a integer type of
> parameter, D should instantiate one form of the member method, and if the
> parameter is a floating point data type, D should instantiate another form
> of the member method, and finally if the parameter is a specific class, D
> should instantiate yet another form of the member method.
> 
> So in 'pseudo' code ...
> 
> class Bar
> {
>  template Foo(T)
>  {
>     static if (T is an integer type) // byte, ubyte, short, ushort, ...
>     {
>         void Foo(T x)
>         {
>              // do something with the integer 'x'
>         }
>     }
> 
>     static if (T is a floating point type) // float, double, real
>     {
>         void Foo(T x)
>         {
>              // do something with the floating point value.
>         }
>     }
> 
>     static if (T is the class 'Bar')
>     {
>         void Foo(Bar x)
>         {
>              // do something with this object.
>         }
>     }
> 
>     // All other forms are illegal.
>  }
> }
> 
> I've searched the docs and failed to solve how this can be done, if at all.
> I'm sure there are some examples in the newsgroup postings but its like
> looking for something that you don't know what it actually looks like.

Try one of these two methods[1] (Foo1 & Foo2):
-----
import std.stdio;

class Bar {
     private template Foo1Impl(T) {
         static if (is(T : int)) { // byte, ubyte, short, ushort, ...
             void Foo1Impl(T x) {
                 // do something with the integer 'x'
                 writefln("Foo1!(%s : int)(%x)", typeid(T), x);
             }
         } else static if (is(T : float)) { // float, double, real
             void Foo1Impl(T x) {
                 // do something with the floating point value.
                 writefln("Foo1!(%s : float)(%f)", typeid(T), x);
             }
         } else static if (is(T == Bar)) {
             void Foo1Impl(Bar x) {
                 // do something with this object.
                 writefln("Foo1!(%s == Bar)(%s)", typeid(T), x);
             }
         } else {         // All other forms are illegal.
             static assert(0,
                 "Illegal instantiation of Bar with " ~ T.mangleof);
         }
     }

     void Foo1(T)(T x) {
         // FooImpl isn't a function template so we need to explicitly
         // instantiate it
         Foo1Impl!(T)(x);
     }

     void Foo2(T)(T x) {
         static if (is(T : int)) { // byte, ubyte, short, ushort, ...
                 // do something with the integer 'x'
                 writefln("Foo2!(%s : int)(%x)", typeid(T), x);
         } else static if (is(T : float)) { // float, double, real
                 // do something with the floating point value.
                 writefln("Foo2!(%s : float)(%f)", typeid(T), x);
         } else static if (is(T == Bar)) {
                 // do something with this object.
                 writefln("Foo2!(%s == Bar)(%s)", typeid(T), x);
         } else {         // All other forms are illegal.
             static assert(0,
                 "Illegal instantiation of Bar with " ~ T.mangleof);
         }
     }

     char[] toString() { return "[Some Bar]"; };
}

// Some code to test this out:

template Tuple(Ts...) { alias Ts Tuple; }

alias Tuple!(char, wchar, dchar, byte, ubyte, short, ushort, int, uint,
              long, ulong, float, double, real, Bar) Types;

void main() {
     Bar b = new Bar;
     foreach (T; Types) {
         T t;
         static if (is(T == Bar)) {
             t = new T;
         } else {
             t = 1;
         }
         b.Foo1(t);
         b.Foo2(t);
     }

}
-----
Is-expressions are very nice for this sort of thing. The ones above 
match (respectively) anything that implicitly converts to 'int', 
anything that explicitly converts to 'float', and Bar (and only Bar), 
and then repeat the list for Foo2.
To match any subclasses of Bar as well, change the '==' to ':' in that 
is-expression.
Anything else runs into a static assert (though in the first case it's 
redundant; if you leave it out there won't be a match for 
Foo1Impl!(Whatever).Foo1Impl and it'll still be an error).

These do allow char, wchar and dchar as integers though, not sure if 
that was your intent. If not, you'll need to explicitly filter them out.

Note also that order is important: the types that convert to int _also_ 
convert to float, so the int case needs to be before the float case.

> I did note that the docs say you can't use templates to add non-static
> class members, but I tried and it works fine; so I don't know if the docs
> are wrong, I'm wrong or DMD is wrong.

All I know is it works. I'm pretty sure you can't override them though.


[1] No pun intended :p.


More information about the Digitalmars-d-learn mailing list