View mode: basic / threaded / horizontal-split · Log in · Help
January 29, 2007
Conditional Templates
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.

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.

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Down with mediocrity!"
29/01/2007 5:17:20 PM
January 29, 2007
Re: Conditional Templates
On Mon, 29 Jan 2007 17:28:51 +1100, Derek Parnell
<derek@nomail.afraid.org> 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.
>
>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.

With a mixin (if you want your method in more than one class):

template TFoo(T)
{
	static if (is(T == float) || is(T == real))
	{
		void Foo(T x)
		{
			writefln("In float foo: ", x);
		}
	}
	else static if (is(T == int) || is(T == short))
	{
		void Foo(T x)
		{
			writefln("In integer foo: ", x);
		}			
	}
	else static assert("Bad type");

}

class Bar(T)
{
	mixin TFoo!(T);
}

void main()
{
	auto bar = new Bar!(int);
	bar.Foo(1);

	auto bar2 = new  Bar!(real);
	bar2.Foo(2.345364);
}

Or directly in the class body:

class Bar(T)
{
	static if (is(T == float) || is(T == real))
	{
		void Foo(T x)
		{
			writefln("In float foo: ", x);
		}
	}
	else static if (is(T == int) || is(T == short))
	{
		void Foo(T x)
		{
			writefln("In integer foo: ", x);
		}			
	}
	else static assert("Bad type");
}

void main()
{
	auto bar = new Bar!(int);
	bar.Foo(1);

	auto bar2 = new  Bar!(real);
	bar2.Foo(2.345364);
}

You can also turn 'is' conditions into templates (I'm not sure if they
are in the std library already)

template IsFloat(T)
{
	const bool IsFloat = is(T == float) || is(T == double) || ...;
}

and use it like this: static if (IsFloat!(T)) ...

You could also try to put 'static if' in the method's body, which is
cleaner:

...
void Foo(T x)
{
	static if ()
	{
	}
	else static if () ...	
}
January 29, 2007
Re: Conditional Templates
> 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.

It seems as if you can add non-static template member functions but not  
variables:
http://d.puremagic.com/issues/show_bug.cgi?id=566

Christian
January 29, 2007
Re: Conditional Templates
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.
January 29, 2007
Re: Conditional Templates
Max Samukha schrieb am 2007-01-29:
> On Mon, 29 Jan 2007 17:28:51 +1100, Derek Parnell
><derek@nomail.afraid.org> 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.
>>
>>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.
>
> With a mixin (if you want your method in more than one class):
>
> template TFoo(T)
> {
> 	static if (is(T == float) || is(T == real))
> 	{
> 		void Foo(T x)
> 		{
> 			writefln("In float foo: ", x);
> 		}
> 	}
> 	else static if (is(T == int) || is(T == short))
> 	{
> 		void Foo(T x)
> 		{
> 			writefln("In integer foo: ", x);
> 		}			
> 	}
> 	else static assert("Bad type");
>
> }

I'd rather sugest

	static if(is(T : long) || is(T : ulong))
	// ...
	else static if(is(T : real))
	// ..
	else 

Thomas
January 29, 2007
Re: Conditional Templates
On Mon, 29 Jan 2007 09:18:54 +0000 (UTC), Thomas Kuehne
<thomas-dloop@kuehne.cn> wrote:

>
>I'd rather sugest
>
>	static if(is(T : long) || is(T : ulong))
>	// ...
>	else static if(is(T : real))
>	// ..
>	else 
>
>Thomas
>
>
>-----BEGIN PGP SIGNATURE-----
>
>iD8DBQFFvciULK5blCcjpWoRAn5nAKCpvyONxqqBpPdamnb/+NzTc+3aTQCePURd
>jKCGiYXxaD3LH7nbUEJ0ysw=
>=OnR+
>-----END PGP SIGNATURE-----

Yes, that's better. Thanks
January 29, 2007
Re: Conditional Templates
"Derek Parnell" <derek@nomail.afraid.org> wrote in message 
news:1p0qyzhxblvjj$.xbog5ahy31gi$.dlg@40tude.net...
> 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.

