Thread overview
Re: Function templates for UDT's and native types.
Apr 03, 2005
Derek Parnell
Apr 04, 2005
Dave
Apr 04, 2005
Ben Hinkle
Apr 04, 2005
Dave
Apr 04, 2005
Ben Hinkle
April 03, 2005
On Sun, 3 Apr 2005 12:42:53 -0500, Dave wrote:

> I would like to use a template function with both native and UD types like a struct. The problem in this case seems to be that D doesn't allow 'explicit construction' for native types like C++.
> 
> I've tried specializing, but can't specialize on a class or struct template, so I think I'd have to specialize for all of the native types and leave the non-specialized foo to work with the struct (needless to say, that would largely defeat the purpose of templates).

I've run into this a couple of times now too. It *is* annoying and the workaround is extremely cumbersome. I want coding to be easier than this, and that is surely one of the aims of using templates.

-- 
Derek
Melbourne, Australia
4/04/2005 9:17:29 AM
April 04, 2005
----- Original Message ----- 
From: "Derek Parnell" <derek@psych.ward>
Newsgroups: digitalmars.D.learn
Sent: Sunday, April 03, 2005 6:19 PM
Subject: Re: Function templates for UDT's and native types.


> On Sun, 3 Apr 2005 12:42:53 -0500, Dave wrote:
>
>> I would like to use a template function with both native and UD types
>> like a
>> struct. The problem in this case seems to be that D doesn't allow
>> 'explicit
>> construction' for native types like C++.
>>
>> I've tried specializing, but can't specialize on a class or struct
>> template,
>> so I think I'd have to specialize for all of the native types and leave
>> the
>> non-specialized foo to work with the struct (needless to say, that would
>> largely defeat the purpose of templates).
>
> I've run into this a couple of times now too. It *is* annoying and the workaround is extremely cumbersome. I want coding to be easier than this, and that is surely one of the aims of using templates.
>
> -- 

Yea - this just strikes me as one of those things that would make a lot of template stuff easier.

I'm thinking it ('explicit static ctors' for native types (only)) would get rid of a lot of the clamoring for "overloaded opCast" and "opAssign" functionality as well, because then you could do that type of thing with class/struct templates & opCall(), and still use many of the same templates for native types too.

BTW - Here's a work-around for my particular situation.

;---
import std.stdio;

struct ad(F)
{
    F x = 0, dx = 0;
    static ad opCall(int y) { ad t; t.x = y; t.dx = 0; return t; }
    static ad opCall(F X, F DX) { ad t; t.x = X; t.dx = DX; return t; }
    ad opMul(ad y) { return ad(x*y.x,dx*y.x+x*y.dx); }
}

template EC(F) { F EC(F v) { return v; } }
template EC(F: ad!(float)) { F EC(int v) { return F(v); } }
template EC(F: ad!(double)) { F EC(int v) { return F(v); } }
template EC(F: ad!(real)) { F EC(int v) { return F(v); } }

template foo(F)
{
    F foo(F val, int i)
    {
        //if(i < 1) return F(1);
        if(i < 1) return EC!(F)(1);
        return val * val;
    }
}

void main()
{
    ad!(double) d = ad!(double)(10.0,20.0);
    d = foo!(ad!(double))(d,10);
    writefln(d.x,",",d.dx);

    double x = foo!(double)(10.333333,10);
    writefln(x);
}
;---


April 04, 2005
> I would like to use a template function with both native and UD types like a struct. The problem in this case seems to be that D doesn't allow 'explicit construction' for native types like C++.

I hope Walter can think of a solution to the issues with using primitives
and structs in templates. One way would be to follow Java/C# and box
primitives to subclass Object. That seems like overkill and it would force
the user to abandon value semantics and go with reference semantics. There
are two general area where structs and primitive cause problems with
templates:
1) static opCast/ctors
2) top-level functions vs member functions.
Number (2) comes up all the time with, for example, toString. For a struct
it is easy to write a toString member and call it like x.toString but you
can't use that notation for primitive types and instead you have to rely on
overloading (which in D is cumbersome). Maybe it could be possible some day
to write x.toString for a primitive type and have it mean  the same thing as
toString(x) but with no overload resolution?

These two problems will come up again and again in templates that try to work with both primitive types and structs. A solution could probably wait until after 1.0 unless struct ctors have some conflict with opCall or something.