I'm probably missing something important, but can't this be done with normal 
function overloading?
January 29, 2007
Re: Conditional Templates
Jarrett Billingsley wrote:
> "Derek Parnell" <derek@nomail.afraid.org> wrote in message 
> news:1p0qyzhxblvjj$.xbog5ahy31gi$.dlg@40tude.net...
>> 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.
> 
> I'm probably missing something important, but can't this be done with normal 
> function overloading? 

All integer types in D can implicitly convert to floating-point types. 
And he wants integer and float types to behave differently. In order to 
solve this use overloading you'd have to introduce a separate overload 
for every integer type to avoid ambiguous overloads. That's a lot of 
overloads :p...
And it wouldn't even work for the 'cent' and 'ucent' types when they get 
added (they're reserved keywords for 128-bit integer types).
January 29, 2007
Re: Conditional Templates
"Frits van Bommel" <fvbommel@REMwOVExCAPSs.nl> wrote in message 
news:epkvl5$2vc1$1@digitaldaemon.com...
>
> All integer types in D can implicitly convert to floating-point types. And 
> he wants integer and float types to behave differently. In order to solve 
> this use overloading you'd have to introduce a separate overload for every 
> integer type to avoid ambiguous overloads. That's a lot of overloads :p...

Ahh.
January 30, 2007
Re: Conditional Templates
On Mon, 29 Jan 2007 18:48:44 -0500, Jarrett Billingsley wrote:

> "Frits van Bommel" <fvbommel@REMwOVExCAPSs.nl> wrote in message 
> news:epkvl5$2vc1$1@digitaldaemon.com...
>>
>> All integer types in D can implicitly convert to floating-point types. And 
>> he wants integer and float types to behave differently. In order to solve 
>> this use overloading you'd have to introduce a separate overload for every 
>> integer type to avoid ambiguous overloads. That's a lot of overloads :p...
> 
> Ahh.

Exactly. I started out by using function overloading but all the cut&paste
code got a bit obvious. 

Thanks for all who stopped by to help me. I now have the class working
(sort of).

** Things I have Learned **
(a) Walter, true to his admission, is not a good documenter. The current D
documentation provided by DigitalMars is a productivity sinkhole. Things
are hard, or nigh impossible to find, and if you do find  the thing you are
looking for, the explanation is usually too brief, and may or may not be
accurate.
 (I'll be soon adding to the Wiki-Doc-comments on a few items ).

(b) Templated member functions do not seem to be able to be used as
properties. In the example code given by Frits van Bommel, if you replace
the execution code ...

 b.Foo1(t);
 b.Foo2(t);

with 

 b.Foo1 = t;
 b.Foo2 = t;

you get errors along the lines of ...

Error: (b).Foo2(T) has no value
Error: (b).Foo2 is not an lvalue


(c) Current DMD is unable to help templates distinguish (accurately)
between character and integer types. 

I added some code to detect the 'char' family of types, but this just
caused all char and int types to fail.

  void Foo2(T)(T x) {
      static if (is(T : char)) {
              // do something with the char 'x'
              static assert(0, "No Char allowed");
              writefln("Foo2!(%s : char)(%x)", typeid(T), x);
      } else 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);
      }
    }

Then tested it using 'char' and it failed as expected. I then removed the
'char' types and it still failed ("No Char allowed"). I next removed all
the int types and then it worked. So it seems that D regards characters and
integers as interchangeable entities, which is just not correct. I know
that characters are implemented in memory as integers, but they are
conceptually different things, and just sometimes coders need to treat them
as different things. 


As a double-blind test, I went back to Frits' original code and replaced
 
	static if (is(T : int)) {

with

	static if (is(T : char)) {

and the test ran without failing. This indicates that D can't see any
difference between integer and character literals.

-- 
Derek
(skype: derek.j.parnell)
Melbourne, Australia
"Down with mediocrity!"
30/01/2007 10:53:56 AM
« First   ‹ Prev
1 2
Top | Discussion index | About this forum | D home