April 04, 2005
"Dave" <Dave_member@pathlink.com> wrote in message news:d2q590$kjr$1@digitaldaemon.com...
> ----- Original Message ----- 
> From: "Derek Parnell" <derek@psych.ward>
> Newsgroups: digitalmars.D.learn
> Sent: Sunday, April 03, 2005 6:19 PM
> Subject: Re: Function templates for UDT's and native types.
>
>
>> On Sun, 3 Apr 2005 12:42:53 -0500, Dave wrote:
>>
>>> I would like to use a template function with both native and UD types
>>> like a
>>> struct. The problem in this case seems to be that D doesn't allow
>>> 'explicit
>>> construction' for native types like C++.
>>>
>>> I've tried specializing, but can't specialize on a class or struct
>>> template,
>>> so I think I'd have to specialize for all of the native types and leave
>>> the
>>> non-specialized foo to work with the struct (needless to say, that would
>>> largely defeat the purpose of templates).
>>
>> I've run into this a couple of times now too. It *is* annoying and the workaround is extremely cumbersome. I want coding to be easier than this, and that is surely one of the aims of using templates.
>>
>> -- 
>
> Yea - this just strikes me as one of those things that would make a lot of template stuff easier.
>
> I'm thinking it ('explicit static ctors' for native types (only)) would get rid of a lot of the clamoring for "overloaded opCast" and "opAssign" functionality as well, because then you could do that type of thing with class/struct templates & opCall(), and still use many of the same templates for native types too.
>
> BTW - Here's a work-around for my particular situation.
>
> ;---
> import std.stdio;
>
> struct ad(F)
> {
>    F x = 0, dx = 0;
>    static ad opCall(int y) { ad t; t.x = y; t.dx = 0; return t; }
>    static ad opCall(F X, F DX) { ad t; t.x = X; t.dx = DX; return t; }
>    ad opMul(ad y) { return ad(x*y.x,dx*y.x+x*y.dx); }
> }
>
> template EC(F) { F EC(F v) { return v; } }
> template EC(F: ad!(float)) { F EC(int v) { return F(v); } }
> template EC(F: ad!(double)) { F EC(int v) { return F(v); } }
> template EC(F: ad!(real)) { F EC(int v) { return F(v); } }
>
> template foo(F)
> {
>    F foo(F val, int i)
>    {
>        //if(i < 1) return F(1);
>        if(i < 1) return EC!(F)(1);
>        return val * val;
>    }
> }
>
> void main()
> {
>    ad!(double) d = ad!(double)(10.0,20.0);
>    d = foo!(ad!(double))(d,10);
>    writefln(d.x,",",d.dx);
>
>    double x = foo!(double)(10.333333,10);
>    writefln(x);
> }
> ;---
>

Here's an evil hack that only works for non-floating point types since it relies on overloading opAdd and the default values for integer types being 0. Hopefully it will scare Walter enough to investigate a real solution :-)

import std.stdio;
struct ad(F)
{
    F x = 0, dx = 0;
    static ad opCall(int y) { ad t; t.x = y; t.dx = 0; return t; }
    static ad opCall(F X, F DX) { ad t; t.x = X; t.dx = DX; return t; }
    ad opMul(ad y) { return ad(x*y.x,dx*y.x+x*y.dx); }
    ad opAdd(int y) { return ad(x+y,dx); }
}
template foo(F)
{
    F foo(F val, int i)
    {
        if(i < 1) {
          F res; // initializer must be 0 so can't be float pt
          return res+1; // 0+1 or call overloaded opAdd
        }
        return val * val;
    }
}
int main() {
  ad!(int) d = ad!(int)(10,20);
  d = foo!(ad!(int))(d,10);
  writefln(d.x,",",d.dx);

  int x = foo!(int)(10,20);
  writefln(x);
  return 0;
}


April 04, 2005
"Ben Hinkle" <ben.hinkle@gmail.com> wrote in message news:d2q79r$me3$1@digitaldaemon.com...
>
> "Dave" <Dave_member@pathlink.com> wrote in message news:d2q590$kjr$1@digitaldaemon.com...
>> ----- Original Message ----- 
>> From: "Derek Parnell" <derek@psych.ward>
>> Newsgroups: digitalmars.D.learn
>> Sent: Sunday, April 03, 2005 6:19 PM
>> Subject: Re: Function templates for UDT's and native types.
>>
>>
>>> On Sun, 3 Apr 2005 12:42:53 -0500, Dave wrote:
>>>
>>>> I would like to use a template function with both native and UD types
>>>> like a
>>>> struct. The problem in this case seems to be that D doesn't allow
>>>> 'explicit
>>>> construction' for native types like C++.
>>>>
>>>> I've tried specializing, but can't specialize on a class or struct
>>>> template,
>>>> so I think I'd have to specialize for all of the native types and leave
>>>> the
>>>> non-specialized foo to work with the struct (needless to say, that
>>>> would
>>>> largely defeat the purpose of templates).
>>>
>>> I've run into this a couple of times now too. It *is* annoying and the
>>> workaround is extremely cumbersome. I want coding to be easier than
>>> this,
>>> and that is surely one of the aims of using templates.
>>>
>>> -- 
>>
>> Yea - this just strikes me as one of those things that would make a lot of template stuff easier.
>>
>> I'm thinking it ('explicit static ctors' for native types (only)) would get rid of a lot of the clamoring for "overloaded opCast" and "opAssign" functionality as well, because then you could do that type of thing with class/struct templates & opCall(), and still use many of the same templates for native types too.
>>
>> BTW - Here's a work-around for my particular situation.
>>
>> ;---
>> import std.stdio;
>>
>> struct ad(F)
>> {
>>    F x = 0, dx = 0;
>>    static ad opCall(int y) { ad t; t.x = y; t.dx = 0; return t; }
>>    static ad opCall(F X, F DX) { ad t; t.x = X; t.dx = DX; return t; }
>>    ad opMul(ad y) { return ad(x*y.x,dx*y.x+x*y.dx); }
>> }
>>
>> template EC(F) { F EC(F v) { return v; } }
>> template EC(F: ad!(float)) { F EC(int v) { return F(v); } }
>> template EC(F: ad!(double)) { F EC(int v) { return F(v); } }
>> template EC(F: ad!(real)) { F EC(int v) { return F(v); } }
>>
>> template foo(F)
>> {
>>    F foo(F val, int i)
>>    {
>>        //if(i < 1) return F(1);
>>        if(i < 1) return EC!(F)(1);
>>        return val * val;
>>    }
>> }
>>
>> void main()
>> {
>>    ad!(double) d = ad!(double)(10.0,20.0);
>>    d = foo!(ad!(double))(d,10);
>>    writefln(d.x,",",d.dx);
>>
>>    double x = foo!(double)(10.333333,10);
>>    writefln(x);
>> }
>> ;---
>>
>
> Here's an evil hack that only works for non-floating point types since it relies on overloading opAdd and the default values for integer types being 0. Hopefully it will scare Walter enough to investigate a real solution :-)
>
> import std.stdio;
> struct ad(F)
> {
>    F x = 0, dx = 0;
>    static ad opCall(int y) { ad t; t.x = y; t.dx = 0; return t; }
>    static ad opCall(F X, F DX) { ad t; t.x = X; t.dx = DX; return t; }
>    ad opMul(ad y) { return ad(x*y.x,dx*y.x+x*y.dx); }
>    ad opAdd(int y) { return ad(x+y,dx); }
> }
> template foo(F)
> {
>    F foo(F val, int i)
>    {
>        if(i < 1) {
>          F res; // initializer must be 0 so can't be float pt
>          return res+1; // 0+1 or call overloaded opAdd
>        }
>        return val * val;
>    }
> }
> int main() {
>  ad!(int) d = ad!(int)(10,20);
>  d = foo!(ad!(int))(d,10);
>  writefln(d.x,",",d.dx);
>
>  int x = foo!(int)(10,20);
>  writefln(x);
>  return 0;
> }
>

It is floating point, so I tried that with typedef double double0 = 0.0; etc... but in the end, the "EC(F)" specializations ended up being a lot cleaner (IMHO at least). I think it is something that is generally workable, because if another struct template or the same for other types are needed, then one could just add more "EC(F: ...)" specializations. No doubt, this work-around would get pretty tiresome if you had several different UDT's to work with. And of course, to some extent that really dings the whole idea behind 'templates' -- to be able to use a 'template' for any type (basic or UDT) that can have the same operations performed on it.

I've spent several hours trying to port a 100+ lines of convoluted C++ template code (when it should have probably taken an hour or two) and the one really major problem I've noticed is you can't do "double x = double(3.14156);" in D. The rest of the issues in this code at least were ultimately taken care of primarily by a few overloaded functions (which probably should have been done in the C++ code anyway).

- Dave